Skip to content

Commit

Permalink
wip: public mod downloads
Browse files Browse the repository at this point in the history
  • Loading branch information
BenMcAvoy committed Dec 13, 2024
1 parent d2a1549 commit 1deb1c0
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CarbonLauncher/CarbonLauncher.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
<ClCompile Include="src\gamemanager.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\pipemanager.cpp" />
<ClCompile Include="src\repomanager.cpp" />
<ClCompile Include="src\state.cpp" />
<ClCompile Include="src\utils.cpp" />
</ItemGroup>
Expand All @@ -155,6 +156,7 @@
<ClInclude Include="include\discordmanager.h" />
<ClInclude Include="include\gamemanager.h" />
<ClInclude Include="include\pipemanager.h" />
<ClInclude Include="include\repomanager.h" />
<ClInclude Include="include\state.h" />
<ClInclude Include="include\utils.h" />
</ItemGroup>
Expand Down
63 changes: 63 additions & 0 deletions CarbonLauncher/include/repomanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include <nlohmann/json.hpp>

#include <vector>
#include <string>

constexpr const char* REPOS_URL = "https://github.com/ScrappySM/CarbonLauncher/raw/refs/heads/main/repos.json";

namespace Carbon {
struct Mod {
// Name of the mode
std::string name;

// A list of all the authors of the mod
std::vector<std::string> authors;

// A short description of the mod
std::string description;

// The link to the GitHub repo of the mod
std::string repo;

// A list of all the dependencies of the mod
std::vector<std::string> dependencies;

// The git repo tag to download
std::string tag;

// A list of all the files to download from the GitHub releases
std::vector<std::string> files;

// The supported game version
std::string supported;

bool installed = false;
};

struct Repo {
// The name of the repository (e.g. ScrappySM, Scrap-Mods)
std::string name;

// The link to the repositories website
std::string link;

// A list of all the mods
std::vector<Mod> mods;
};


class RepoManager {
public:
RepoManager();
~RepoManager();

std::vector<Repo> URLToRepos(const std::string& url);
std::vector<Repo>& GetRepos() { return repos; }

private:
Repo JSONToRepo(nlohmann::json json);
std::vector<Repo> repos = {};
};
}; // namespace Carbon
2 changes: 2 additions & 0 deletions CarbonLauncher/include/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "discordmanager.h"
#include "gamemanager.h"
#include "pipemanager.h"
#include "repomanager.h"

