parent
75ca681dd2
commit
3cef4a80b1
@ -0,0 +1,10 @@ |
|||||||
|
.*.sw* |
||||||
|
.DS_Store |
||||||
|
*.sqlite3 |
||||||
|
*.sqlite3-wal |
||||||
|
*.sqlite3-shm |
||||||
|
debug |
||||||
|
coverage/ |
||||||
|
.coverage |
||||||
|
builddir |
||||||
|
subprojects |
@ -1,3 +1,63 @@ |
|||||||
# game-dev-starter-pack |
# Game Dev Starter Pack |
||||||
|
|
||||||
A cross platform pre-configured C++ game development setup. It uses Meson to build a bouncing game using SFML, ImGUI, and Box2d in the smallest C++ example possible. |
This repository contains a simple C++ project with the basics you need to get a game in C++ going. It's meant to be an educational starting point for people interested in learning to make games in C++ from scratch. It also would be a decent starting point for building a game with other frameworks like [Raylib](https://www.libsdl.org/), [SDL2](https://www.libsdl.org/), or anything that needs to be compiled. This setup also works on Windows, OSX, and Linux and includes a "meta install" script for Windows that installs a complete C++ development environment. |
||||||
|
|
||||||
|
I recommend you get this project working with SFML and then rework it for the other framework you want to use, but honestly SFML is really good and works well. |
||||||
|
|
||||||
|
## If You Just Want to Make a Game |
||||||
|
|
||||||
|
This project is more geared toward people who want to learn C++ by making a bunch of little games, or people who want to learn how to make games from almost nothing. If you have a burning desire to craft a game, then I recommend _not_ using this project and instead go learn any of these: |
||||||
|
|
||||||
|
1. [Defold](https://defold.com/) |
||||||
|
2. [Game Maker Studio](https://gamemaker.io/en) |
||||||
|
3. [Godot](https://godotengine.org/) |
||||||
|
4. [Unreal Engine](https://www.unrealengine.com/en-US) |
||||||
|
5. [O3DE](https://o3de.org/) |
||||||
|
|
||||||
|
Keep in mind that I've only played with each of these, and have _not_ made a full game in them, so my opinion is highly suspect. You should probably just download each one, think of a tiny little game to make, and try to make it. Pick the framework that gets the most of your game done with the least effort. |
||||||
|
|
||||||
|
### Windows |
||||||
|
|
||||||
|
If you have nothing installed then you'll want to run the `scripts/windows_setup.ps1` script to install everything. This script will run and prompt you for admin passwords as it installs what you need, so be sure to stay near your computer to type passwords in when requested. To run it do this: |
||||||
|
|
||||||
|
1. Start a _normal_ non-Administrator PowerShell window. |
||||||
|
2. `irm https://learncodethehardway.com/cppsetup.ps1 > cppsetup.ps1` |
||||||
|
3. `powershell -executionpolicy bypass .\cppsetup.ps1` |
||||||
|
4. _STAY HERE_. For some dumb reason many installers have a timeout that will cause the installer to fail if you don't enter a password fast enough, so stay close until this is done. |
||||||
|
5. Close this PowerShell window and then you can run Windows Terminal and everything should work. If not please email help@learncodethehardway.com to tell me what happened. |
||||||
|
|
||||||
|
The `cppsetup.ps1` file and the `scripts/windows_setup.ps1` should be exactly the same. I'm having you use the `cppsetup.ps1` file since I assume you don't have `git` yet. Once that's done you should then be able to build the project: |
||||||
|
|
||||||
|
2. Run `.\setup.ps1` |
||||||
|
3. `meson compile -C builddir` |
||||||
|
4. `.\builddir\sfmldemo` -- That should run it and you see a window with ImGUI's demo panel. |
||||||
|
|
||||||
|
**WARNING**: You should look in the `setup.ps1` and `reset_build.p1` files for how a build is actually configured. The most important line is at the bottom `meson setup -Ddefault_library=static builddir` which properly configures the build so that the `sfmldemo.exe` program actually runs. Without the `-Ddefault_library=static` the `sfmldemo.exe` file will not have the `.dll` files it needs and will silently fail. If it fails to start then run `start builddir` and double click on it to get the error messages. Then run `reset_build.ps1` to get a good build. |
||||||
|
|
||||||
|
### OSX |
||||||
|
|
||||||
|
1. Get XCode and [Meson](https://mesonbuild.com/) installed. |
||||||
|
2. Run `./setup.sh` |
||||||
|
3. `meson compile -C builddir` |
||||||
|
4. `.\builddir\sfmldemo` -- That should run it and you see a window with ImGUI's demo panel. |
||||||
|
|
||||||
|
I'll have more extensive instructions in a later blog post, but if you have time try this out and let me know how it went at help@learncodethehardway.com. Please let me know if you tried a different compiler, Windows version, etc. If you're on OSX or Linux it should work the same but Linux people might want to use their package manager instead. |
||||||
|
|
||||||
|
### "Keyboard without any keys" |
||||||
|
|
||||||
|
If you get a weird error message of, "We got a keyboard without any keys" it's because of a security feature in OSX. Go to `Security settings->Input Monitoring` and select your Terminal. Check it, enter your admin password, then restart your Terminal. Now you can...read the keyboard in your own software. |
||||||
|
|
||||||
|
No, this does not enhance security at all. These people have gone full on insane at this point. |
||||||
|
|
||||||
|
## Linux |
||||||
|
|
||||||
|
I actually don't have a Linux computer ready to test, but if you have a brand of Linux you like then try the OSX instructions and email me at [help@learncodethehardway.com](mailto:help@learncodethehardway.com). |
||||||
|
|
||||||
|
## Next Steps |
||||||
|
|
||||||
|
I want the `main.cpp` to hit all the major features of SFML without getting too large, so these are some of the features I need to add: |
||||||
|
|
||||||
|
1. Joystick control. |
||||||
|
2. Drawing a floor and walls that work with the physics. |
||||||
|
3. Possibly using the networking and threading capabilities of SFML, but not really sure for what. |
||||||
|
4. Some kind of hit point calculator, since most everyone needs that. |
||||||
|
@ -0,0 +1,93 @@ |
|||||||
|
[Window][Debug##Default] |
||||||
|
Pos=60,60 |
||||||
|
Size=400,400 |
||||||
|
Collapsed=0 |
||||||
|
|
||||||
|
[Window][Dear ImGui Demo] |
||||||
|
Pos=854,133 |
||||||
|
Size=550,680 |
||||||
|
Collapsed=0 |
||||||
|
|
||||||
|
[Window][Hello, world!] |
||||||
|
Pos=60,60 |
||||||
|
Size=233,67 |
||||||
|
Collapsed=0 |
||||||
|
|
||||||
|
[Window][SFML Simple Game Demo] |
||||||
|
Pos=60,60 |
||||||
|
Size=73,54 |
||||||
|
Collapsed=0 |
||||||
|
|
||||||
|
[Window][Clock] |
||||||
|
Pos=1770,0 |
||||||
|
Size=150,50 |
||||||
|
Collapsed=0 |
||||||
|
|
||||||
|
[Table][0xC9935533,3] |
||||||
|
Column 0 Weight=1.0000 |
||||||
|
Column 1 Weight=1.0000 |
||||||
|
Column 2 Weight=1.0000 |
||||||
|
|
||||||
|
[Table][0x64418101,3] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Width=63 |
||||||
|
Column 1 Width=63 |
||||||
|
Column 2 Width=63 |
||||||
|
|
||||||
|
[Table][0x47600645,3] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Width=63 |
||||||
|
Column 1 Width=63 |
||||||
|
Column 2 Weight=1.0000 |
||||||
|
|
||||||
|
[Table][0xDE6957FF,6] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Width=63 |
||||||
|
Column 1 Width=63 |
||||||
|
Column 2 Width=-1 |
||||||
|
Column 3 Weight=1.0000 |
||||||
|
Column 4 Weight=1.0000 |
||||||
|
Column 5 Weight=-1.0000 |
||||||
|
|
||||||
|
[Table][0x861D378E,3] |
||||||
|
Column 0 Weight=1.0000 |
||||||
|
Column 1 Weight=1.0000 |
||||||
|
Column 2 Weight=1.0000 |
||||||
|
|
||||||
|
[Table][0x1F146634,3] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Width=63 |
||||||
|
Column 1 Width=63 |
||||||
|
Column 2 Width=63 |
||||||
|
|
||||||
|
[Table][0x8DFA6E86,2] |
||||||
|
Column 0 Weight=1.0000 |
||||||
|
Column 1 Weight=1.0000 |
||||||
|
|
||||||
|
[Table][0xFABAAEF7,2] |
||||||
|
Column 0 Weight=1.0000 |
||||||
|
Column 1 Weight=1.0000 |
||||||
|
|
||||||
|
[Table][0xA43C3885,3] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Width=56 |
||||||
|
Column 1 Width=56 |
||||||
|
Column 2 Width=56 |
||||||
|
|
||||||
|
[Table][0x49F8DCEA,3] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Weight=1.0000 |
||||||
|
Column 1 Width=84 |
||||||
|
Column 2 Width=126 |
||||||
|
|
||||||
|
[Table][0x82CBB907,3] |
||||||
|
Column 0 Weight=1.0000 |
||||||
|
Column 1 Weight=1.0000 |
||||||
|
Column 2 Weight=1.0000 |
||||||
|
|
||||||
|
[Table][0x49D11DC0,3] |
||||||
|
RefScale=13 |
||||||
|
Column 0 Width=86 |
||||||
|
Column 1 Width=86 |
||||||
|
Column 2 Width=86 |
||||||
|
|
@ -0,0 +1,193 @@ |
|||||||
|
#include "imgui.h" |
||||||
|
#include "imgui-SFML.h" |
||||||
|
#define _USE_MATH_DEFINES |
||||||
|
#include <math.h> |
||||||
|
#include <fmt/core.h> |
||||||
|
#include <box2d/box2d.h> |
||||||
|
#include <SFML/Graphics/Sprite.hpp> |
||||||
|
#include <SFML/Graphics/Texture.hpp> |
||||||
|
#include <SFML/Graphics/RenderWindow.hpp> |
||||||
|
#include <SFML/System.hpp> |
||||||
|
#include <SFML/Audio.hpp> |
||||||
|
#include <SFML/Window/Event.hpp> |
||||||
|
|
||||||
|
void ImGui_setup(sf::RenderWindow &window) { |
||||||
|
int res = ImGui::SFML::Init(window); |
||||||
|
if(res == 1) { |
||||||
|
fmt::print("ImGui returned result {}\n", res); |
||||||
|
} else { |
||||||
|
fmt::print("ImGui returned an error code={}\n", res); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ImGui_update(sf::RenderWindow &window, sf::Clock &deltaClock, sf::Time &tick) { |
||||||
|
ImGui::SFML::Update(window, deltaClock.restart()); |
||||||
|
// ImGui::ShowDemoWindow();
|
||||||
|
ImGui::Begin("Clock"); |
||||||
|
sf::Vector2u size = window.getSize(); |
||||||
|
ImGui::SetWindowPos(ImVec2(size.x - 150, 0)); |
||||||
|
ImGui::SetWindowSize(ImVec2(150, 50)); |
||||||
|
std::string msg = fmt::format("Time: {}\n", tick.asSeconds()); |
||||||
|
ImGui::Button(msg.c_str()); |
||||||
|
ImGui::End(); |
||||||
|
} |
||||||
|
|
||||||
|
void Window_update(sf::RenderWindow &window, sf::Sprite &player) { |
||||||
|
window.clear(); |
||||||
|
window.draw(player); |
||||||
|
ImGui::SFML::Render(window); |
||||||
|
window.display(); |
||||||
|
} |
||||||
|
|
||||||
|
struct BoxTest { |
||||||
|
b2Body *groundBody; |
||||||
|
b2Body *body; |
||||||
|
}; |
||||||
|
|
||||||
|
struct BoxTest Box2d_setup(b2World &world) { |
||||||
|
b2BodyDef groundBodyDef; |
||||||
|
groundBodyDef.position.Set(0.0f, -10.0f); |
||||||
|
b2Body *groundBody = world.CreateBody(&groundBodyDef); |
||||||
|
|
||||||
|
b2PolygonShape groundBox; |
||||||
|
groundBox.SetAsBox(50.0f, 10.0f); |
||||||
|
groundBody->CreateFixture(&groundBox, 0.0f); |
||||||
|
|
||||||
|
b2BodyDef bodyDef; |
||||||
|
bodyDef.type = b2_dynamicBody; |
||||||
|
bodyDef.position.Set(3.0f, 4.0f); |
||||||
|
b2Body *body = world.CreateBody(&bodyDef); |
||||||
|
|
||||||
|
b2PolygonShape dynamicBox; |
||||||
|
dynamicBox.SetAsBox(1.0f, 1.0f); |
||||||
|
b2FixtureDef fixtureDef; |
||||||
|
fixtureDef.shape = &dynamicBox; |
||||||
|
|
||||||
|
fixtureDef.density = 1.0f; |
||||||
|
fixtureDef.friction = 0.3f; |
||||||
|
|
||||||
|
body->CreateFixture(&fixtureDef); |
||||||
|
|
||||||
|
BoxTest box {groundBody, body}; |
||||||
|
return box; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Handle_events(sf::RenderWindow &window, BoxTest &box, sf::Sound &click) { |
||||||
|
sf::Event event; |
||||||
|
|
||||||
|
// is this a main event loop
|
||||||
|
while (window.pollEvent(event)) { |
||||||
|
ImGui::SFML::ProcessEvent(window, event); |
||||||
|
|
||||||
|
switch(event.type) { |
||||||
|
|
||||||
|
case sf::Event::Closed: |
||||||
|
fmt::print("Exiting...\n"); |
||||||
|
window.close(); |
||||||
|
break; |
||||||
|
case sf::Event::KeyPressed: |
||||||
|
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) { |
||||||
|
b2Vec2 force(-200, 1000); |
||||||
|
box.body->ApplyForceToCenter(force, true); |
||||||
|
box.body->ApplyTorque(100.0f, true); |
||||||
|
click.play(); |
||||||
|
} else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) { |
||||||
|
b2Vec2 force(200, 1000); |
||||||
|
box.body->ApplyForceToCenter(force, true); |
||||||
|
box.body->ApplyTorque(-100.0f, true); |
||||||
|
click.play(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case sf::Event::MouseButtonPressed: |
||||||
|
if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) { |
||||||
|
b2Vec2 force(-200, 1000); |
||||||
|
box.body->ApplyForceToCenter(force, true); |
||||||
|
box.body->ApplyTorque(100.0f, true); |
||||||
|
click.play(); |
||||||
|
} else if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) { |
||||||
|
b2Vec2 force(200, 1000); |
||||||
|
box.body->ApplyForceToCenter(force, true); |
||||||
|
box.body->ApplyTorque(-100.0f, true); |
||||||
|
click.play(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sf::Time Update_entities(sf::RenderWindow &window, b2World &world, sf::Clock &clock, sf::Clock &deltaClock, sf::Time &tick, BoxTest &box, sf::Sprite &player) { |
||||||
|
sf::Vector2u winSize = window.getSize(); |
||||||
|
float timeStep = 1.0f / 60.0f; |
||||||
|
int velocityIterations = 6; |
||||||
|
int positionIterations = 2; |
||||||
|
sf::Time since = clock.getElapsedTime(); |
||||||
|
sf::Time nextTick = since - tick > sf::seconds(1) ? since : tick; |
||||||
|
|
||||||
|
world.Step(timeStep, velocityIterations, positionIterations); |
||||||
|
b2Vec2 position = box.body->GetPosition(); |
||||||
|
float angle = box.body->GetAngle(); |
||||||
|
|
||||||
|
player.setPosition(position.x * 100.0f, winSize.y - position.y * 100.0f); |
||||||
|
player.setRotation(angle * 180.0f / M_PI); |
||||||
|
|
||||||
|
ImGui_update(window, deltaClock, tick); |
||||||
|
Window_update(window, player); |
||||||
|
|
||||||
|
return nextTick; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void Create_player(sf::RenderWindow &window, sf::Sprite &player, sf::Texture &texture) { |
||||||
|
if(!texture.loadFromFile("sprite.png")) { |
||||||
|
fmt::print("Error loading sprite!"); |
||||||
|
} |
||||||
|
|
||||||
|
texture.setSmooth(true); |
||||||
|
|
||||||
|
player.setTexture(texture); |
||||||
|
|
||||||
|
// position the prite
|
||||||
|
sf::Vector2u winSize = window.getSize(); |
||||||
|
player.setPosition(winSize.x / 2, winSize.y / 2); |
||||||
|
player.setOrigin(50.f, 50.f); |
||||||
|
} |
||||||
|
|
||||||
|
int main() { |
||||||
|
fmt::print("Setting up a window for you...\n"); |
||||||
|
|
||||||
|
sf::ContextSettings settings; |
||||||
|
settings.antialiasingLevel = 8; |
||||||
|
|
||||||
|
sf::RenderWindow window(sf::VideoMode(1920, 1080), "Simple Game Demo", sf::Style::Default, settings); |
||||||
|
window.setFramerateLimit(60); |
||||||
|
window.setVerticalSyncEnabled(true); |
||||||
|
ImGui_setup(window); |
||||||
|
|
||||||
|
sf::SoundBuffer buffer; |
||||||
|
if(!buffer.loadFromFile("click.mp3")) { |
||||||
|
fmt::print("Failed to load click.ogg!\n"); |
||||||
|
} |
||||||
|
sf::Sound click; |
||||||
|
click.setBuffer(buffer); |
||||||
|
|
||||||
|
sf::Clock deltaClock; |
||||||
|
sf::Clock clock; |
||||||
|
sf::Time tick = clock.getElapsedTime(); |
||||||
|
|
||||||
|
sf::Sprite player; |
||||||
|
sf::Texture texture; |
||||||
|
Create_player(window, player, texture); |
||||||
|
|
||||||
|
b2Vec2 gravity(0.0f, -10.0f); |
||||||
|
b2World world(gravity); |
||||||
|
BoxTest box = Box2d_setup(world); |
||||||
|
|
||||||
|
while (window.isOpen()) { |
||||||
|
Handle_events(window, box, click); |
||||||
|
// preparing for refactoring this into a class or struct for everything
|
||||||
|
tick = Update_entities(window, world, clock, deltaClock, tick, box, player); |
||||||
|
} |
||||||
|
|
||||||
|
ImGui::SFML::Shutdown(); |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
project('sfmldemo', 'cpp', |
||||||
|
default_options: ['cpp_std=c++20']) |
||||||
|
|
||||||
|
dependencies = [ |
||||||
|
dependency('sfml'), |
||||||
|
dependency('imgui-sfml'), |
||||||
|
dependency('fmt'), |
||||||
|
dependency('box2d'), |
||||||
|
] |
||||||
|
|
||||||
|
executable('sfmldemo', 'main.cpp', |
||||||
|
win_subsystem: 'windows', |
||||||
|
cpp_args: '-DFMT_HEADER_ONLY', |
||||||
|
dependencies: dependencies) |
@ -0,0 +1,18 @@ |
|||||||
|
mv .\subprojects\packagecache . |
||||||
|
rm -recurse -force .\subprojects\,.\builddir\ |
||||||
|
mkdir subprojects |
||||||
|
mv .\packagecache .\subprojects\ |
||||||
|
mkdir builddir |
||||||
|
meson wrap install flac |
||||||
|
meson wrap install freetype2 |
||||||
|
meson wrap install imgui-sfml |
||||||
|
meson wrap install imgui |
||||||
|
meson wrap install libpng |
||||||
|
meson wrap install ogg |
||||||
|
meson wrap install openal-soft |
||||||
|
meson wrap install sfml |
||||||
|
meson wrap install vorbis |
||||||
|
meson wrap install zlib |
||||||
|
meson wrap install fmt |
||||||
|
meson wrap install box2d |
||||||
|
meson setup -Ddefault_library=static builddir |
@ -0,0 +1,21 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
set -ex |
||||||
|
|
||||||
|
mv ./subprojects/packagecache . |
||||||
|
rm -rf ./subprojects ./builddir |
||||||
|
mkdir subprojects |
||||||
|
mv ./packagecache ./subprojects |
||||||
|
mkdir builddir |
||||||
|
meson wrap install flac |
||||||
|
meson wrap install freetype2 |
||||||
|
meson wrap install imgui-sfml |
||||||
|
meson wrap install imgui |
||||||
|
meson wrap install libpng |
||||||
|
meson wrap install ogg |
||||||
|
meson wrap install openal-soft |
||||||
|
meson wrap install sfml |
||||||
|
meson wrap install vorbis |
||||||
|
meson wrap install zlib |
||||||
|
meson wrap install fmt |
||||||
|
meson wrap install box2d |
||||||
|
meson setup builddir |
@ -0,0 +1,15 @@ |
|||||||
|
mkdir builddir |
||||||
|
mkdir subprojects |
||||||
|
meson wrap install flac |
||||||
|
meson wrap install freetype2 |
||||||
|
meson wrap install imgui-sfml |
||||||
|
meson wrap install imgui |
||||||
|
meson wrap install libpng |
||||||
|
meson wrap install ogg |
||||||
|
meson wrap install openal-soft |
||||||
|
meson wrap install sfml |
||||||
|
meson wrap install vorbis |
||||||
|
meson wrap install zlib |
||||||
|
meson wrap install box2d |
||||||
|
meson wrap install fmt |
||||||
|
meson setup -Ddefault_library=static builddir |
@ -0,0 +1,18 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
set -ex |
||||||
|
|
||||||
|
mkdir subprojects |
||||||
|
mkdir builddir |
||||||
|
meson wrap install flac |
||||||
|
meson wrap install freetype2 |
||||||
|
meson wrap install imgui-sfml |
||||||
|
meson wrap install imgui |
||||||
|
meson wrap install libpng |
||||||
|
meson wrap install ogg |
||||||
|
meson wrap install openal-soft |
||||||
|
meson wrap install sfml |
||||||
|
meson wrap install vorbis |
||||||
|
meson wrap install zlib |
||||||
|
meson wrap install fmt |
||||||
|
meson wrap install box2d |
||||||
|
meson setup builddir |
@ -0,0 +1,12 @@ |
|||||||
|
#!/usr/bin/env bash |
||||||
|
set -e |
||||||
|
|
||||||
|
fswatch -o *.cpp | while read num |
||||||
|
do echo ">>>>>>>>>>>>>>>>>>>>>> `date`" |
||||||
|
if meson compile -C builddir |
||||||
|
then |
||||||
|
./builddir/sfmldemo |
||||||
|
else |
||||||
|
echo "^^^^^^^^^^^^^^^^^^^^^ ERROR `date`" |
||||||
|
fi |
||||||
|
done |
@ -0,0 +1,255 @@ |
|||||||
|
function Test-WinUtilPackageManager { |
||||||
|
<# |
||||||
|
|
||||||
|
.SYNOPSIS |
||||||
|
Checks if Winget and/or Choco are installed |
||||||
|
|
||||||
|
.PARAMETER winget |
||||||
|
Check if Winget is installed |
||||||
|
|
||||||
|
.PARAMETER choco |
||||||
|
Check if Chocolatey is installed |
||||||
|
|
||||||
|
#> |
||||||
|
|
||||||
|
Param( |
||||||
|
[System.Management.Automation.SwitchParameter]$winget, |
||||||
|
[System.Management.Automation.SwitchParameter]$choco |
||||||
|
) |
||||||
|
|
||||||
|
$status = "not-installed" |
||||||
|
|
||||||
|
if ($winget) { |
||||||
|
# Check if Winget is available while getting it's Version if it's available |
||||||
|
$wingetExists = $true |
||||||
|
try { |
||||||
|
$wingetVersionFull = winget --version |
||||||
|
} catch [System.Management.Automation.CommandNotFoundException], [System.Management.Automation.ApplicationFailedException] { |
||||||
|
Write-Warning "Winget was not found due to un-availablity reasons" |
||||||
|
$wingetExists = $false |
||||||
|
} catch { |
||||||
|
Write-Warning "Winget was not found due to un-known reasons, The Stack Trace is:`n$($psitem.Exception.StackTrace)" |
||||||
|
$wingetExists = $false |
||||||
|
} |
||||||
|
|
||||||
|
# If Winget is available, Parse it's Version and give proper information to Terminal Output. |
||||||
|
# If it isn't available, the return of this funtion will be "not-installed", indicating that |
||||||
|
# Winget isn't installed/available on The System. |
||||||
|
if ($wingetExists) { |
||||||
|
# Check if Preview Version |
||||||
|
if ($wingetVersionFull.Contains("-preview")) { |
||||||
|
$wingetVersion = $wingetVersionFull.Trim("-preview") |
||||||
|
$wingetPreview = $true |
||||||
|
} else { |
||||||
|
$wingetVersion = $wingetVersionFull |
||||||
|
$wingetPreview = $false |
||||||
|
} |
||||||
|
|
||||||
|
# Check if Winget's Version is too old. |
||||||
|
$wingetCurrentVersion = [System.Version]::Parse($wingetVersion.Trim('v')) |
||||||
|
# Grabs the latest release of Winget from the Github API for version check process. |
||||||
|
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/Winget-cli/releases/latest" -Method Get -ErrorAction Stop |
||||||
|
$wingetLatestVersion = [System.Version]::Parse(($response.tag_name).Trim('v')) #Stores version number of latest release. |
||||||
|
$wingetOutdated = $wingetCurrentVersion -lt $wingetLatestVersion |
||||||
|
Write-Host "===========================================" -ForegroundColor Green |
||||||
|
Write-Host "--- Winget is installed ---" -ForegroundColor Green |
||||||
|
Write-Host "===========================================" -ForegroundColor Green |
||||||
|
Write-Host "Version: $wingetVersionFull" -ForegroundColor White |
||||||
|
|
||||||
|
if (!$wingetPreview) { |
||||||
|
Write-Host " - Winget is a release version." -ForegroundColor Green |
||||||
|
} else { |
||||||
|
Write-Host " - Winget is a preview version. Unexpected problems may occur." -ForegroundColor Yellow |
||||||
|
} |
||||||
|
|
||||||
|
if (!$wingetOutdated) { |
||||||
|
Write-Host " - Winget is Up to Date" -ForegroundColor Green |
||||||
|
$status = "installed" |
||||||
|
} |
||||||
|
else { |
||||||
|
Write-Host " - Winget is Out of Date" -ForegroundColor Red |
||||||
|
$status = "outdated" |
||||||
|
} |
||||||
|
} else { |
||||||
|
Write-Host "===========================================" -ForegroundColor Red |
||||||
|
Write-Host "--- Winget is not installed ---" -ForegroundColor Red |
||||||
|
Write-Host "===========================================" -ForegroundColor Red |
||||||
|
$status = "not-installed" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ($choco) { |
||||||
|
if ((Get-Command -Name choco -ErrorAction Ignore) -and ($chocoVersion = (Get-Item "$env:ChocolateyInstall\choco.exe" -ErrorAction Ignore).VersionInfo.ProductVersion)) { |
||||||
|
Write-Host "===========================================" -ForegroundColor Green |
||||||
|
Write-Host "--- Chocolatey is installed ---" -ForegroundColor Green |
||||||
|
Write-Host "===========================================" -ForegroundColor Green |
||||||
|
Write-Host "Version: v$chocoVersion" -ForegroundColor White |
||||||
|
$status = "installed" |
||||||
|
} else { |
||||||
|
Write-Host "===========================================" -ForegroundColor Red |
||||||
|
Write-Host "--- Chocolatey is not installed ---" -ForegroundColor Red |
||||||
|
Write-Host "===========================================" -ForegroundColor Red |
||||||
|
$status = "not-installed" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $status |
||||||
|
} |
||||||
|
|
||||||
|
function Get-WinUtilWingetPrerequisites { |
||||||
|
<# |
||||||
|
.SYNOPSIS |
||||||
|
Downloads the Winget Prereqs. |
||||||
|
.DESCRIPTION |
||||||
|
Downloads Prereqs for Winget. Version numbers are coded as variables and can be updated as uncommonly as Microsoft updates the prereqs. |
||||||
|
#> |
||||||
|
|
||||||
|
# I don't know of a way to detect the prereqs automatically, so if someone has a better way of defining these, that would be great. |
||||||
|
# Microsoft.VCLibs version rarely changes, but for future compatibility I made it a variable. |
||||||
|
$versionVCLibs = "14.00" |
||||||
|
$fileVCLibs = "https://aka.ms/Microsoft.VCLibs.x64.${versionVCLibs}.Desktop.appx" |
||||||
|
# Write-Host "$fileVCLibs" |
||||||
|
# Microsoft.UI.Xaml version changed recently, so I made the version numbers variables. |
||||||
|
$versionUIXamlMinor = "2.8" |
||||||
|
$versionUIXamlPatch = "2.8.6" |
||||||
|
$fileUIXaml = "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v${versionUIXamlPatch}/Microsoft.UI.Xaml.${versionUIXamlMinor}.x64.appx" |
||||||
|
# Write-Host "$fileUIXaml" |
||||||
|
|
||||||
|
Try{ |
||||||
|
Write-Host "Downloading Microsoft.VCLibs Dependency..." |
||||||
|
Invoke-WebRequest -Uri $fileVCLibs -OutFile $ENV:TEMP\Microsoft.VCLibs.x64.Desktop.appx |
||||||
|
Write-Host "Downloading Microsoft.UI.Xaml Dependency...`n" |
||||||
|
Invoke-WebRequest -Uri $fileUIXaml -OutFile $ENV:TEMP\Microsoft.UI.Xaml.x64.appx |
||||||
|
} |
||||||
|
Catch{ |
||||||
|
throw [WingetFailedInstall]::new('Failed to install prerequsites') |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function Get-WinUtilWingetLatest { |
||||||
|
<# |
||||||
|
.SYNOPSIS |
||||||
|
Uses GitHub API to check for the latest release of Winget. |
||||||
|
.DESCRIPTION |
||||||
|
This function grabs the latest version of Winget and returns the download path to Install-WinUtilWinget for installation. |
||||||
|
#> |
||||||
|
# Invoke-WebRequest is notoriously slow when the byte progress is displayed. The following lines disable the progress bar and reset them at the end of the function |
||||||
|
$PreviousProgressPreference = $ProgressPreference |
||||||
|
$ProgressPreference = "silentlyContinue" |
||||||
|
Try{ |
||||||
|
# Grabs the latest release of Winget from the Github API for the install process. |
||||||
|
$response = Invoke-RestMethod -Uri "https://api.github.com/repos/microsoft/Winget-cli/releases/latest" -Method Get -ErrorAction Stop |
||||||
|
$latestVersion = $response.tag_name #Stores version number of latest release. |
||||||
|
$licenseWingetUrl = $response.assets.browser_download_url | Where-Object {$_ -like "*License1.xml"} #Index value for License file. |
||||||
|
Write-Host "Latest Version:`t$($latestVersion)`n" |
||||||
|
Write-Host "Downloading..." |
||||||
|
$assetUrl = $response.assets.browser_download_url | Where-Object {$_ -like "*Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle"} |
||||||
|
Invoke-WebRequest -Uri $licenseWingetUrl -OutFile $ENV:TEMP\License1.xml |
||||||
|
# The only pain is that the msixbundle for winget-cli is 246MB. In some situations this can take a bit, with slower connections. |
||||||
|
Invoke-WebRequest -Uri $assetUrl -OutFile $ENV:TEMP\Microsoft.DesktopAppInstaller.msixbundle |
||||||
|
} |
||||||
|
Catch{ |
||||||
|
throw [WingetFailedInstall]::new('Failed to get latest Winget release and license') |
||||||
|
} |
||||||
|
$ProgressPreference = $PreviousProgressPreference |
||||||
|
} |
||||||
|
|
||||||
|
function Install-WinUtilWinget { |
||||||
|
<# |
||||||
|
|
||||||
|
.SYNOPSIS |
||||||
|
Installs Winget if it is not already installed. |
||||||
|
|
||||||
|
.DESCRIPTION |
||||||
|
This function will download the latest version of Winget and install it. If Winget is already installed, it will do nothing. |
||||||
|
#> |
||||||
|
$isWingetInstalled = Test-WinUtilPackageManager -winget |
||||||
|
|
||||||
|
Try { |
||||||
|
if ($isWingetInstalled -eq "installed") { |
||||||
|
Write-Host "`nWinget is already installed.`r" -ForegroundColor Green |
||||||
|
return |
||||||
|
} elseif ($isWingetInstalled -eq "outdated") { |
||||||
|
Write-Host "`nWinget is Outdated. Continuing with install.`r" -ForegroundColor Yellow |
||||||
|
} else { |
||||||
|
Write-Host "`nWinget is not Installed. Continuing with install.`r" -ForegroundColor Red |
||||||
|
} |
||||||
|
|
||||||
|
# Gets the computer's information |
||||||
|
if ($null -eq $sync.ComputerInfo){ |
||||||
|
$ComputerInfo = Get-ComputerInfo -ErrorAction Stop |
||||||
|
} else { |
||||||
|
$ComputerInfo = $sync.ComputerInfo |
||||||
|
} |
||||||
|
|
||||||
|
if (($ComputerInfo.WindowsVersion) -lt "1809") { |
||||||
|
# Checks if Windows Version is too old for Winget |
||||||
|
Write-Host "Winget is not supported on this version of Windows (Pre-1809)" -ForegroundColor Red |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
# Install Winget via GitHub method. |
||||||
|
# Used part of my own script with some modification: ruxunderscore/windows-initialization |
||||||
|
Write-Host "Downloading Winget Prerequsites`n" |
||||||
|
Get-WinUtilWingetPrerequisites |
||||||
|
Write-Host "Downloading Winget and License File`r" |
||||||
|
Get-WinUtilWingetLatest |
||||||
|
Write-Host "Installing Winget w/ Prerequsites`r" |
||||||
|
Add-AppxProvisionedPackage -Online -PackagePath $ENV:TEMP\Microsoft.DesktopAppInstaller.msixbundle -DependencyPackagePath $ENV:TEMP\Microsoft.VCLibs.x64.Desktop.appx, $ENV:TEMP\Microsoft.UI.Xaml.x64.appx -LicensePath $ENV:TEMP\License1.xml |
||||||
|
Write-Host "Manually adding Winget Sources, from Winget CDN." |
||||||
|
Add-AppxPackage -Path https://cdn.winget.microsoft.com/cache/source.msix #Seems some installs of Winget don't add the repo source, this should makes sure that it's installed every time. |
||||||
|
Write-Host "Winget Installed" -ForegroundColor Green |
||||||
|
Write-Host "Enabling NuGet and Module..." |
||||||
|
Install-PackageProvider -Name NuGet -Force |
||||||
|
Install-Module -Name Microsoft.WinGet.Client -Force |
||||||
|
# Winget only needs a refresh of the environment variables to be used. |
||||||
|
Write-Output "Refreshing Environment Variables...`n" |
||||||
|
$ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") |
||||||
|
} Catch { |
||||||
|
Write-Host "Failure detected while installing via GitHub method. Continuing with Chocolatey method as fallback." -ForegroundColor Red |
||||||
|
# In case install fails via GitHub method. |
||||||
|
Try { |
||||||
|
# Install Choco if not already present |
||||||
|
Install-WinUtilChoco |
||||||
|
Start-Process -Verb runas -FilePath powershell.exe -ArgumentList "choco install winget-cli" |
||||||
|
Write-Host "Winget Installed" -ForegroundColor Green |
||||||
|
Write-Output "Refreshing Environment Variables...`n" |
||||||
|
$ENV:PATH = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") |
||||||
|
} Catch { |
||||||
|
throw [WingetFailedInstall]::new('Failed to install!') |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
$isAdmin = [System.Security.Principal.WindowsPrincipal]::new( |
||||||
|
[System.Security.Principal.WindowsIdentity]::GetCurrent()). |
||||||
|
IsInRole('Administrators') |
||||||
|
|
||||||
|
if(-not $isAdmin) { |
||||||
|
$params = @{ |
||||||
|
FilePath = 'powershell' # or pwsh if Core |
||||||
|
Verb = 'RunAs' |
||||||
|
ArgumentList = @( |
||||||
|
'-ExecutionPolicy ByPass' |
||||||
|
'-File "{0}"' -f $PSCommandPath |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
Start-Process -Wait @params |
||||||
|
Write-Host "Admin stuff done..." |
||||||
|
} else { |
||||||
|
Write-Host "In Admin stuff..." |
||||||
|
Install-WinUtilWinget |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','chocolatey' |
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','Git.Git' |
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','Microsoft.WindowsTerminal' |
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','Python.Python.3.12' |
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','AntibodySoftware.WizFile' |
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','Kitware.CMake' |
||||||
|
Start-Process -NoNewWindow -Wait winget -ArgumentList 'install','Microsoft.VCRedist.2015+.x64' |
||||||
|
|
||||||
|
Start-Process -Verb RunAs -Wait powershell -argumentlist 'C:\ProgramData\chocolatey\bin\choco.exe','install','geany','geany-plugins','winlibs','conan','meson' |
After Width: | Height: | Size: 2.7 KiB |
Loading…
Reference in new issue