Skip to content

Commit

Permalink
fix: stability and maintainability improvements
Browse files Browse the repository at this point in the history
NOTE: window can now close without any errors
NOTE: comments are compliant docstrings
  • Loading branch information
BenMcAvoy committed Dec 13, 2024
1 parent 9629f05 commit 5c71e53
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 35 deletions.
13 changes: 13 additions & 0 deletions CarbonLauncher/include/discordmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,32 @@
#include <memory>

namespace Carbon {
// Manages the Discord RPC
class DiscordManager {
public:
// Initializes the Discord RPC and starts a thread
// listening for game state changes (through PipeManager)
DiscordManager();
~DiscordManager();

// Updates the state of the Discord RPC
// @param state The new state
void UpdateState(const std::string& state);

// Updates the details of the Discord RPC
// @param details The new details
void UpdateDetails(const std::string& details);

// Updates the activity of the Discord RPC
void UpdateActivity() const;

// Gets the current activity
discord::Activity& GetActivity();

// Updates the Discord RPC and listens for game state changes
void Update();

private:
discord::User currentUser = discord::User{};
discord::Activity currentActivity = discord::Activity{};

Expand Down
26 changes: 22 additions & 4 deletions CarbonLauncher/include/gamemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,56 @@
#include <mutex>

namespace Carbon {
// Manages the game process including starting and stopping it
// along with monitoring the game's executable and loaded modules
class GameManager {
public:
// Initializes the game manager and starts a thread
// listening for the game process
GameManager();
~GameManager();

// Checks if the game is running
// @return True if the game is running, false otherwise
bool IsGameRunning();

// Injects a module into the game process
// @param modulePath The path to the module to inject
void InjectModule(const std::string& modulePath);

// Starts the game
void StartGame();

// Stops the game forcefully
void KillGame();

// Checks if a module is loaded in the game process
// @param moduleName The name of the module to check
// @return True if the module is loaded, false otherwise
bool IsModuleLoaded(const std::string& moduleName);
std::vector<std::string> GetLoadedCustomModules();

// TODO: Send a message to the
// supervisor to stop the game
// void StopGame();
// Gets all the loaded custom modules
// @return A vector of all the loaded custom modules (mods injected via CarbonLauncher)
std::vector<std::string> GetLoadedCustomModules();

private:
// Checks every 1s if the game is running
std::thread gameStatusThread;

// Mutex to protect the game status
std::mutex gameStatusMutex;

// Checks every 1s for game injected modules
std::thread moduleHandlerThread;

// Tracks when the game was first seen as running by the GameManager
std::optional<std::chrono::time_point<std::chrono::system_clock>> gameStartedTime;

// A list of all the loaded modules in the game process
std::vector<MODULEENTRY32> modules;
bool gameRunning = false;

// The PID of the game process
DWORD pid = 0;
};
}; // namespace Carbon
6 changes: 6 additions & 0 deletions CarbonLauncher/include/guimanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@
namespace Carbon {
class GUIManager {
public:
// Initializes the GUI manager
GUIManager();
~GUIManager();

// Sets the render callback (called every frame on the main thread)
// @param callback The callback to call
// @note This is where you should put your ImGui code
void RenderCallback(std::function<void()> callback) {
this->renderCallback = callback;
}

// Runs the GUI manager
// @note This function will block until the window is closed
void Run() const;

GLFWwindow* window;
Expand Down
16 changes: 14 additions & 2 deletions CarbonLauncher/include/pipemanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,46 @@
#include <queue>

namespace Carbon {
// The type of packet
enum class PacketType {
LOADED, // Sent when the game is loaded
STATECHANGE, // Sent when the game state changes (e.g. menu -> game)
LOG, // (e.g.game log [will be implemented later])
UNKNOWNTYPE // Unknown packet type
};

// A packet sent over the pipe
class Packet {
public:
PacketType type = PacketType::UNKNOWNTYPE;
std::optional<std::string> data;
};

// Manages the pipe connection to the game
class PipeManager {
public:
// Initializes the pipe manager and starts a thread
// listening for packets from the game
PipeManager();
~PipeManager();

bool pipeInitialized = false;

// Protects the packets vector
std::mutex pipeMutex;

// A vector of all the packets received from the game
std::vector<Packet> packets = {};

// Gets all the packets received from the game
// @return A vector of all the packets received from the game
// @note This function is thread-safe
std::vector<Packet>& GetPackets() {
std::lock_guard<std::mutex> lock(this->pipeMutex);
return this->packets;
}

// Gets all the packets of a specific type, removing them from the vector
// @param packet The type of packet to get
// @return A queue of all the packets of the specified type
std::queue<Packet> GetPacketsByType(PacketType packet);

private:
Expand Down
7 changes: 7 additions & 0 deletions CarbonLauncher/include/repomanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,17 @@ namespace Carbon {

class RepoManager {
public:
// Initializes the RepoManager and downloads the repos.json file
RepoManager();
~RepoManager();

// Converts a JSON object to a Repo object
// @param json The JSON object to convert
// @return The converted Repo object (`json` -> `Repo`)
std::vector<Repo> URLToRepos(const std::string& url);

// Gets all the repos
// @return A vector of all the repos
std::vector<Repo>& GetRepos() { return repos; }

private:
Expand Down
6 changes: 6 additions & 0 deletions CarbonLauncher/include/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "repomanager.h"

namespace Carbon {
// The main state of the Carbon Launcher
// Contains all the managers and the process target
class CarbonState_t {
public:
Carbon::GUIManager guiManager;
Expand All @@ -15,7 +17,11 @@ namespace Carbon {
Carbon::PipeManager pipeManager;
Carbon::RepoManager repoManager;

// The target process to manage (e.g. ScrapMechanic.exe)
// This should never be a process that does not have a Contraption
// located in the same place as ScrapMechanic.exe
const char* processTarget = "ScrapMechanic.exe";

// const char* processTarget = "DummyGame.exe";
};
}; // namespace Carbon
Expand Down
14 changes: 4 additions & 10 deletions CarbonLauncher/include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,11 @@
#include <string>

namespace Carbon::Utils {
/*
* Get the current module path
*
* @return The current module path
*/
// Get the current module path
// @return The current module path
std::string GetCurrentModulePath();

/*
* Get the current module directory
*
* @return The current module directory
*/
// Get the current module directory
// @return The current module directory
std::string GetCurrentModuleDir();
}; // namespace Carbon::Utils
30 changes: 21 additions & 9 deletions CarbonLauncher/src/gamemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ GameManager::GameManager() {
}
});

this->gameStatusThread.detach();

this->moduleHandlerThread = std::thread([this]() {
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
Expand Down Expand Up @@ -125,12 +127,12 @@ GameManager::GameManager() {
}
}
});
}

GameManager::~GameManager() {
this->gameStatusThread.join();
this->moduleHandlerThread.detach();
}

GameManager::~GameManager() { }

bool GameManager::IsGameRunning() {
std::lock_guard<std::mutex> lock(this->gameStatusMutex);
return this->gameRunning;
Expand Down Expand Up @@ -168,15 +170,21 @@ void GameManager::InjectModule(const std::string& modulePath) {
}

void GameManager::StartGame() {
if (C.processTarget == "ScrapMechanic.exe") {
ShellExecute(NULL, L"open", L"steam://rungameid/387990", NULL, NULL, SW_SHOWNORMAL);
return;
}
std::thread([]() {
if (C.processTarget == "ScrapMechanic.exe") {
ShellExecute(NULL, L"open", L"steam://rungameid/387990", NULL, NULL, SW_SHOWNORMAL);
return;
}

std::string exePath = Utils::GetCurrentModuleDir() + C.processTarget;
ShellExecute(NULL, L"open", std::wstring(exePath.begin(), exePath.end()).c_str(), NULL, NULL, SW_SHOWNORMAL);
}).detach();

this->gameStartedTime = std::chrono::system_clock::now();

std::string exePath = Utils::GetCurrentModuleDir() + C.processTarget;
ShellExecute(NULL, L"open", std::wstring(exePath.begin(), exePath.end()).c_str(), NULL, NULL, SW_SHOWNORMAL);
while (!this->IsGameRunning()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

void GameManager::KillGame() {
Expand All @@ -201,6 +209,10 @@ void GameManager::KillGame() {
}
} while (Process32Next(snapshot, &entry));
CloseHandle(snapshot);

while (this->IsGameRunning()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}

bool GameManager::IsModuleLoaded(const std::string& moduleName) {
Expand Down
8 changes: 4 additions & 4 deletions CarbonLauncher/src/guimanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ void _GUI() {
// Go to bottom of child window
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetContentRegionAvail().y - ImGui::GetTextLineHeightWithSpacing() - ImGui::GetStyle().ItemSpacing.y);

int frameWidth = ImGui::GetContentRegionAvail().x;
if (ImGui::Button("Uninstall", ImVec2(frameWidth, 0))) {
int frameWidth = (int)ImGui::GetContentRegionAvail().x;
if (ImGui::Button("Uninstall", ImVec2((float)frameWidth, 0))) {
if (C.gameManager.IsGameRunning()) {
spdlog::error("TODO: Unload the mod from the game (ctx: tried to uninstall mod while game was running)");
return;
Expand Down Expand Up @@ -215,9 +215,9 @@ void _GUI() {
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 32);

// Big centered start/kill game button
int width = (ImGui::GetWindowWidth() * 2 / 3); // 2/3 of the window width
int width = (int)(ImGui::GetWindowWidth() * 2 / 3); // 2/3 of the window width
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - width) / 2);
if (ImGui::Button(C.gameManager.IsGameRunning() ? "Kill Game" : "Start Game", ImVec2(width, 40))) {
if (ImGui::Button(C.gameManager.IsGameRunning() ? "Kill Game" : "Start Game", ImVec2((float)width, 40))) {
if (C.gameManager.IsGameRunning()) {
C.gameManager.KillGame();
}
Expand Down
5 changes: 3 additions & 2 deletions CarbonLauncher/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _
spdlog::set_default_logger(console); // Set the default logger to the console logger
console->set_level(spdlog::level::trace); // Set the log level to info

// Tell windows this program isn't important, so other processes can use more resources
SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);

C.guiManager.RenderCallback(_GUI);
C.guiManager.Run();

std::this_thread::sleep_for(std::chrono::seconds(5));

FreeConsole();

return 0;
Expand Down
7 changes: 3 additions & 4 deletions CarbonLauncher/src/pipemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,12 @@ PipeManager::PipeManager() {
}
}
});
}

PipeManager::~PipeManager() {
this->pipeReader.join();
this->packets = {};
this->pipeReader.detach();
}

PipeManager::~PipeManager() { }

std::queue<Packet> PipeManager::GetPacketsByType(PacketType packet) {
std::queue<Packet> filteredPackets;
std::lock_guard<std::mutex> lock(this->pipeMutex);
Expand Down

0 comments on commit 5c71e53

Please sign in to comment.