namespace Carbon {
class CarbonState_t {
Expand All @@ -12,6 +13,7 @@ namespace Carbon {
Carbon::DiscordManager discordManager;
Carbon::GameManager gameManager;
Carbon::PipeManager pipeManager;
Carbon::RepoManager repoManager;

const char* processTarget = "DummyGame.exe"; // (NOTE: this is not included in this repo at the moment)
// const char* processTarget = "ScrapMechanic.exe";
Expand Down
8 changes: 7 additions & 1 deletion CarbonLauncher/src/gamemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ using namespace Carbon;

GameManager::GameManager() {
this->gameStatusThread = std::thread([this]() {
std::this_thread::sleep_for(std::chrono::milliseconds(500));

while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));

Expand Down Expand Up @@ -99,7 +101,11 @@ GameManager::GameManager() {
std::string modulesDir = Utils::GetCurrentModuleDir() + "modules";
std::filesystem::create_directory(modulesDir);

for (auto& module : std::filesystem::directory_iterator(modulesDir)) {
// Alternative recursive implementation
for (auto& module : std::filesystem::recursive_directory_iterator(modulesDir)) {
if (!module.is_regular_file() || module.path().extension() != ".dll")
continue; // Skip directories and non-DLL files

bool found = false;
for (auto& foundModule : this->modules) {
if (std::wstring(module.path().filename().wstring()) == foundModule.szModule) {
Expand Down
110 changes: 108 additions & 2 deletions CarbonLauncher/src/guimanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
#include "state.h"

#include <Windows.h>

#include <TlHelp32.h>
#include <shellapi.h>
#include <dwmapi.h>

#include <cpr/cpr.h>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
Expand Down Expand Up @@ -146,7 +148,7 @@ void _GUI() {
}
}
else {
static bool dummyGame = true;
static bool dummyGame = strcmp("DummyGame.exe", C.processTarget) == 0;
if (ImGui::Checkbox("Dummy-game", &dummyGame)) {
if (dummyGame) {
C.processTarget = "DummyGame.exe";
Expand Down Expand Up @@ -174,10 +176,114 @@ void _GUI() {

ImGui::EndTabItem();
}

if (ImGui::BeginTabItem("Public mods")) {
//ImGui::Text("Mods here");

ImGui::Columns(3, "modColumns", false);
for (auto& repo : C.repoManager.GetRepos()) {
// Layout:
// | mod | mod | mod |
// | mod |

for (auto& mod : repo.mods) {
ImGui::BeginChild(mod.name.c_str(), ImVec2(0, 300), true);

ImGui::TextWrapped(mod.name.c_str());
ImGui::Separator();
ImGui::TextWrapped(mod.description.c_str());

ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 8);

if (mod.authors.size() > 1) {
ImGui::TextWrapped("Authors: ");
ImGui::SameLine();

for (auto& author : mod.authors) {
std::string link = "https://github.com/" + author;
std::string text = "@" + author + " ";

if (ImGui::SmallButton(text.c_str())) {
ShellExecute(NULL, L"open", std::wstring(link.begin(), link.end()).c_str(), NULL, NULL, SW_SHOWNORMAL);
}

ImGui::PopStyleColor();

if (author != mod.authors.back()) {
ImGui::SameLine();
}
}
}
else {
ImGui::TextWrapped("Author: ");
ImGui::SameLine();

std::string link = "https://github.com/" + mod.authors[0];
std::string text = "@" + mod.authors[0] + " ";

if (ImGui::SmallButton(text.c_str())) {
ShellExecute(NULL, L"open", std::wstring(link.begin(), link.end()).c_str(), NULL, NULL, SW_SHOWNORMAL);
}
}

// Set imgui cursor y pos to bottom of child window
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().ItemSpacing.y);

if (ImGui::Button(mod.installed ? "Uninstall" : "Install")) {
if (mod.installed) {
// Uninstall the mod
std::filesystem::remove(Utils::GetCurrentModuleDir() + "modules\\" + repo.name + "\\" + mod.name + ".dll");
std::filesystem::remove(Utils::GetCurrentModuleDir() + "modules\\" + repo.name + "\\" + mod.name + ".tag");
mod.installed = false;
}
else {

// Start a detached thread to download the mod using CPR to /modules/repo/name.dll and save the tag to /modules/repo/name.tag
// we don't want to download dependencies here...

std::filesystem::create_directory(Utils::GetCurrentModuleDir() + "modules");
std::filesystem::create_directory(Utils::GetCurrentModuleDir() + "modules\\" + repo.name);

std::thread([&]() {
// Download the mods
// TODO: error handling in http
for (auto& file : mod.files) {
std::string url = mod.repo + "/releases/download/" + mod.tag + "/" + file;
std::string path = Utils::GetCurrentModuleDir() + "modules\\" + repo.name + "\\" + mod.name + ".dll";
cpr::Response response = cpr::Get(cpr::Url{ url });
std::ofstream out(path, std::ios::binary);
out << response.text;
out.close();

std::string tagPath = Utils::GetCurrentModuleDir() + "modules\\" + repo.name + "\\" + mod.name + ".tag";
std::ofstream tagOut(tagPath);

tagOut << mod.tag;
tagOut.close();
}

// Set the mod as installed
mod.installed = true;
}).detach();
}
}

ImGui::EndChild();

ImGui::NextColumn();
}
}

ImGui::Columns(1);

ImGui::EndTabItem();
}

// Begin the second tab
if (ImGui::BeginTabItem("Settings")) {
ImGui::EndTabItem();
}

ImGui::EndTabBar();
}

Expand Down
70 changes: 70 additions & 0 deletions CarbonLauncher/src/repomanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "repomanager.h"

#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <cpr/cpr.h>

using namespace Carbon;

Repo RepoManager::JSONToRepo(nlohmann::json json) {
Repo repo;

repo.name = json["name"];
repo.link = json["link"];

for (const auto& mod : json["mods"]) {
Mod m{
.name = mod["name"],
.description = mod["description"],
.repo = mod["repo"],
.tag = mod["tag"],
.supported = mod["supported"]
};

for (const auto& author : mod["authors"]) {
m.authors.push_back(author);
}

for (const auto& dependency : mod["dependencies"]) {
m.dependencies.push_back(dependency);
}

for (const auto& file : mod["files"]) {
std::string sFile = file;
spdlog::info("File: {}", sFile);
m.files.push_back(sFile);
}

repo.mods.push_back(m);
}

return repo;
}

std::vector<Repo> RepoManager::URLToRepos(const std::string& url) {
cpr::Response response = cpr::Get(cpr::Url{ url });

nlohmann::json json = nlohmann::json::parse(response.text);

std::vector<Repo> repos;
for (const auto& repo : json["repositories"]) {
repos.push_back(JSONToRepo(repo));
}

return repos;
}

RepoManager::RepoManager() {
this->repos = URLToRepos(REPOS_URL);

for (auto& repo : this->repos) {
spdlog::info("Repo: {}", repo.name);
for (auto& mod : repo.mods) {
spdlog::info("Mod: {}", mod.name);
}
}
}

RepoManager::~RepoManager() {
this->repos.clear();
}
6 changes: 4 additions & 2 deletions CarbonLauncher/vcpkg.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"dependencies": [
"cpr",
"discord-game-sdk",
{
"name": "glad",
"features": [
Expand All @@ -15,6 +17,6 @@
]
},
"spdlog",
"discord-game-sdk"
"nlohmann-json"
]
}
}

0 comments on commit 1deb1c0

Please sign in to comment.