diff --git a/src/component/chat.cpp b/src/component/chat.cpp index 22fd2056..f32ad7de 100644 --- a/src/component/chat.cpp +++ b/src/component/chat.cpp @@ -1,15 +1,16 @@ #include -#include "chat.hpp" -#include "scheduler.hpp" +#include "loader/component_loader.hpp" #include "game/game.hpp" #include "game/dvars.hpp" +#include "chat.hpp" +#include "scheduler.hpp" + #include #include #define chat_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25) -#define material_white game::Material_RegisterHandle("white") namespace chat { @@ -78,11 +79,17 @@ namespace chat history.push_front(m); } - void init() + class component final : public component_interface { - scheduler::loop([]() + public: + void post_unpack() override { - draw_chat(); - }, scheduler::pipeline::renderer); - } -} \ No newline at end of file + scheduler::loop([]() + { + draw_chat(); + }, scheduler::pipeline::renderer); + } + }; +} + +REGISTER_COMPONENT(chat::component) diff --git a/src/component/chat.hpp b/src/component/chat.hpp index 9b406e7b..d1428459 100644 --- a/src/component/chat.hpp +++ b/src/component/chat.hpp @@ -3,6 +3,4 @@ namespace chat { void print(const std::string& msg); - - void init(); } \ No newline at end of file diff --git a/src/component/command.cpp b/src/component/command.cpp index 7aca8225..b0613cba 100644 --- a/src/component/command.cpp +++ b/src/component/command.cpp @@ -1,7 +1,12 @@ #include -#include "command.hpp" +#include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/dvars.hpp" + +#include "command.hpp" +#include "game_console.hpp" +#include "chat.hpp" #include #include @@ -142,137 +147,77 @@ namespace command } } - void init() + class component final : public component_interface { - utils::hook::jump(game::base_address + 0x5A74F0, dvar_command_stub, true); - - add("say", [](const params& params) + public: + void post_unpack() override { - chat::print(params.join(1)); - }); + utils::hook::jump(game::base_address + 0x5A74F0, dvar_command_stub, true); - add("listassetpool", [](const params& params) - { - if (params.size() < 2) + add("say", [](const params& params) { - game_console::print(game_console::con_type_info, "listassetpool : list all the assets in the specified pool\n"); + chat::print(params.join(1)); + }); - for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++) + add("listassetpool", [](const params& params) + { + if (params.size() < 2) { - game_console::print(game_console::con_type_info, "%d %s\n", i, game::g_assetNames[i]); + game_console::print(game_console::con_type_info, "listassetpool : list all the assets in the specified pool\n"); + + for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++) + { + game_console::print(game_console::con_type_info, "%d %s\n", i, game::g_assetNames[i]); + } } - } - else - { - const auto type = static_cast(atoi(params.get(1))); - - if (type < 0 || type >= game::XAssetType::ASSET_TYPE_COUNT) + else { - game_console::print(game_console::con_type_info, "Invalid pool passed must be between [%d, %d]\n", 0, game::XAssetType::ASSET_TYPE_COUNT - 1); - return; + const auto type = static_cast(atoi(params.get(1))); + + if (type < 0 || type >= game::XAssetType::ASSET_TYPE_COUNT) + { + game_console::print(game_console::con_type_info, "Invalid pool passed must be between [%d, %d]\n", 0, game::XAssetType::ASSET_TYPE_COUNT - 1); + return; + } + + game_console::print(game_console::con_type_info, "Listing assets in pool %s\n", game::g_assetNames[type]); + + enum_assets(type, [type](const game::XAssetHeader header) + { + const auto asset = game::XAsset{ type, header }; + const auto* const asset_name = game::DB_GetXAssetName(&asset); + //const auto entry = game::DB_FindXAssetEntry(type, asset_name); + //TODO: display which zone the asset is from + game_console::print(game_console::con_type_info, "%s\n", asset_name); + }, true); + } + }); + + add("baseAddress", []() + { + printf("%p\n", (void*)game::base_address); + }); + + add("commandDump", []() + { + printf("======== Start command dump =========\n"); + + game::cmd_function_s* cmd = (*game::cmd_functions); + + while (cmd) + { + if (cmd->name) + { + game_console::print(game_console::con_type_info, "%s\n", cmd->name); + } + + cmd = cmd->next; } - game_console::print(game_console::con_type_info, "Listing assets in pool %s\n", game::g_assetNames[type]); - - enum_assets(type, [type](const game::XAssetHeader header) - { - const auto asset = game::XAsset{ type, header }; - const auto* const asset_name = game::DB_GetXAssetName(&asset); - //const auto entry = game::DB_FindXAssetEntry(type, asset_name); - //TODO: display which zone the asset is from - game_console::print(game_console::con_type_info, "%s\n", asset_name); - }, true); - } - }); - - add("baseAddress", []() - { - printf("%p\n", (void*)game::base_address); - }); - - add("commandDump", []() - { - printf("======== Start command dump =========\n"); - - game::cmd_function_s* cmd = (*game::cmd_functions); - - while (cmd) - { - if (cmd->name) - { - game_console::print(game_console::con_type_info, "%s\n", cmd->name); - } - - cmd = cmd->next; - } - - printf("======== End command dump =========\n"); - }); - - /*add("noclip", [&]() - { - if (!game::SV_Loaded()) - { - return; - } - - game::sp::g_entities[0].client->flags ^= 1; - game::CG_GameMessage(0, utils::string::va("noclip %s", - game::sp::g_entities[0].client->flags & 1 - ? "^2on" - : "^1off")); - }); - - add("ufo", [&]() - { - if (!game::SV_Loaded()) - { - return; - } - - game::sp::g_entities[0].client->flags ^= 2; - game::CG_GameMessage( - 0, utils::string::va("ufo %s", game::sp::g_entities[0].client->flags & 2 ? "^2on" : "^1off")); - }); - - add("give", [](const params& params) - { - if (!game::SV_Loaded()) - { - return; - } - - if (params.size() < 2) - { - game::CG_GameMessage(0, "You did not specify a weapon name"); - return; - } - - auto ps = game::SV_GetPlayerstateForClientNum(0); - auto wp = game::G_GetWeaponForName(params.get(1)); - if (game::G_GivePlayerWeapon(ps, wp, 0, 0, 0)) - { - game::G_InitializeAmmo(ps, wp, 0); - game::G_SelectWeapon(0, wp); - } - }); - - add("take", [](const params& params) - { - if (!game::SV_Loaded()) - { - return; - } - - if (params.size() < 2) - { - game::CG_GameMessage(0, "You did not specify a weapon name"); - return; - } - - auto ps = game::SV_GetPlayerstateForClientNum(0); - auto wp = game::G_GetWeaponForName(params.get(1)); - game::G_TakePlayerWeapon(ps, wp); - });*/ - } + printf("======== End command dump =========\n"); + }); + } + }; } + +REGISTER_COMPONENT(command::component) diff --git a/src/component/command.hpp b/src/component/command.hpp index 74a51cb7..b7a4e5db 100644 --- a/src/component/command.hpp +++ b/src/component/command.hpp @@ -25,6 +25,4 @@ namespace command void add(const char* name, const std::function& callback); void execute(std::string command, bool sync = false); - - void init(); } diff --git a/src/component/fps.cpp b/src/component/fps.cpp new file mode 100644 index 00000000..634e5529 --- /dev/null +++ b/src/component/fps.cpp @@ -0,0 +1,62 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include "chat.hpp" +#include "scheduler.hpp" + +#include +#include + +#define fps_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25) + +namespace fps +{ + namespace + { + std::chrono::nanoseconds frametime; + auto lastframe = std::chrono::high_resolution_clock::now(); + + float fps_color_good[4] = { 0.6f, 1.0f, 0.0f, 1.0f }; + float fps_color_ok[4] = { 1.0f, 0.7f, 0.3f, 1.0f }; + float fps_color_bad[4] = { 1.0f, 0.3f, 0.3f, 1.0f }; + + float screen_max[2]; + + void check_resize() + { + screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0]; + screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1]; + } + + void draw_fps() + { + check_resize(); + + const auto now = std::chrono::high_resolution_clock::now(); + const auto frametime = now - lastframe; + lastframe = now; + + const int fps = 1000000000 / frametime.count(); + + const auto fps_string = utils::string::va("%i", fps); + const auto x = screen_max[0] - 15.f - game::R_TextWidth(fps_string, 0x7FFFFFFF, fps_font); + + game::R_AddCmdDrawText(fps_string, 0x7FFFFFFF, fps_font, x, 25.f, 1.0f, 1.0f, 0.0f, + fps >= 60 ? fps_color_good : (fps >= 30 ? fps_color_ok : fps_color_bad), 0); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + scheduler::loop(draw_fps, scheduler::pipeline::renderer); + } + }; +} + +REGISTER_COMPONENT(fps::component) diff --git a/src/component/game_console.cpp b/src/component/game_console.cpp index 73f991c2..559658fc 100644 --- a/src/component/game_console.cpp +++ b/src/component/game_console.cpp @@ -1,18 +1,19 @@ #include -#include "game_console.hpp" -#include "command.hpp" -#include "scheduler.hpp" +#include "loader/component_loader.hpp" #include "game/game.hpp" #include "game/dvars.hpp" +#include "game_console.hpp" +#include "command.hpp" +#include "scheduler.hpp" + #include "game/scripting/event.hpp" #include "game/scripting/execution.hpp" #include "game/scripting/lua/engine.hpp" #include #include -#include #define console_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 18) #define material_white game::Material_RegisterHandle("white") @@ -620,82 +621,96 @@ namespace game_console return true; } - void init() + class component final : public component_interface { - scheduler::loop(draw_console, scheduler::pipeline::renderer); - - con.cursor = 0; - con.visible_line_count = 0; - con.output_visible = false; - con.display_line_offset = 0; - con.line_count = 0; - strncpy_s(con.buffer, "", 256); - - con.globals.x = 0.0f; - con.globals.y = 0.0f; - con.globals.left_x = 0.0f; - con.globals.font_height = 0.0f; - con.globals.may_auto_complete = false; - con.globals.info_line_count = 0; - strncpy_s(con.globals.auto_complete_choice, "", 64); - - // add clear command - command::add("clear", [&]() + public: + void post_unpack() override { - clear(); + scheduler::loop(draw_console, scheduler::pipeline::renderer); + + con.cursor = 0; + con.visible_line_count = 0; + con.output_visible = false; + con.display_line_offset = 0; con.line_count = 0; - con.output.clear(); - history_index = -1; - history.clear(); - }); + strncpy_s(con.buffer, "", 256); - char a2[1] = {}; + con.globals.x = 0.0f; + con.globals.y = 0.0f; + con.globals.left_x = 0.0f; + con.globals.font_height = 0.0f; + con.globals.may_auto_complete = false; + con.globals.info_line_count = 0; + strncpy_s(con.globals.auto_complete_choice, "", 64); - // add our dvars - dvars::con_inputBoxColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputBoxColor"), a2, - 0.2f, 0.2f, 0.2f, 0.9f, - 0.0f, 1.0f, - 1); + // add clear command + command::add("clear", [&]() + { + clear(); + con.line_count = 0; + con.output.clear(); + history_index = -1; + history.clear(); + }); - dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputHintBoxColor"), a2, - 0.3f, 0.3f, 0.3f, 1.0f, - 0.0f, 1.0f, - 1); + char a2[1] = {}; - dvars::con_outputBarColor = game::Dvar_RegisterVec4(game::generateHashValue("con_outputBarColor"), a2, - 0.5f, 0.5f, 0.5f, 0.6f, - 0.0f, 1.0f, - 1); + // add our dvars + dvars::con_inputBoxColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_inputBoxColor"), a2, + 0.2f, 0.2f, 0.2f, 0.9f, + 0.0f, 1.0f, + 1); - dvars::con_outputSliderColor = game::Dvar_RegisterVec4(game::generateHashValue("con_outputSliderColor"), a2, - 0.0f, 0.7f, 1.0f, 1.00f, - 0.0f, 1.0f, - 1); + dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_inputHintBoxColor"), a2, + 0.3f, 0.3f, 0.3f, 1.0f, + 0.0f, 1.0f, + 1); - dvars::con_outputWindowColor = game::Dvar_RegisterVec4(game::generateHashValue("con_outputWindowColor"), a2, - 0.25f, 0.25f, 0.25f, 0.85f, - 0.0f, 1.0f, - 1); + dvars::con_outputBarColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_outputBarColor"), a2, + 0.5f, 0.5f, 0.5f, 0.6f, + 0.0f, 1.0f, + 1); - dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputDvarMatchColor"), a2, - 1.0f, 1.0f, 0.8f, 1.0f, - 0.0f, 1.0f, - 1); + dvars::con_outputSliderColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_outputSliderColor"), a2, + 0.0f, 0.7f, 1.0f, 1.00f, + 0.0f, 1.0f, + 1); - dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputDvarValueColor"), a2, - 1.0f, 1.0f, 0.8f, 1.0f, - 0.0f, 1.0f, - 1); + dvars::con_outputWindowColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_outputWindowColor"), a2, + 0.25f, 0.25f, 0.25f, 0.85f, + 0.0f, 1.0f, + 1); - dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputDvarInactiveValueColor"), a2, - 0.8f, 0.8f, 0.8f, 1.0f, - 0.0f, 1.0f, - 1); + dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_inputDvarMatchColor"), a2, + 1.0f, 1.0f, 0.8f, 1.0f, + 0.0f, 1.0f, + 1); - dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4(game::generateHashValue("con_inputCmdMatchColor"), a2, - 0.80f, 0.80f, 1.0f, 1.0f, - 0.0f, 1.0f, - 1); + dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_inputDvarValueColor"), a2, + 1.0f, 1.0f, 0.8f, 1.0f, + 0.0f, 1.0f, + 1); - } + dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_inputDvarInactiveValueColor"), a2, + 0.8f, 0.8f, 0.8f, 1.0f, + 0.0f, 1.0f, + 1); + + dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4( + game::generateHashValue("con_inputCmdMatchColor"), a2, + 0.80f, 0.80f, 1.0f, 1.0f, + 0.0f, 1.0f, + 1); + } + }; } + +REGISTER_COMPONENT(game_console::component) diff --git a/src/component/game_console.hpp b/src/component/game_console.hpp index 23fc157c..7c4fc21a 100644 --- a/src/component/game_console.hpp +++ b/src/component/game_console.hpp @@ -15,6 +15,4 @@ namespace game_console bool console_key_event(int local_client_num, int key, int down); void execute(const char* cmd); - - void init(); } \ No newline at end of file diff --git a/src/component/input.cpp b/src/component/input.cpp index 8c70335a..a636350a 100644 --- a/src/component/input.cpp +++ b/src/component/input.cpp @@ -1,4 +1,5 @@ #include +#include "loader/component_loader.hpp" #include "game/game.hpp" @@ -34,9 +35,15 @@ namespace input } } - void init() + class component final : public component_interface { - cl_char_event_hook.create(game::base_address + 0x3D27B0, cl_char_event_stub); - cl_key_event_hook.create(game::base_address + 0x3D2AE0, cl_key_event_stub); - } + public: + void post_unpack() override + { + cl_char_event_hook.create(game::base_address + 0x3D27B0, cl_char_event_stub); + cl_key_event_hook.create(game::base_address + 0x3D2AE0, cl_key_event_stub); + } + }; } + +REGISTER_COMPONENT(input::component) diff --git a/src/component/input.hpp b/src/component/input.hpp deleted file mode 100644 index c187288f..00000000 --- a/src/component/input.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -namespace input -{ - void init(); -} diff --git a/src/component/patches.cpp b/src/component/patches.cpp new file mode 100644 index 00000000..350c0cd2 --- /dev/null +++ b/src/component/patches.cpp @@ -0,0 +1,29 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" + +#include + +namespace patches +{ + namespace + { + + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + // Unlock fps in main menu + utils::hook::set(game::base_address + 0x3D8E1B, 0xEB); + + // Disable battle net popup + utils::hook::set(game::base_address + 0xBE7F83C, true); + } + }; +} + +REGISTER_COMPONENT(patches::component) diff --git a/src/component/scheduler.cpp b/src/component/scheduler.cpp index 151bccc8..c52ae63b 100644 --- a/src/component/scheduler.cpp +++ b/src/component/scheduler.cpp @@ -1,6 +1,9 @@ #include +#include "loader/component_loader.hpp" + #include "scheduler.hpp" #include "game/game.hpp" + #include #include @@ -135,18 +138,24 @@ namespace scheduler }, type, delay); } - void init() + class component final : public component_interface { - thread = std::thread([]() + public: + void post_unpack() override { - while (!kill) + thread = std::thread([]() { - execute(pipeline::async); - std::this_thread::sleep_for(10ms); - } - }); + while (!kill) + { + execute(pipeline::async); + std::this_thread::sleep_for(10ms); + } + }); - r_end_frame_hook.create(game::base_address + 0x76D7B0, scheduler::r_end_frame_stub); - g_run_frame_hook.create(game::base_address + 0x4CB030, scheduler::server_frame_stub); - } + r_end_frame_hook.create(game::base_address + 0x76D7B0, scheduler::r_end_frame_stub); + g_run_frame_hook.create(game::base_address + 0x4CB030, scheduler::server_frame_stub); + } + }; } + +REGISTER_COMPONENT(scheduler::component) diff --git a/src/component/scheduler.hpp b/src/component/scheduler.hpp index c129c96f..7332248d 100644 --- a/src/component/scheduler.hpp +++ b/src/component/scheduler.hpp @@ -28,6 +28,4 @@ namespace scheduler std::chrono::milliseconds delay = 0ms); void once(const std::function& callback, pipeline type = pipeline::async, std::chrono::milliseconds delay = 0ms); - - void init(); } diff --git a/src/component/scripting.cpp b/src/component/scripting.cpp index cfd5c496..c36e2137 100644 --- a/src/component/scripting.cpp +++ b/src/component/scripting.cpp @@ -1,13 +1,15 @@ #include +#include "loader/component_loader.hpp" #include "game/game.hpp" -#include + +#include "scheduler.hpp" #include "game/scripting/event.hpp" #include "game/scripting/execution.hpp" #include "game/scripting/lua/engine.hpp" -#include "scheduler.hpp" +#include namespace scripting { @@ -77,18 +79,24 @@ namespace scripting } } - void init() + class component final : public component_interface { - vm_notify_hook.create(game::base_address + 0x5CC450, vm_notify_stub); - - scr_load_level_hook.create(game::base_address + 0x520AB0, scr_load_level_stub); - g_shutdown_game_hook.create(game::base_address + 0x4CBAD0, g_shutdown_game_stub); - - scr_add_class_field_hook.create(game::base_address + 0x5C2C30, scr_add_class_field_stub); - - scheduler::loop([]() + public: + void post_unpack() override { - lua::engine::run_frame(); - }, scheduler::pipeline::server); - } + vm_notify_hook.create(game::base_address + 0x5CC450, vm_notify_stub); + + scr_load_level_hook.create(game::base_address + 0x520AB0, scr_load_level_stub); + g_shutdown_game_hook.create(game::base_address + 0x4CBAD0, g_shutdown_game_stub); + + scr_add_class_field_hook.create(game::base_address + 0x5C2C30, scr_add_class_field_stub); + + scheduler::loop([]() + { + lua::engine::run_frame(); + }, scheduler::pipeline::server); + } + }; } + +REGISTER_COMPONENT(scripting::component) diff --git a/src/component/scripting.hpp b/src/component/scripting.hpp index 4bd33dc8..adf9be6c 100644 --- a/src/component/scripting.hpp +++ b/src/component/scripting.hpp @@ -3,6 +3,4 @@ namespace scripting { extern std::unordered_map> fields_table; - - void init(); } \ No newline at end of file diff --git a/src/dllmain.cpp b/src/dllmain.cpp index a78a8297..ac9975c7 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -1,8 +1,14 @@ -#include "stdinc.hpp" +#include + +#include "game/game.hpp" + +#include "component/game_console.hpp" + +#include "loader/component_loader.hpp" #pragma warning(disable:4996) -DWORD WINAPI dwConsole(LPVOID) +DWORD WINAPI console(LPVOID) { AllocConsole(); AttachConsole(GetCurrentProcessId()); @@ -25,18 +31,11 @@ DWORD WINAPI dwConsole(LPVOID) void init() { - CreateThread(0, 0, dwConsole, 0, 0, 0); + CreateThread(0, 0, console, 0, 0, 0); game::load_base_address(); - utils::hook::set(game::base_address + 0xBE7F83C, true); // disable bnet popup - - command::init(); - input::init(); - scheduler::init(); - game_console::init(); - scripting::init(); - chat::init(); + component_loader::post_unpack(); } BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) diff --git a/src/game/game.hpp b/src/game/game.hpp index a880afed..229bc46f 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -1,5 +1,7 @@ #pragma once +#include "structs.hpp" + namespace game { extern uint64_t base_address; diff --git a/src/game/scripting/lua/context.cpp b/src/game/scripting/lua/context.cpp index 1079d92e..e08da0e7 100644 --- a/src/game/scripting/lua/context.cpp +++ b/src/game/scripting/lua/context.cpp @@ -7,6 +7,7 @@ #include "../functions.hpp" #include "../../../component/command.hpp" +#include "../../../component/chat.hpp" #include diff --git a/src/loader/component_interface.hpp b/src/loader/component_interface.hpp new file mode 100644 index 00000000..e1ee433f --- /dev/null +++ b/src/loader/component_interface.hpp @@ -0,0 +1,35 @@ +#pragma once + +class component_interface +{ +public: + virtual ~component_interface() + { + } + + virtual void post_start() + { + } + + virtual void post_load() + { + } + + virtual void pre_destroy() + { + } + + virtual void post_unpack() + { + } + + virtual void* load_import([[maybe_unused]] const std::string& library, [[maybe_unused]] const std::string& function) + { + return nullptr; + } + + virtual bool is_supported() + { + return true; + } +}; diff --git a/src/loader/component_loader.cpp b/src/loader/component_loader.cpp new file mode 100644 index 00000000..d6a0090f --- /dev/null +++ b/src/loader/component_loader.cpp @@ -0,0 +1,127 @@ +#include +#include "component_loader.hpp" + +void component_loader::register_component(std::unique_ptr&& component_) +{ + get_components().push_back(std::move(component_)); +} + +bool component_loader::post_start() +{ + static auto handled = false; + if (handled) return true; + handled = true; + + try + { + for (const auto& component_ : get_components()) + { + component_->post_start(); + } + } + catch (premature_shutdown_trigger&) + { + return false; + } + + return true; +} + +bool component_loader::post_load() +{ + static auto handled = false; + if (handled) return true; + handled = true; + + clean(); + + try + { + for (const auto& component_ : get_components()) + { + component_->post_load(); + } + } + catch (premature_shutdown_trigger&) + { + return false; + } + + return true; +} + +void component_loader::post_unpack() +{ + static auto handled = false; + if (handled) return; + handled = true; + + for (const auto& component_ : get_components()) + { + component_->post_unpack(); + } +} + +void component_loader::pre_destroy() +{ + static auto handled = false; + if (handled) return; + handled = true; + + for (const auto& component_ : get_components()) + { + component_->pre_destroy(); + } +} + +void component_loader::clean() +{ + auto& components = get_components(); + for (auto i = components.begin(); i != components.end();) + { + if (!(*i)->is_supported()) + { + (*i)->pre_destroy(); + i = components.erase(i); + } + else + { + ++i; + } + } +} + +void* component_loader::load_import(const std::string& library, const std::string& function) +{ + void* function_ptr = nullptr; + + for (const auto& component_ : get_components()) + { + auto* const component_function_ptr = component_->load_import(library, function); + if (component_function_ptr) + { + function_ptr = component_function_ptr; + } + } + + return function_ptr; +} + +void component_loader::trigger_premature_shutdown() +{ + throw premature_shutdown_trigger(); +} + +std::vector>& component_loader::get_components() +{ + using component_vector = std::vector>; + using component_vector_container = std::unique_ptr>; + + static component_vector_container components(new component_vector, [](component_vector* component_vector) + { + pre_destroy(); + delete component_vector; + }); + + return *components; +} diff --git a/src/loader/component_loader.hpp b/src/loader/component_loader.hpp new file mode 100644 index 00000000..4ff8f3c4 --- /dev/null +++ b/src/loader/component_loader.hpp @@ -0,0 +1,61 @@ +#pragma once +#include "component_interface.hpp" + +class component_loader final +{ +public: + class premature_shutdown_trigger final : public std::exception + { + [[nodiscard]] const char* what() const noexcept override + { + return "Premature shutdown requested"; + } + }; + + template + class installer final + { + static_assert(std::is_base_of::value, "component has invalid base class"); + + public: + installer() + { + register_component(std::make_unique()); + } + }; + + template + static T* get() + { + for (const auto& component_ : get_components()) + { + if (typeid(*component_.get()) == typeid(T)) + { + return reinterpret_cast(component_.get()); + } + } + + return nullptr; + } + + static void register_component(std::unique_ptr&& component); + + static bool post_start(); + static bool post_load(); + static void post_unpack(); + static void pre_destroy(); + static void clean(); + + static void* load_import(const std::string& library, const std::string& function); + + static void trigger_premature_shutdown(); + +private: + static std::vector>& get_components(); +}; + +#define REGISTER_COMPONENT(name) \ +namespace \ +{ \ + static component_loader::installer __component; \ +} diff --git a/src/stdinc.hpp b/src/stdinc.hpp index fb60fedf..a8f96e08 100644 --- a/src/stdinc.hpp +++ b/src/stdinc.hpp @@ -34,21 +34,4 @@ #include #include -using namespace std::literals; - -#include "utils/memory.hpp" -#include "utils/string.hpp" -#include "utils/hook.hpp" -#include "utils/string.hpp" -#include "utils/io.hpp" - -#include "game/structs.hpp" -#include "game/game.hpp" -#include "game/dvars.hpp" - -#include "component/command.hpp" -#include "component/scripting.hpp" -#include "component/scheduler.hpp" -#include "component/input.hpp" -#include "component/game_console.hpp" -#include "component/chat.hpp" +using namespace std::literals; \ No newline at end of file diff --git a/src/utils/hook.cpp b/src/utils/hook.cpp index f570731b..599879cc 100644 --- a/src/utils/hook.cpp +++ b/src/utils/hook.cpp @@ -1,4 +1,5 @@ -#include "stdinc.hpp" +#include + #include "hook.hpp" #include "string.hpp" diff --git a/src/utils/io.cpp b/src/utils/io.cpp index b2325a0f..3d479d00 100644 --- a/src/utils/io.cpp +++ b/src/utils/io.cpp @@ -1,4 +1,5 @@ #include + #include "io.hpp" #include diff --git a/src/utils/memory.cpp b/src/utils/memory.cpp index 3abe1a1c..bc256c11 100644 --- a/src/utils/memory.cpp +++ b/src/utils/memory.cpp @@ -1,6 +1,8 @@ // https://github.com/momo5502/open-iw5 -#include "stdinc.hpp" +#include + +#include "memory.hpp" namespace utils { @@ -54,15 +56,6 @@ namespace utils return this->pool_.empty(); } - /*char* memory::allocator::duplicate_string(const std::string& string) - { - std::lock_guard _(this->mutex_); - - const auto data = memory::duplicate_string(string); - this->pool_.push_back(data); - return data; - }*/ - void* memory::allocate(const size_t length) { const auto data = calloc(length, 1); @@ -70,13 +63,6 @@ namespace utils return data; } - /*char* memory::duplicate_string(const std::string& string) - { - const auto new_string = allocate_array(string.size() + 1); - std::memcpy(new_string, string.data(), string.size()); - return new_string; - }*/ - void memory::free(void* data) { if (data) diff --git a/src/utils/memory.hpp b/src/utils/memory.hpp index bcc9d965..e783e962 100644 --- a/src/utils/memory.hpp +++ b/src/utils/memory.hpp @@ -34,8 +34,6 @@ namespace utils bool empty() const; - //char* duplicate_string(const std::string& string); - private: std::mutex mutex_; std::vector pool_; @@ -55,8 +53,6 @@ namespace utils return static_cast(allocate(count * sizeof(T))); } - //static char* duplicate_string(const std::string& string); - static void free(void* data); static void free(const void* data); diff --git a/src/utils/string.cpp b/src/utils/string.cpp index a1bc1cce..92c1ecca 100644 --- a/src/utils/string.cpp +++ b/src/utils/string.cpp @@ -1,6 +1,9 @@ // iw6x-client -#include "stdinc.hpp" +#include + +#include "memory.hpp" +#include "string.hpp" namespace utils::string { diff --git a/src/utils/string.hpp b/src/utils/string.hpp index 174228bd..b2f7d694 100644 --- a/src/utils/string.hpp +++ b/src/utils/string.hpp @@ -1,5 +1,4 @@ #pragma once -#include "stdinc.hpp" namespace utils::string {