diff --git a/.gitmodules b/.gitmodules index 92d0b332..1f577bd3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,3 +32,6 @@ [submodule "deps/dxsdk"] path = deps/dxsdk url = https://github.com/devKlausS/dxsdk.git +[submodule "deps/GSL"] + path = deps/GSL + url = https://github.com/microsoft/GSL.git diff --git a/deps/GSL b/deps/GSL new file mode 160000 index 00000000..da01eb28 --- /dev/null +++ b/deps/GSL @@ -0,0 +1 @@ +Subproject commit da01eb28dbb75bed11a51acff0f80ecedd622573 diff --git a/deps/premake/gsl.lua b/deps/premake/gsl.lua new file mode 100644 index 00000000..7a2daf64 --- /dev/null +++ b/deps/premake/gsl.lua @@ -0,0 +1,19 @@ +gsl = { + source = path.join(dependencies.basePath, "GSL"), +} + +function gsl.import() + gsl.includes() +end + +function gsl.includes() + includedirs { + path.join(gsl.source, "include") + } +end + +function gsl.project() + +end + +table.insert(dependencies, gsl) diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 119cfe01..36f03585 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -5,8 +5,8 @@ namespace Components class Component { public: - Component() {}; - virtual ~Component() {}; + Component() {} + virtual ~Component() {} #if defined(DEBUG) || defined(FORCE_UNIT_TESTS) virtual std::string getName() @@ -20,8 +20,8 @@ namespace Components // It's illegal to spawn threads in DLLMain, and apparently it causes problems if they are destroyed there as well. // This method is called before DLLMain (if possible) and should to destroy threads. // It's not 100% guaranteed that it's called outside DLLMain, as it depends on the game, but it's 100% guaranteed, that it is called at all. - virtual void preDestroy() {}; - virtual bool unitTest() { return true; }; // Unit testing entry + virtual void preDestroy() {} + virtual bool unitTest() { return true; } // Unit testing entry }; class Loader diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index f5df53b9..f20ce605 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -514,18 +514,19 @@ namespace Components if (!ZoneBuilder::IsEnabled()) Utils::Hook(0x5BB3F2, AssetHandler::MissingAssetError, HOOK_CALL).install()->quick(); // Log missing empty assets - Scheduler::OnFrame([]() + Scheduler::Loop([] { if (FastFiles::Ready() && !AssetHandler::EmptyAssets.empty()) { for (auto& asset : AssetHandler::EmptyAssets) { - Game::Com_PrintWarning(25, reinterpret_cast(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data()); + Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_FILES, + reinterpret_cast(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data()); } AssetHandler::EmptyAssets.clear(); } - }); + }, Scheduler::Pipeline::MAIN); AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*) { diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index 51e0bd29..54fd81b2 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -429,7 +429,7 @@ namespace Components Auth::LoadKey(true); Steam::SteamUser()->GetSteamID(); - Scheduler::OnFrame(Auth::Frame); + Scheduler::Loop(Auth::Frame, Scheduler::Pipeline::MAIN); // Register dvar Dvar::Register("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_SERVERINFO, "Security level for GUID certificates (POW)"); diff --git a/src/Components/Modules/Bans.cpp b/src/Components/Modules/Bans.cpp index 8e43cc2d..27929498 100644 --- a/src/Components/Modules/Bans.cpp +++ b/src/Components/Modules/Bans.cpp @@ -13,7 +13,7 @@ namespace Components if (entry.first.bits) { - for (auto& idEntry : list.idList) + for (const auto& idEntry : list.idList) { if (idEntry.bits == entry.first.bits) { @@ -268,10 +268,10 @@ namespace Components }); // Verify the list on startup - Scheduler::Once([]() + Scheduler::OnGameInitialized([] { Bans::BanList list; Bans::LoadBans(&list); - }); + }, Scheduler::Pipeline::SERVER); } } diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 7f0952d9..1158bd1d 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -83,28 +83,30 @@ namespace Components void Bots::Spawn(unsigned int count) { - for (auto i = 0u; i < count; ++i) + for (std::size_t i = 0; i < count; ++i) { - Scheduler::OnDelay([]() + Scheduler::Once([] { auto* ent = Game::SV_AddTestClient(); if (ent == nullptr) return; - Scheduler::OnDelay([ent]() + Scheduler::Once([ent] { Game::Scr_AddString("autoassign"); Game::Scr_AddString("team_marinesopfor"); Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2); - Scheduler::OnDelay([ent]() + Scheduler::Once([ent] { Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u)); Game::Scr_AddString("changeclass"); Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2); - }, 1s); - }, 1s); - }, 500ms * (i + 1)); + }, Scheduler::Pipeline::SERVER, 1s); + + }, Scheduler::Pipeline::SERVER, 1s); + + }, Scheduler::Pipeline::SERVER, 500ms * (i + 1)); } } diff --git a/src/Components/Modules/Branding.cpp b/src/Components/Modules/Branding.cpp index 3a513af6..2e0b4686 100644 --- a/src/Components/Modules/Branding.cpp +++ b/src/Components/Modules/Branding.cpp @@ -95,7 +95,7 @@ namespace Components Branding::Branding() { - Dvar::OnInit(Branding::RegisterBrandingDvars); + Branding::RegisterBrandingDvars(); // UI version string Utils::Hook::Set(0x43F73B, "IW4x: " VERSION); diff --git a/src/Components/Modules/Bullet.cpp b/src/Components/Modules/Bullet.cpp index c07476d7..b8ec1338 100644 --- a/src/Components/Modules/Bullet.cpp +++ b/src/Components/Modules/Bullet.cpp @@ -44,15 +44,12 @@ namespace Components Bullet::Bullet() { - Dvar::OnInit([] - { - BGSurfacePenetration = Dvar::Register("bg_surfacePenetration", 0.0f, - 0.0f, std::numeric_limits::max(), Game::dvar_flag::DVAR_CODINFO, - "Set to a value greater than 0 to override the surface penetration depth"); - BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f, - 0.0f, std::numeric_limits::max(), Game::dvar_flag::DVAR_CODINFO, - "Max range used when calculating the bullet end position"); - }); + BGSurfacePenetration = Dvar::Register("bg_surfacePenetration", 0.0f, + 0.0f, std::numeric_limits::max(), Game::dvar_flag::DVAR_CODINFO, + "Set to a value greater than 0 to override the surface penetration depth"); + BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f, + 0.0f, std::numeric_limits::max(), Game::dvar_flag::DVAR_CODINFO, + "Max range used when calculating the bullet end position"); Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick(); diff --git a/src/Components/Modules/CardTitles.cpp b/src/Components/Modules/CardTitles.cpp index fdc3604e..c5eae749 100644 --- a/src/Components/Modules/CardTitles.cpp +++ b/src/Components/Modules/CardTitles.cpp @@ -190,10 +190,10 @@ namespace Components CardTitles::CardTitles() { - Dvar::OnInit([]() + Scheduler::Once([] { CardTitles::CustomTitleDvar = Dvar::Register("customtitle", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, "Custom card title"); - }); + }, Scheduler::Pipeline::MAIN); ServerCommands::OnCommand(21, [](Command::Params* params) { diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index 561d0bec..f675fdd6 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -324,15 +324,9 @@ namespace Components Chat::Chat() { - Dvar::OnInit([] - { - Chat::cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, - std::numeric_limits::max(), Game::dvar_flag::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); - Chat::sv_disableChat = Dvar::Register("sv_disableChat", false, - Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients"); - - Chat::AddChatCommands(); - }); + cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, std::numeric_limits::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); + Chat::sv_disableChat = Dvar::Register("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients"); + Scheduler::Once(Chat::AddChatCommands, Scheduler::Pipeline::MAIN); // Intercept chat sending Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Clantags.cpp b/src/Components/Modules/Clantags.cpp index 8d127a5f..c137ef97 100644 --- a/src/Components/Modules/Clantags.cpp +++ b/src/Components/Modules/Clantags.cpp @@ -73,10 +73,11 @@ namespace Components ClanTags::ClanTags() { // Create clantag dvar - Dvar::OnInit([]() + Scheduler::Once([] { - Dvar::Register("clantag", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, "If set, your clantag will be shown on the scoreboard."); - }); + Dvar::Register("clantag", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, + "If set, your clantag will be shown on the scoreboard."); + }, Scheduler::Pipeline::MAIN); // Servercommand hook ServerCommands::OnCommand(22, [](Command::Params* params) diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 5b429a3f..6896bbd9 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -246,6 +246,23 @@ namespace Components ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time }); + ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + { + assert(ent->client != nullptr); + assert(ent->client->connected != Game::clientConnected_t::CON_DISCONNECTED); + + if (ent->client->sessionState != Game::sessionState_t::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent)) + return; + + Scheduler::Once([ent] + { + ent->flags &= ~(Game::entityFlag::FL_GODMODE | Game::entityFlag::FL_DEMI_GODMODE); + ent->health = 0; + ent->client->ps.stats[0] = 0; + Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::hitLocation_t::HITLOC_NONE, 0); + }, Scheduler::Pipeline::SERVER); + }); + ClientCommand::Add("give", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) diff --git a/src/Components/Modules/ConnectProtocol.cpp b/src/Components/Modules/ConnectProtocol.cpp index c07d3df8..703bb2f5 100644 --- a/src/Components/Modules/ConnectProtocol.cpp +++ b/src/Components/Modules/ConnectProtocol.cpp @@ -213,7 +213,7 @@ namespace Components }); // Invocation handler - Scheduler::OnReady(ConnectProtocol::Invocation); + Scheduler::OnGameInitialized(ConnectProtocol::Invocation, Scheduler::Pipeline::MAIN); ConnectProtocol::InstallProtocol(); ConnectProtocol::EvaluateProtocol(); @@ -232,10 +232,10 @@ namespace Components // Only skip intro here, invocation will be done later. Utils::Hook::Set(0x60BECF, 0xEB); - Scheduler::OnDelay([]() + Scheduler::Once([]() { Command::Execute("openmenu popup_reconnectingtoparty", false); - }, 8s); + }, Scheduler::Pipeline::CLIENT, 8s); } } } diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index 20cfb527..0f2bc7d6 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -580,7 +580,7 @@ namespace Components if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) { - Scheduler::OnFrame(Console::RefreshStatus); + Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN); } // Code below is not necessary when performing unit tests! @@ -622,10 +622,10 @@ namespace Components } }, HOOK_CALL).install()->quick(); - Scheduler::OnFrame([]() + Scheduler::Loop([] { Console::LastRefresh = Game::Sys_Milliseconds(); - }); + }, Scheduler::Pipeline::MAIN); } else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/) { diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index c8446c10..40bd8a4e 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -139,24 +139,79 @@ namespace Components Network::SendCommand(master, "heartbeat", "IW4"); } - __declspec(naked) void Dedicated::FrameStub() - { - __asm - { - pushad - call Scheduler::FrameHandler - popad - - push 5A8E80h - retn - } - } - Game::dvar_t* Dedicated::Dvar_RegisterSVNetworkFps(const char* dvarName, int, int min, int, int, const char* description) { return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::dvar_flag::DVAR_NONE, description); } + void Dedicated::AddDedicatedCommands() + { + Dvar::Register("sv_sayName", "^7Console", Game::dvar_flag::DVAR_NONE, "The name to pose as for 'say' commands"); + Dvar::Register("sv_motd", "", Game::dvar_flag::DVAR_NONE, "A custom message of the day for servers"); + + // Say command + Command::AddSV("say", [](Command::Params* params) + { + if (params->size() < 2) return; + + auto message = params->join(1); + auto name = Dvar::Var("sv_sayName").get(); + + if (!name.empty()) + { + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); + Game::Com_Printf(15, "%s: %s\n", name.data(), message.data()); + } + else + { + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::Com_Printf(15, "Console: %s\n", message.data()); + } + }); + + // Tell command + Command::AddSV("tell", [](Command::Params* params) + { + if (params->size() < 3) return; + + const auto client = atoi(params->get(1)); + auto message = params->join(2); + auto name = Dvar::Var("sv_sayName").get(); + + if (!name.empty()) + { + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); + Game::Com_Printf(15, "%s -> %i: %s\n", name.data(), client, message.data()); + } + else + { + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::Com_Printf(15, "Console -> %i: %s\n", client, message.data()); + } + }); + + // Sayraw command + Command::AddSV("sayraw", [](Command::Params* params) + { + if (params->size() < 2) return; + + auto message = params->join(1); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data())); + Game::Com_Printf(15, "Raw: %s\n", message.data()); + }); + + // Tellraw command + Command::AddSV("tellraw", [](Command::Params* params) + { + if (params->size() < 3) return; + + const auto client = atoi(params->get(1)); + std::string message = params->join(2); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data())); + Game::Com_Printf(15, "Raw -> %i: %s\n", client, message.data()); + }); + } + Dedicated::Dedicated() { Dedicated::COMLogFilter = Dvar::Register("com_logFilter", true, @@ -165,13 +220,10 @@ namespace Components if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) { // Make sure all callbacks are handled - Scheduler::OnFrame(Steam::SteamAPI_RunCallbacks); + Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::MAIN); - Dvar::OnInit([] - { - Dedicated::SVLanOnly = Dvar::Register("sv_lanOnly", false, - Game::dvar_flag::DVAR_NONE, "Don't act as node"); - }); + Dedicated::SVLanOnly = Dvar::Register("sv_lanOnly", false, + Game::dvar_flag::DVAR_NONE, "Don't act as node"); Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick(); @@ -230,111 +282,32 @@ namespace Components // don't load the config Utils::Hook::Set(0x4B4D19, 0xEB); - // Dedicated frame handler - Utils::Hook(0x4B0F81, Dedicated::FrameStub, HOOK_CALL).install()->quick(); - // Intercept time wrapping Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick(); //Utils::Hook::Set(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks) if (!ZoneBuilder::IsEnabled()) { + Scheduler::Once(Dedicated::AddDedicatedCommands, Scheduler::Pipeline::MAIN); + // Post initialization point Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick(); // Transmit custom data - Scheduler::OnFrame([]() + Scheduler::Loop([] { - static Utils::Time::Interval interval; - if (interval.elapsed(10s)) - { - interval.update(); - - CardTitles::SendCustomTitlesToClients(); - //Clantags::SendClantagsToClients(); - } - }); + CardTitles::SendCustomTitlesToClients(); + //Clantags::SendClantagsToClients(); + }, Scheduler::Pipeline::SERVER, 10s); // Heartbeats - Scheduler::Once(Dedicated::Heartbeat); - Scheduler::OnFrame([]() + Scheduler::Loop([] { - static Utils::Time::Interval interval; - - if (Dvar::Var("sv_maxclients").get() > 0 && interval.elapsed(2min)) + if (Dvar::Var("sv_maxclients").get() > 0) { - interval.update(); Dedicated::Heartbeat(); } - }); - - Dvar::OnInit([]() - { - Dvar::Register("sv_sayName", "^7Console", Game::dvar_flag::DVAR_NONE, "The name to pose as for 'say' commands"); - Dvar::Register("sv_motd", "", Game::dvar_flag::DVAR_NONE, "A custom message of the day for servers"); - - // Say command - Command::AddSV("say", [](Command::Params* params) - { - if (params->size() < 2) return; - - std::string message = params->join(1); - std::string name = Dvar::Var("sv_sayName").get(); - - if (!name.empty()) - { - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); - Game::Com_Printf(15, "%s: %s\n", name.data(), message.data()); - } - else - { - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); - Game::Com_Printf(15, "Console: %s\n", message.data()); - } - }); - - // Tell command - Command::AddSV("tell", [](Command::Params* params) - { - if (params->size() < 3) return; - - int client = atoi(params->get(1)); - std::string message = params->join(2); - std::string name = Dvar::Var("sv_sayName").get(); - - if (!name.empty()) - { - Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); - Game::Com_Printf(15, "%s -> %i: %s\n", name.data(), client, message.data()); - } - else - { - Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); - Game::Com_Printf(15, "Console -> %i: %s\n", client, message.data()); - } - }); - - // Sayraw command - Command::AddSV("sayraw", [](Command::Params* params) - { - if (params->size() < 2) return; - - std::string message = params->join(1); - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data())); - Game::Com_Printf(15, "Raw: %s\n", message.data()); - }); - - // Tellraw command - Command::AddSV("tellraw", [](Command::Params* params) - { - if (params->size() < 3) return; - - int client = atoi(params->get(1)); - std::string message = params->join(2); - Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data())); - Game::Com_Printf(15, "Raw -> %i: %s\n", client, message.data()); - }); - }); + }, Scheduler::Pipeline::SERVER, 2min); } } else @@ -363,18 +336,12 @@ namespace Components }); } - Scheduler::OnFrame([]() + Scheduler::Loop([] { if (Dvar::Var("sv_running").get()) { - static Utils::Time::Interval interval; - - if (interval.elapsed(15s)) - { - interval.update(); - Dedicated::TransmitGuids(); - } + Dedicated::TransmitGuids(); } - }); + }, Scheduler::Pipeline::SERVER, 15s); } } diff --git a/src/Components/Modules/Dedicated.hpp b/src/Components/Modules/Dedicated.hpp index 381f34e2..ce4d0f43 100644 --- a/src/Components/Modules/Dedicated.hpp +++ b/src/Components/Modules/Dedicated.hpp @@ -21,12 +21,12 @@ namespace Components static void PostInitialization(); static void PostInitializationStub(); - static void FrameStub(); - static void TransmitGuids(); static void TimeWrapStub(Game::errorParm_t code, const char* message); static Game::dvar_t* Dvar_RegisterSVNetworkFps(const char* dvarName, int value, int min, int max, int flags, const char* description); + + static void AddDedicatedCommands(); }; } diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 5865d569..d6eddceb 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -900,12 +900,12 @@ namespace Components } else { - Dvar::OnInit([]() + Scheduler::Once([] { Dvar::Register("ui_dl_timeLeft", "", Game::dvar_flag::DVAR_NONE, ""); Dvar::Register("ui_dl_progress", "", Game::dvar_flag::DVAR_NONE, ""); Dvar::Register("ui_dl_transRate", "", Game::dvar_flag::DVAR_NONE, ""); - }); + }, Scheduler::Pipeline::MAIN); UIScript::Add("mod_download_cancel", [](UIScript::Token) { @@ -913,7 +913,7 @@ namespace Components }); } - Dvar::OnInit([]() + Scheduler::Once([] { Dvar::Register("sv_wwwDownload", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to enable downloading maps/mods from an external server."); Dvar::Register("sv_wwwBaseUrl", "", Game::dvar_flag::DVAR_ARCHIVE, "Set to the base url for the external map download."); @@ -922,9 +922,9 @@ namespace Components // not saying we are but ya know... accidents happen // by having it saved we force the user to enable it in config_mp because it only checks the dvar on startup to see if we should init download or not Dvar::Register("mod_force_download_server", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches)."); - }); + }, Scheduler::Pipeline::MAIN); - Scheduler::OnFrame([]() + Scheduler::Loop([] { int workingCount = 0; @@ -957,7 +957,8 @@ namespace Components ++workingCount; } } - }); + + }, Scheduler::Pipeline::MAIN); Script::OnVMShutdown([]() { diff --git a/src/Components/Modules/Dvar.cpp b/src/Components/Modules/Dvar.cpp index 99acf132..88f6928c 100644 --- a/src/Components/Modules/Dvar.cpp +++ b/src/Components/Modules/Dvar.cpp @@ -2,7 +2,6 @@ namespace Components { - Utils::Signal Dvar::RegistrationSignal; const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg"; Dvar::Var::Var(const std::string& dvarName) : Var() @@ -201,11 +200,6 @@ namespace Components return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag.val, description); } - void Dvar::OnInit(Utils::Slot callback) - { - Dvar::RegistrationSignal.connect(callback); - } - void Dvar::ResetDvarsValue() { if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath)) @@ -216,16 +210,13 @@ namespace Components Utils::IO::RemoveFile(Dvar::ArchiveDvarPath); } - Game::dvar_t* Dvar::RegisterName(const char* name, const char* /*default*/, Game::dvar_flag flag, const char* description) + Game::dvar_t* Dvar::Dvar_RegisterName(const char* name, const char* /*default*/, unsigned __int16 flags, const char* description) { - // Run callbacks - Dvar::RegistrationSignal(); - // Name watcher - Scheduler::OnFrame([]() + Scheduler::Loop([] { static std::string lastValidName = "Unknown Soldier"; - std::string name = Dvar::Var("name").get(); + auto name = Dvar::Var("name").get(); // Don't perform any checks if name didn't change if (name == lastValidName) return; @@ -241,7 +232,7 @@ namespace Components lastValidName = name; Friends::UpdateName(); } - }, true); + }, Scheduler::MAIN, 3s); // Don't need to do this every frame std::string username = "Unknown Soldier"; @@ -255,7 +246,7 @@ namespace Components } } - return Dvar::Register(name, username.data(), Dvar::Flag(flag | Game::dvar_flag::DVAR_ARCHIVE).val, description).get(); + return Dvar::Register(name, username.data(), flags | Game::dvar_flag::DVAR_ARCHIVE, description).get(); } void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string) @@ -272,7 +263,7 @@ namespace Components "ui_mptype", }; - for (int i = 0; i < ARRAYSIZE(exceptions); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(exceptions); ++i) { if (Utils::String::ToLower(dvarName) == Utils::String::ToLower(exceptions[i])) { @@ -352,7 +343,7 @@ namespace Components Utils::Hook::Xor(0x6312DE, Game::dvar_flag::DVAR_CHEAT); // Hook dvar 'name' registration - Utils::Hook(0x40531C, Dvar::RegisterName, HOOK_CALL).install()->quick(); + Utils::Hook(0x40531C, Dvar::Dvar_RegisterName, HOOK_CALL).install()->quick(); // un-cheat safeArea_* and add archive flags Utils::Hook::Xor(0x42E3F5, Game::dvar_flag::DVAR_READONLY | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_horizontal @@ -385,15 +376,11 @@ namespace Components Utils::Hook(0x59386A, Dvar::DvarSetFromStringByNameStub, HOOK_CALL).install()->quick(); // If the game closed abruptly, the dvars would not have been restored - Dvar::OnInit([] - { - Dvar::ResetDvarsValue(); - }); + Scheduler::Once(Dvar::ResetDvarsValue, Scheduler::Pipeline::MAIN); } Dvar::~Dvar() { - Dvar::RegistrationSignal.clear(); Utils::IO::RemoveFile(Dvar::ArchiveDvarPath); } } diff --git a/src/Components/Modules/Dvar.hpp b/src/Components/Modules/Dvar.hpp index c140ac73..3a184db0 100644 --- a/src/Components/Modules/Dvar.hpp +++ b/src/Components/Modules/Dvar.hpp @@ -8,8 +8,8 @@ namespace Components class Flag { public: - Flag(Game::dvar_flag flag) : val(flag) {}; - Flag(unsigned __int16 flag) : Flag(static_cast(flag)) {}; + Flag(Game::dvar_flag flag) : val(flag) {} + Flag(unsigned __int16 flag) : Flag(static_cast(flag)) {} Game::dvar_flag val; }; @@ -17,10 +17,10 @@ namespace Components class Var { public: - Var() : dvar(nullptr) {}; - Var(const Var& obj) { this->dvar = obj.dvar; }; - Var(Game::dvar_t* _dvar) : dvar(_dvar) {}; - Var(DWORD ppdvar) : Var(*reinterpret_cast(ppdvar)) {}; + Var() : dvar(nullptr) {} + Var(const Var& obj) { this->dvar = obj.dvar; } + Var(Game::dvar_t* _dvar) : dvar(_dvar) {} + Var(DWORD ppdvar) : Var(*reinterpret_cast(ppdvar)) {} Var(const std::string& dvarName); template T get(); @@ -43,8 +43,6 @@ namespace Components Dvar(); ~Dvar(); - static void OnInit(Utils::Slot callback); - // Only strings and bools use this type of declaration template static Var Register(const char* dvarName, T value, Flag flag, const char* description); template static Var Register(const char* dvarName, T value, T min, T max, Flag flag, const char* description); @@ -52,10 +50,9 @@ namespace Components static void ResetDvarsValue(); private: - static Utils::Signal RegistrationSignal; static const char* ArchiveDvarPath; - static Game::dvar_t* RegisterName(const char* name, const char* defaultVal, Game::dvar_flag flag, const char* description); + static Game::dvar_t* Dvar_RegisterName(const char* name, const char* defaultVal, unsigned __int16 flags, const char* description); static void SetFromStringByNameExternal(const char* dvar, const char* value); static void SetFromStringByNameSafeExternal(const char* dvar, const char* value); diff --git a/src/Components/Modules/Elevators.cpp b/src/Components/Modules/Elevators.cpp index 6507f1f2..dfd09a33 100644 --- a/src/Components/Modules/Elevators.cpp +++ b/src/Components/Modules/Elevators.cpp @@ -97,7 +97,7 @@ namespace Components Elevators::Elevators() { - Dvar::OnInit([] + Scheduler::Once([] { static const char* values[] = { @@ -109,7 +109,7 @@ namespace Components Elevators::BG_Elevators = Game::Dvar_RegisterEnum("bg_elevators", values, Elevators::ENABLED, Game::DVAR_CODINFO, "Elevators glitch settings"); - }); + }, Scheduler::Pipeline::MAIN); Utils::Hook(0x57369E, Elevators::PM_CorrectAllSolidStub, HOOK_CALL).install()->quick(); // PM_GroundTrace diff --git a/src/Components/Modules/FastFiles.cpp b/src/Components/Modules/FastFiles.cpp index 963b1777..29bc4c66 100644 --- a/src/Components/Modules/FastFiles.cpp +++ b/src/Components/Modules/FastFiles.cpp @@ -569,28 +569,28 @@ namespace Components FastFiles::AddZonePath("zone\\patch\\"); FastFiles::AddZonePath("zone\\dlc\\"); - Scheduler::OnFrame([]() + Scheduler::Loop([]() { if (FastFiles::Current().empty() || !Dvar::Var("ui_zoneDebug").get()) return; - Game::Font_s* font = Game::R_RegisterFont("fonts/consoleFont", 0); - float color[4] = { 1.0f, 1.0f, 1.0f, (Game::CL_IsCgameInitialized() ? 0.3f : 1.0f) }; + auto* const font = Game::R_RegisterFont("fonts/consoleFont", 0); + float color[4] = {1.0f, 1.0f, 1.0f, (Game::CL_IsCgameInitialized() ? 0.3f : 1.0f)}; - std::uint32_t FFTotalSize = *reinterpret_cast(0x10AA5D8); - std::uint32_t FFCurrentOffset = *reinterpret_cast(0x10AA608); + auto FFTotalSize = *reinterpret_cast(0x10AA5D8); + auto FFCurrentOffset = *reinterpret_cast(0x10AA608); float fastfileLoadProgress = (float(FFCurrentOffset) / float(FFTotalSize)) * 100.0f; - if (fastfileLoadProgress == INFINITY) + if (std::isinf(fastfileLoadProgress)) { fastfileLoadProgress = 100.0f; } - else if (fastfileLoadProgress == NAN) + else if (std::isnan(fastfileLoadProgress)) { fastfileLoadProgress = 0.0f; } Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s [%0.1f%%]", FastFiles::Current().data(), fastfileLoadProgress), 0x7FFFFFFF, font, 5.0f, static_cast(Renderer::Height() - 5), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); - }, true); + }, Scheduler::Pipeline::RENDERER); Command::Add("loadzone", [](Command::Params* params) { diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index b64fd46e..fc642019 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -583,12 +583,9 @@ namespace Components if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled()) return; - Dvar::OnInit([] - { - Friends::UIStreamFriendly = Dvar::Register("ui_streamFriendly", false, Game::DVAR_ARCHIVE, "Stream friendly UI"); - Friends::CLAnonymous = Dvar::Register("cl_anonymous", false, Game::DVAR_ARCHIVE, "Enable invisible mode for Steam"); - Friends::CLNotifyFriendState = Dvar::Register("cl_notifyFriendState", true, Game::DVAR_ARCHIVE, "Update friends about current game status"); - }); + Friends::UIStreamFriendly = Dvar::Register("ui_streamFriendly", false, Game::DVAR_ARCHIVE, "Stream friendly UI"); + Friends::CLAnonymous = Dvar::Register("cl_anonymous", false, Game::DVAR_ARCHIVE, "Enable invisible mode for Steam"); + Friends::CLNotifyFriendState = Dvar::Register("cl_notifyFriendState", true, Game::DVAR_ARCHIVE, "Update friends about current game status"); Command::Add("addFriend", [](Command::Params* params) { @@ -653,7 +650,7 @@ namespace Components } }); - Scheduler::OnFrame([]() + Scheduler::Loop([] { static Utils::Time::Interval timeInterval; static Utils::Time::Interval sortInterval; @@ -692,11 +689,11 @@ namespace Components Friends::SortList(true); } } - }); + }, Scheduler::Pipeline::CLIENT); UIFeeder::Add(61.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend); - Scheduler::OnShutdown([]() + Scheduler::OnGameShutdown([] { Friends::ClearPresence("iw4x_server"); Friends::ClearPresence("iw4x_playing"); @@ -714,7 +711,7 @@ namespace Components } }); - Scheduler::Once([]() + Scheduler::OnGameInitialized([] { if (Steam::Proxy::SteamFriends) { diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 605a5510..080b2746 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -245,7 +245,7 @@ namespace Components Logger::PipeOutput(nullptr); - Scheduler::OnFrame(Logger::Frame); + Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::MAIN); Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick(); Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick(); @@ -255,7 +255,7 @@ namespace Components Utils::Hook(Game::Com_Printf, Logger::PrintStub, HOOK_JUMP).install()->quick(); } - Dvar::OnInit([]() + Scheduler::Once([] { Command::AddSV("log_add", [](Command::Params* params) { @@ -358,7 +358,7 @@ namespace Components Logger::Print("#%03d: %5s\n", i, Logger::LoggingAddresses[1][i].getCString()); } }); - }); + }, Scheduler::Pipeline::MAIN); } Logger::~Logger() diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 1b82de10..6ba13f3c 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -767,7 +767,7 @@ namespace Components Maps::Maps() { - Dvar::OnInit([]() + Scheduler::Once([] { Dvar::Register("isDlcInstalled_All", false, Game::DVAR_EXTERNAL | Game::DVAR_WRITEPROTECTED, ""); Dvar::Register("r_listSModels", false, Game::DVAR_NONE, "Display a list of visible SModels"); @@ -799,7 +799,7 @@ namespace Components Game::ShowMessageBox(Utils::String::VA("DLC %d does not exist!", dlc), "ERROR"); }); - }); + }, Scheduler::Pipeline::MAIN); // disable turrets on CoD:OL 448+ maps for now Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); @@ -879,11 +879,11 @@ namespace Components Command::Add("delayReconnect", [](Command::Params*) { - Scheduler::OnDelay([]() + Scheduler::Once([] { Command::Execute("closemenu popup_reconnectingtoparty", false); Command::Execute("reconnect", false); - }, 10s, true); + }, Scheduler::Pipeline::CLIENT, 10s); }); if(Dedicated::IsEnabled()) @@ -908,9 +908,9 @@ namespace Components // Allow hiding specific smodels Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick(); - Scheduler::OnFrame([]() + Scheduler::Loop([] { - Game::GfxWorld*& gameWorld = *reinterpret_cast(0x66DEE94); + auto*& gameWorld = *reinterpret_cast(0x66DEE94); if (!Game::CL_IsCgameInitialized() || !gameWorld || !Dvar::Var("r_listSModels").get()) return; std::map models; @@ -926,16 +926,16 @@ namespace Components } Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0); - int height = Game::R_TextHeight(font); - float scale = 0.75; - float color[4] = { 0, 1.0f, 0, 1.0f }; + auto height = Game::R_TextHeight(font); + auto scale = 0.75f; + float color[4] = {0.0f, 1.0f, 0.0f, 1.0f}; unsigned int i = 0; for (auto& model : models) { Game::R_AddCmdDrawText(Utils::String::VA("%d %s", model.second, model.first.data()), 0x7FFFFFFF, font, 15.0f, (height * scale + 1) * (i++ + 1) + 15.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); } - }, true); + }, Scheduler::Pipeline::RENDERER); } Maps::~Maps() diff --git a/src/Components/Modules/Movement.cpp b/src/Components/Modules/Movement.cpp index 806899c1..fe782c4b 100644 --- a/src/Components/Modules/Movement.cpp +++ b/src/Components/Modules/Movement.cpp @@ -219,7 +219,7 @@ namespace Components Movement::Movement() { - Dvar::OnInit([] + Scheduler::Once([] { static const char* bg_bouncesValues[] = { @@ -260,7 +260,7 @@ namespace Components Movement::BGPlayerCollision = Dvar::Register("bg_playerCollision", true, Game::DVAR_CODINFO, "Push intersecting players away from each other"); - }); + }, Scheduler::Pipeline::MAIN); // Hook Dvar_RegisterFloat. Only thing that's changed is that the 0x80 flag is not used. Utils::Hook(0x448990, Movement::Dvar_RegisterSpectateSpeedScale, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Node.cpp b/src/Components/Modules/Node.cpp index 6f112469..c76e3cd3 100644 --- a/src/Components/Modules/Node.cpp +++ b/src/Components/Modules/Node.cpp @@ -289,14 +289,14 @@ namespace Components // need to keep the message size below 1404 bytes else recipient will just drop it std::vector nodeListReponseMessages; - for (size_t curNode = 0; curNode < Node::Nodes.size();) + for (std::size_t curNode = 0; curNode < Node::Nodes.size();) { Proto::Node::List list; list.set_isnode(Dedicated::IsEnabled()); list.set_protocol(PROTOCOL); list.set_port(Node::GetPort()); - for (size_t i = 0; i < NODE_MAX_NODES_TO_SEND;) + for (std::size_t i = 0; i < NODE_MAX_NODES_TO_SEND;) { if (curNode >= Node::Nodes.size()) break; @@ -317,14 +317,14 @@ namespace Components nodeListReponseMessages.push_back(list.SerializeAsString()); } - size_t i = 0; - for (auto& nodeListData : nodeListReponseMessages) + auto i = 0; + for (const auto& nodeListData : nodeListReponseMessages) { - Scheduler::OnDelay([nodeListData, i, address]() + Scheduler::Once([&] { NODE_LOG("Sending %d nodeListResponse length to %s\n", nodeListData.length(), address.getCString()); Session::Send(address, "nodeListResponse", nodeListData); - }, NODE_SEND_RATE * i++); + }, Scheduler::Pipeline::MAIN, NODE_SEND_RATE * i++); } } @@ -339,12 +339,13 @@ namespace Components if (ZoneBuilder::IsEnabled()) return; Dvar::Register("net_natFix", false, 0, "Fix node registration for certain firewalls/routers"); - Scheduler::OnFrameAsync([]() + Scheduler::Loop([] { Node::StoreNodes(false); - }); + }, Scheduler::Pipeline::ASYNC); + + Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::MAIN); - Scheduler::OnFrame(Node::RunFrame); Session::Handle("nodeListResponse", Node::HandleResponse); Session::Handle("nodeListRequest", [](Network::Address address, const std::string&) { @@ -359,11 +360,11 @@ namespace Components }; if (Monitor::IsEnabled()) Network::OnStart(loadNodes); - else Dvar::OnInit(loadNodes); + else Scheduler::OnGameInitialized(loadNodes, Scheduler::Pipeline::MAIN); - Network::OnStart([]() + Network::OnStart([] { - std::thread([]() + std::thread([] { Node::LoadNodeRemotePreset(); }).detach(); diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 0cec9c5c..b76a8704 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -286,7 +286,7 @@ namespace Components Party::Connect(Party::Container.target); }); - Scheduler::OnFrame([]() + Scheduler::Loop([] { if (Party::Container.valid) { @@ -305,7 +305,8 @@ namespace Components Party::ConnectError("Playlist request timed out."); } } - }, true); + + }, Scheduler::Pipeline::CLIENT); // Basic info handler Network::OnPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 0f2f3f1f..4da9797f 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -264,7 +264,7 @@ namespace Components Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick(); // Make sure preDestroy is called when the game shuts down - Scheduler::OnShutdown(Loader::PreDestroy); + Scheduler::OnGameShutdown(Loader::PreDestroy); // protocol version (workaround for hacks) Utils::Hook::Set(0x4FB501, PROTOCOL); @@ -481,10 +481,10 @@ namespace Components // Fix mouse lag Utils::Hook::Nop(0x4731F5, 8); - Scheduler::OnFrame([]() + Scheduler::Loop([] { SetThreadExecutionState(ES_DISPLAY_REQUIRED); - }); + }, Scheduler::Pipeline::RENDERER); // Fix mouse pitch adjustments Dvar::Register("ui_mousePitch", false, Game::DVAR_ARCHIVE, ""); diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index 80610061..c3aeab3c 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -76,11 +76,11 @@ namespace Components RCon::BackdoorContainer.timestamp = 0; - Dvar::OnInit([]() + Scheduler::Once([] { RCon::RconPassword = Dvar::Register("rcon_password", "", Game::dvar_flag::DVAR_NONE, "The password for rcon"); RCon::RconLogRequests = Dvar::Register("rcon_log_requests", false, Game::dvar_flag::DVAR_NONE, "Print remote commands in the output log"); - }); + }, Scheduler::Pipeline::MAIN); Network::OnPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { diff --git a/src/Components/Modules/RawMouse.cpp b/src/Components/Modules/RawMouse.cpp index 51cba280..2f928d7c 100644 --- a/src/Components/Modules/RawMouse.cpp +++ b/src/Components/Modules/RawMouse.cpp @@ -151,10 +151,7 @@ namespace Components Utils::Hook(0x467C03, RawMouse::IN_Init, HOOK_CALL).install()->quick(); Utils::Hook(0x64D095, RawMouse::IN_Init, HOOK_JUMP).install()->quick(); - Dvar::OnInit([]() - { - RawMouse::M_RawInput = Dvar::Register("m_rawinput", true, Game::dvar_flag::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled."); - }); + RawMouse::M_RawInput = Dvar::Register("m_rawinput", true, Game::dvar_flag::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled."); Window::OnWndMessage(WM_INPUT, RawMouse::OnRawInput); Window::OnCreate(RawMouse::IN_RawMouse_Init); diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index 7eaaabd7..8693d474 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -5,8 +5,8 @@ namespace Components Utils::Signal Renderer::BackendFrameSignal; Utils::Signal Renderer::SingleBackendFrameSignal; - Utils::Signal Renderer::EndRecoverDeviceSignal; - Utils::Signal Renderer::BeginRecoverDeviceSignal; + Utils::Signal Renderer::EndRecoverDeviceSignal; + Utils::Signal Renderer::BeginRecoverDeviceSignal; Dvar::Var Renderer::r_drawTriggers; Dvar::Var Renderer::r_drawSceneModelCollisions; @@ -32,19 +32,6 @@ namespace Components float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f }; float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; - __declspec(naked) void Renderer::FrameStub() - { - __asm - { - pushad - call Scheduler::FrameHandler - popad - - push 5AC950h - retn - } - } - __declspec(naked) void Renderer::BackendFrameStub() { __asm @@ -87,12 +74,12 @@ namespace Components Renderer::BackendFrameSignal.connect(callback); } - void Renderer::OnDeviceRecoveryEnd(Utils::Slot callback) + void Renderer::OnDeviceRecoveryEnd(Utils::Slot callback) { Renderer::EndRecoverDeviceSignal.connect(callback); } - void Renderer::OnDeviceRecoveryBegin(Utils::Slot callback) + void Renderer::OnDeviceRecoveryBegin(Utils::Slot callback) { Renderer::BeginRecoverDeviceSignal.connect(callback); } @@ -463,7 +450,8 @@ namespace Components { if (Dedicated::IsEnabled()) return; - Scheduler::OnFrame([]() { + Scheduler::Loop([] + { if (Game::CL_IsCgameInitialized()) { DebugDrawAABBTrees(); @@ -472,35 +460,7 @@ namespace Components DebugDrawSceneModelCollisions(); DebugDrawTriggers(); } - }); - - // Renderer::OnBackendFrame([] (IDirect3DDevice9* device) - // { - // if (Game::Sys_Milliseconds() % 2) - // { - // device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0); - // } - // - // return; - // - // IDirect3DSurface9* buffer = nullptr; - // - // device->CreateOffscreenPlainSurface(Renderer::Width(), Renderer::Height(), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &buffer, nullptr); - // device->GetFrontBufferData(0, buffer); - // - // if (buffer) - // { - // D3DSURFACE_DESC desc; - // D3DLOCKED_RECT lockedRect; - // - // buffer->GetDesc(&desc); - // - // HRESULT res = buffer->LockRect(&lockedRect, NULL, D3DLOCK_READONLY); - // - // - // buffer->UnlockRect(); - // } - // }); + }, Scheduler::Pipeline::RENDERER); // Log broken materials Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick(); @@ -510,9 +470,6 @@ namespace Components Utils::Hook::Set(0x005086DA, "^3solid^7"); Utils::Hook(0x00580F53, Renderer::DrawTechsetForMaterial, HOOK_CALL).install()->quick(); - // Frame hook - Utils::Hook(0x5ACB99, Renderer::FrameStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick(); // Begin device recovery (not D3D9Ex) @@ -535,7 +492,7 @@ namespace Components // End vid_restart Utils::Hook(0x4CA3A7, Renderer::PostVidRestartStub, HOOK_CALL).install()->quick(); - Dvar::OnInit([] + Scheduler::Once([] { static const char* values[] = { @@ -552,7 +509,7 @@ namespace Components Renderer::r_drawModelNames = Game::Dvar_RegisterEnum("r_drawModelNames", values, 0, Game::DVAR_CHEAT, "Draw all model names"); Renderer::r_drawAABBTrees = Game::Dvar_RegisterBool("r_drawAabbTrees", false, Game::DVAR_CHEAT, "Draw aabb trees"); Renderer::r_playerDrawDebugDistance = Game::Dvar_RegisterInt("r_drawDebugDistance", 1000, 0, 50000, Game::DVAR_ARCHIVE, "r_draw debug functions draw distance, relative to the player"); - }); + }, Scheduler::Pipeline::MAIN); } Renderer::~Renderer() diff --git a/src/Components/Modules/Renderer.hpp b/src/Components/Modules/Renderer.hpp index 9e43a154..11c799cd 100644 --- a/src/Components/Modules/Renderer.hpp +++ b/src/Components/Modules/Renderer.hpp @@ -6,6 +6,7 @@ namespace Components { public: typedef void(BackendCallback)(IDirect3DDevice9*); + typedef void(Callback)(); Renderer(); ~Renderer(); @@ -15,12 +16,11 @@ namespace Components static void OnBackendFrame(Utils::Slot callback); static void OnNextBackendFrame(Utils::Slot callback); - static void OnDeviceRecoveryEnd(Utils::Slot callback); - static void OnDeviceRecoveryBegin(Utils::Slot callback); + + static void OnDeviceRecoveryEnd(Utils::Slot callback); + static void OnDeviceRecoveryBegin(Utils::Slot callback); private: - static void FrameStub(); - static void BackendFrameStub(); static void BackendFrameHandler(); @@ -40,8 +40,8 @@ namespace Components static void DebugDrawModelNames(); static void DebugDrawAABBTrees(); - static Utils::Signal EndRecoverDeviceSignal; - static Utils::Signal BeginRecoverDeviceSignal; + static Utils::Signal EndRecoverDeviceSignal; + static Utils::Signal BeginRecoverDeviceSignal; static Utils::Signal BackendFrameSignal; static Utils::Signal SingleBackendFrameSignal; diff --git a/src/Components/Modules/Scheduler.cpp b/src/Components/Modules/Scheduler.cpp index 22b412c3..858970a4 100644 --- a/src/Components/Modules/Scheduler.cpp +++ b/src/Components/Modules/Scheduler.cpp @@ -1,162 +1,193 @@ #include +constexpr bool COND_CONTINUE = false; +constexpr bool COND_END = true; + namespace Components { - bool Scheduler::AsyncTerminate; - std::thread Scheduler::AsyncThread; + std::thread Scheduler::Thread; + volatile bool Scheduler::Kill = false; + Scheduler::TaskPipeline Scheduler::Pipelines[Pipeline::COUNT]; - bool Scheduler::ReadyPassed = false; - Utils::Signal Scheduler::ReadySignal; - Utils::Signal Scheduler::ShutdownSignal; - - Utils::Signal Scheduler::FrameSignal; - Utils::Signal Scheduler::FrameOnceSignal; - std::vector Scheduler::DelayedSlots; - - Utils::Signal Scheduler::AsyncFrameSignal; - Utils::Signal Scheduler::AsyncFrameOnceSignal; - - void Scheduler::Once(Utils::Slot callback, bool clientOnly) + void Scheduler::TaskPipeline::add(Task&& task) { - if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return; - Scheduler::FrameOnceSignal.connect(callback); - } - - void Scheduler::OnShutdown(Utils::Slot callback) - { - Scheduler::ShutdownSignal.connect(callback); - } - - void Scheduler::OnFrame(Utils::Slot callback, bool clientOnly) - { - if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return; - Scheduler::FrameSignal.connect(callback); - } - - void Scheduler::OnReady(Utils::Slot callback, bool clientOnly) - { - if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return; - if (Scheduler::ReadyPassed) Scheduler::Once(callback); - else Scheduler::ReadySignal.connect(callback); - } - - void Scheduler::ReadyHandler() - { - if (!FastFiles::Ready()) + newCallbacks_.access([&task](taskList& tasks) { - Scheduler::Once(Scheduler::ReadyHandler); - } - else + tasks.emplace_back(std::move(task)); + }); + } + + void Scheduler::TaskPipeline::execute() + { + callbacks_.access([&](taskList& tasks) { - Scheduler::ReadyPassed = true; - Scheduler::ReadySignal(); - Scheduler::ReadySignal.clear(); - } - } + this->mergeCallbacks(); - void Scheduler::FrameHandler() - { - Scheduler::DelaySignal(); - Scheduler::FrameSignal(); - - Utils::Signal copy(Scheduler::FrameOnceSignal); - Scheduler::FrameOnceSignal.clear(); - copy(); - } - - void Scheduler::OnDelay(Utils::Slot callback, std::chrono::nanoseconds delay, bool clientOnly) - { - if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return; - - Scheduler::DelayedSlot slot; - slot.callback = callback; - slot.delay = delay; - - Scheduler::DelayedSlots.push_back(slot); - } - - void Scheduler::DelaySignal() - { - Utils::Signal signal; - - for (auto i = Scheduler::DelayedSlots.begin(); i != Scheduler::DelayedSlots.end();) - { - if (i->interval.elapsed(i->delay)) + for (auto i = tasks.begin(); i != tasks.end();) { - signal.connect(i->callback); - i = Scheduler::DelayedSlots.erase(i); - continue; + const auto now = std::chrono::high_resolution_clock::now(); + const auto diff = now - i->lastCall; + + if (diff < i->interval) + { + ++i; + continue; + } + + i->lastCall = now; + + const auto res = i->handler(); + if (res == COND_END) + { + i = tasks.erase(i); + } + else + { + ++i; + } + } + }); + } + + void Scheduler::TaskPipeline::mergeCallbacks() + { + callbacks_.access([&](taskList& tasks) + { + newCallbacks_.access([&](taskList& new_tasks) + { + tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), std::move_iterator(new_tasks.end())); + new_tasks = {}; + }); + }); + } + + void Scheduler::Execute(Pipeline type) + { + assert(type < Pipeline::COUNT); + Pipelines[type].execute(); + } + + void Scheduler::REndFrame_Hk() + { + Utils::Hook::Call(0x50AB20)(); + Execute(Pipeline::RENDERER); + } + + void Scheduler::ServerFrame_Hk() + { + Utils::Hook::Call(0x471C50)(); + Execute(Pipeline::SERVER); + } + + void Scheduler::ClientFrame_Hk(const int localClientNum) + { + Utils::Hook::Call(0x5A8E80)(localClientNum); + Execute(Pipeline::CLIENT); + } + + void Scheduler::MainFrame_Hk() + { + Utils::Hook::Call(0x47DCA0)(); + Execute(Pipeline::MAIN); + } + + void Scheduler::SysSetBlockSystemHotkeys_Hk(int block) + { + Execute(Pipeline::QUIT); + Utils::Hook::Call(0x46B370)(block); + } + + void Scheduler::Schedule(const std::function& callback, const Pipeline type, + const std::chrono::milliseconds delay) + { + assert(type < Pipeline::COUNT); + + Task task; + task.handler = callback; + task.interval = delay; + task.lastCall = std::chrono::high_resolution_clock::now(); + + Pipelines[type].add(std::move(task)); + } + + void Scheduler::Loop(const std::function& callback, const Pipeline type, + const std::chrono::milliseconds delay) + { + Schedule([callback] + { + callback(); + return COND_CONTINUE; + }, type, delay); + } + + void Scheduler::Once(const std::function& callback, const Pipeline type, + const std::chrono::milliseconds delay) + { + Schedule([callback] + { + callback(); + return COND_END; + }, type, delay); + } + + void Scheduler::OnGameInitialized(const std::function& callback, const Pipeline type, + const std::chrono::milliseconds delay) + { + Schedule([=] + { + if (Game::Sys_IsDatabaseReady2()) + { + Once(callback, type, delay); + return COND_END; } - ++i; - } - - signal(); + return COND_CONTINUE; + }, Pipeline::MAIN); // Once Com_Frame_Try_Block_Function is called we know the game is 'ready' } - void Scheduler::ShutdownStub(int num) + void Scheduler::OnGameShutdown(const std::function& callback) { - Scheduler::ShutdownSignal(); - Utils::Hook::Call(0x46B370)(num); - } - - void Scheduler::OnFrameAsync(Utils::Slot callback) - { - Scheduler::AsyncFrameSignal.connect(callback); - } - - void Scheduler::OnceAsync(Utils::Slot callback) - { - Scheduler::AsyncFrameOnceSignal.connect(callback); + Schedule([callback] + { + callback(); + return COND_END; + }, Pipeline::QUIT, 0ms); } Scheduler::Scheduler() { - Scheduler::ReadyPassed = false; - Scheduler::Once(Scheduler::ReadyHandler); - - Utils::Hook(0x4D697A, Scheduler::ShutdownStub, HOOK_CALL).install()->quick(); - - if (!Loader::IsPerformingUnitTests()) + Thread = Utils::Thread::createNamedThread("Async Scheduler", [] { - Scheduler::AsyncTerminate = false; - Scheduler::AsyncThread = std::thread([]() + while (!Kill) { - while (!Scheduler::AsyncTerminate) - { - Scheduler::AsyncFrameSignal(); + Execute(Pipeline::ASYNC); + std::this_thread::sleep_for(10ms); + } + }); - Utils::Signal copy(Scheduler::AsyncFrameOnceSignal); - Scheduler::AsyncFrameOnceSignal.clear(); - copy(); + Utils::Hook(0x4DBE9A, REndFrame_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x518D5C, REndFrame_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x5ACBA3, REndFrame_Hk, HOOK_CALL).install()->quick(); - std::this_thread::sleep_for(16ms); - } - }); - } - } + // Hook G_Glass_Update so we may fix TLS issues + Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick(); - Scheduler::~Scheduler() - { - Scheduler::ReadySignal.clear(); - Scheduler::ShutdownSignal.clear(); + // CL_CheckTimeout + Utils::Hook(0x4B0F81, ClientFrame_Hk, HOOK_CALL).install()->quick(); - Scheduler::FrameSignal.clear(); - Scheduler::FrameOnceSignal.clear(); - Scheduler::DelayedSlots.clear(); + // Com_Frame_Try_Block_Function + Utils::Hook(0x4B724F, MainFrame_Hk, HOOK_CALL).install()->quick(); - Scheduler::AsyncFrameSignal.clear(); - Scheduler::AsyncFrameOnceSignal.clear(); - - Scheduler::ReadyPassed = false; + // Sys_Quit + Utils::Hook(0x4D697A, SysSetBlockSystemHotkeys_Hk, HOOK_CALL).install()->quick(); } void Scheduler::preDestroy() { - Scheduler::AsyncTerminate = true; - if (Scheduler::AsyncThread.joinable()) + Kill = true; + if (Thread.joinable()) { - Scheduler::AsyncThread.join(); + Thread.join(); } } } diff --git a/src/Components/Modules/Scheduler.hpp b/src/Components/Modules/Scheduler.hpp index 7b187b89..8a85fd26 100644 --- a/src/Components/Modules/Scheduler.hpp +++ b/src/Components/Modules/Scheduler.hpp @@ -5,50 +5,64 @@ namespace Components class Scheduler : public Component { public: - typedef void(Callback)(); + enum Pipeline + { + ASYNC, + RENDERER, + SERVER, + CLIENT, + MAIN, + QUIT, + COUNT, + }; Scheduler(); - ~Scheduler(); void preDestroy() override; - static void OnShutdown(Utils::Slot callback); - static void OnFrame(Utils::Slot callback, bool clientOnly = false); - static void OnReady(Utils::Slot callback, bool clientOnly = false); - static void Once(Utils::Slot callback, bool clientOnly = false); - static void OnDelay(Utils::Slot callback, std::chrono::nanoseconds delay, bool clientOnly = false); - - static void OnFrameAsync(Utils::Slot callback); - static void OnceAsync(Utils::Slot callback); - - static void FrameHandler(); + static void Schedule(const std::function& callback, Pipeline type = Pipeline::ASYNC, + std::chrono::milliseconds delay = 0ms); + static void Loop(const std::function& callback, Pipeline type = Pipeline::ASYNC, + std::chrono::milliseconds delay = 0ms); + static void Once(const std::function& callback, Pipeline type = Pipeline::ASYNC, + std::chrono::milliseconds delay = 0ms); + static void OnGameInitialized(const std::function& callback, Pipeline type = Pipeline::ASYNC, + std::chrono::milliseconds delay = 0ms); + static void OnGameShutdown(const std::function& callback); private: - class DelayedSlot + struct Task { - public: - std::chrono::nanoseconds delay; - Utils::Time::Interval interval; - Utils::Slot callback; + std::function handler{}; + std::chrono::milliseconds interval{}; + std::chrono::high_resolution_clock::time_point lastCall{}; }; - static bool AsyncTerminate; - static std::thread AsyncThread; + using taskList = std::vector; - static Utils::Signal FrameSignal; - static Utils::Signal FrameOnceSignal; - static std::vector DelayedSlots; + class TaskPipeline + { + public: + void add(Task&& task); + void execute(); - static bool ReadyPassed; - static Utils::Signal ReadySignal; - static Utils::Signal ShutdownSignal; + private: + Utils::Concurrency::Container newCallbacks_; + Utils::Concurrency::Container callbacks_; - static Utils::Signal AsyncFrameSignal; - static Utils::Signal AsyncFrameOnceSignal; + void mergeCallbacks(); + }; - static void ReadyHandler(); - static void DelaySignal(); + static volatile bool Kill; + static std::thread Thread; + static TaskPipeline Pipelines[]; - static void ShutdownStub(int num); + static void Execute(Pipeline type); + + static void REndFrame_Hk(); + static void ServerFrame_Hk(); + static void ClientFrame_Hk(int localClientNum); + static void MainFrame_Hk(); + static void SysSetBlockSystemHotkeys_Hk(int block); }; } diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 9f686cfd..79007579 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -14,7 +14,7 @@ namespace Components const char* Script::ReplacedPos = nullptr; int Script::LastFrameTime = -1; - Utils::Signal Script::VMShutdownSignal; + Utils::Signal Script::VMShutdownSignal; void Script::FunctionError() { @@ -411,7 +411,7 @@ namespace Components } } - void Script::OnVMShutdown(Utils::Slot callback) + void Script::OnVMShutdown(Utils::Slot callback) { Script::ScriptBaseProgramNum.clear(); Script::VMShutdownSignal.connect(std::move(callback)); @@ -718,7 +718,7 @@ namespace Components Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_LoadGame Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_ShutdownGame - Scheduler::OnFrame([]() + Scheduler::Loop([]() { if (!Game::SV_Loaded()) return; @@ -735,7 +735,7 @@ namespace Components } Script::LastFrameTime = nowMs; - }); + }, Scheduler::Pipeline::SERVER); #ifdef _DEBUG Script::AddFunction("DebugBox", [] diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/Script.hpp index 2885e53e..893fd726 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/Script.hpp @@ -9,12 +9,14 @@ namespace Components Script(); ~Script(); + typedef void(Callback)(); + static int LoadScriptAndLabel(const std::string& script, const std::string& label); static void AddFunction(const char* name, Game::BuiltinFunction func, int type = 0); static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0); - static void OnVMShutdown(Utils::Slot callback); + static void OnVMShutdown(Utils::Slot callback); static Game::client_t* GetClient(const Game::gentity_t* gentity); @@ -31,7 +33,7 @@ namespace Components static const char* ReplacedPos; static int LastFrameTime; - static Utils::Signal VMShutdownSignal; + static Utils::Signal VMShutdownSignal; static void CompileError(unsigned int offset, const char* message, ...); static void PrintSourcePos(const char* filename, unsigned int offset); diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index e981169e..f235ad02 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -775,7 +775,7 @@ namespace Components ServerList::FavouriteList.clear(); ServerList::VisibleList.clear(); - Dvar::OnInit([]() + Scheduler::Once([] { ServerList::UIServerSelected = Dvar::Register("ui_serverSelected", false, Game::dvar_flag::DVAR_NONE, "Whether a server has been selected in the serverlist"); @@ -786,7 +786,7 @@ namespace Components 1, 10, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server queries per frame"); ServerList::NETServerFrames = Dvar::Register("net_serverFrames", 30, 1, 60, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server query frames per second"); - }); + }, Scheduler::Pipeline::MAIN); // Fix ui_netsource dvar Utils::Hook::Nop(0x4CDEEC, 5); // Don't reset the netsource when gametypes aren't loaded @@ -922,7 +922,7 @@ namespace Components UIScript::AddOwnerDraw(253, ServerList::UpdateGameType); // Add frame callback - Scheduler::OnFrame(ServerList::Frame); + Scheduler::Loop(ServerList::Frame, Scheduler::Pipeline::CLIENT); } ServerList::~ServerList() diff --git a/src/Components/Modules/Session.cpp b/src/Components/Modules/Session.cpp index d8642ff3..d6b175d5 100644 --- a/src/Components/Modules/Session.cpp +++ b/src/Components/Modules/Session.cpp @@ -33,11 +33,11 @@ namespace Components Network::SendCommand(target, command, data); - Scheduler::OnDelay([delayData]() + Scheduler::Once([delayData]() { Network::SendCommand(delayData->target, delayData->command, delayData->data); delete delayData; - }, 500ms + std::chrono::milliseconds(rand() % 200)); + }, Scheduler::Pipeline::MAIN, 500ms + std::chrono::milliseconds(rand() % 200)); #else std::lock_guard _(Session::Mutex); diff --git a/src/Components/Modules/StartupMessages.cpp b/src/Components/Modules/StartupMessages.cpp index 8b9fbe8e..6e8fac3d 100644 --- a/src/Components/Modules/StartupMessages.cpp +++ b/src/Components/Modules/StartupMessages.cpp @@ -7,12 +7,12 @@ namespace Components StartupMessages::StartupMessages() { - Dvar::OnInit([]() + Scheduler::Once([] { Dvar::Register("ui_startupMessage", "", Game::DVAR_EXTERNAL | Game::DVAR_WRITEPROTECTED, ""); Dvar::Register("ui_startupMessageTitle", "", Game::DVAR_EXTERNAL | Game::DVAR_WRITEPROTECTED, ""); Dvar::Register("ui_startupNextButtonText", "", Game::DVAR_EXTERNAL | Game::DVAR_WRITEPROTECTED, ""); - }); + }, Scheduler::Pipeline::MAIN); UIScript::Add("nextStartupMessage", [](UIScript::Token) { diff --git a/src/Components/Modules/Toast.cpp b/src/Components/Modules/Toast.cpp index eda8240e..669b5231 100644 --- a/src/Components/Modules/Toast.cpp +++ b/src/Components/Modules/Toast.cpp @@ -148,15 +148,14 @@ namespace Components { if (Dedicated::IsEnabled() || Monitor::IsEnabled()) return; - Scheduler::OnReady([]() - { - Scheduler::OnFrame(Toast::Handler); - }); + Scheduler::OnGameInitialized(Toast::Handler, Scheduler::Pipeline::RENDERER); - Command::Add("testtoast", [](Command::Params*) +#ifdef _DEBUG + Command::Add("testtoast", []([[maybe_unused]] Command::Params* params) { Toast::Show("cardicon_prestige10", "Test", "This is a test toast", 3000); }); +#endif } Toast::~Toast() diff --git a/src/Components/Modules/UIFeeder.cpp b/src/Components/Modules/UIFeeder.cpp index cc07d4c8..292428da 100644 --- a/src/Components/Modules/UIFeeder.cpp +++ b/src/Components/Modules/UIFeeder.cpp @@ -381,12 +381,12 @@ namespace Components { if (Dedicated::IsEnabled()) return; - Dvar::OnInit([]() + Scheduler::Once([] { Dvar::Register("ui_map_long", "Afghan", Game::dvar_flag::DVAR_NONE, ""); Dvar::Register("ui_map_name", "mp_afghan", Game::dvar_flag::DVAR_NONE, ""); Dvar::Register("ui_map_desc", "", Game::dvar_flag::DVAR_NONE, ""); - }); + }, Scheduler::Pipeline::MAIN); // Get feeder item count Utils::Hook(0x41A0D0, UIFeeder::GetItemCountStub, HOOK_JUMP).install()->quick(); diff --git a/src/Components/Modules/Window.cpp b/src/Components/Modules/Window.cpp index 04fa8f22..e725d397 100644 --- a/src/Components/Modules/Window.cpp +++ b/src/Components/Modules/Window.cpp @@ -178,7 +178,7 @@ namespace Components Utils::Hook(0x48E5D3, Window::DrawCursorStub, HOOK_CALL).install()->quick(); // Draw the cursor if necessary - Scheduler::OnFrame([]() + Scheduler::Loop([] { if (Window::NativeCursor.get() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow)) { @@ -198,7 +198,7 @@ namespace Components Window::CursorVisible = FALSE; } - }); + }, Scheduler::Pipeline::RENDERER); // Don't let the game interact with the native cursor Utils::Hook::Set(0x6D7348, Window::ShowCursorHook); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index c662fa35..b3a3a4ed 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -442,6 +442,8 @@ namespace Game ClientUserinfoChanged_t ClientUserinfoChanged = ClientUserinfoChanged_t(0x445240); + player_die_t player_die = player_die_t(0x42BC70); + XAssetHeader* DB_XAssetPool = reinterpret_cast(0x7998A8); unsigned int* g_poolSize = reinterpret_cast(0x7995E8); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index bbd58284..67d6aa39 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -888,10 +888,10 @@ namespace Game typedef void(__cdecl * Sys_FreeFileList_t)(char** list); extern Sys_FreeFileList_t Sys_FreeFileList; - typedef bool(__cdecl * Sys_IsDatabaseReady_t)(); + typedef int(__cdecl * Sys_IsDatabaseReady_t)(); extern Sys_IsDatabaseReady_t Sys_IsDatabaseReady; - typedef bool(__cdecl * Sys_IsDatabaseReady2_t)(); + typedef int(__cdecl * Sys_IsDatabaseReady2_t)(); extern Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2; typedef bool(__cdecl * Sys_IsMainThread_t)(); @@ -1053,6 +1053,9 @@ namespace Game typedef void(__cdecl * ClientUserinfoChanged_t)(int clientNum); extern ClientUserinfoChanged_t ClientUserinfoChanged; + typedef void(__cdecl * player_die_t)(gentity_s* self, const gentity_s* inflictor, gentity_s* attacker, int damage, int meansOfDeath, int iWeapon, const float* vDir, const hitLocation_t hitLoc, int psTimeOffset); + extern player_die_t player_die; + extern XAssetHeader* DB_XAssetPool; extern unsigned int* g_poolSize; diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 59fad1ad..7e06b277 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -366,7 +366,7 @@ namespace Game const char** argv[8]; }; - static_assert(sizeof(CmdArgs) == 132); + static_assert(sizeof(CmdArgs) == 0x84); typedef struct cmd_function_s { diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 4480f216..8b9d29a8 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -16,7 +16,7 @@ #define VLD_FORCE_ENABLE //#include -#include +#include #include #include #include @@ -29,7 +29,7 @@ #pragma warning(push) #pragma warning(disable: 4091) #pragma warning(disable: 4244) -#include +#include #include #include @@ -45,13 +45,14 @@ #include #include #include +#include #pragma warning(pop) #include #pragma comment(lib, "D3dx9.lib") -#include +#include #pragma comment (lib, "xinput.lib") // Ignore the warnings @@ -63,7 +64,6 @@ #pragma warning(disable: 4389) #pragma warning(disable: 4702) #pragma warning(disable: 4800) -#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS #pragma warning(disable: 5054) #pragma warning(disable: 6001) #pragma warning(disable: 6011) @@ -74,22 +74,33 @@ #pragma warning(disable: 6387) #pragma warning(disable: 26812) -#include - #include -#include +#include #include #include +#include #include +#include + +// Enable additional literals +using namespace std::literals; #ifdef max -#undef max + #undef max #endif #ifdef min -#undef min + #undef min #endif +#define AssertSize(x, size) \ + static_assert(sizeof(x) == (size), \ + "Structure has an invalid size. " #x " must be " #size " bytes") + +#define AssertOffset(x, y, offset) \ + static_assert(offsetof(x, y) == (offset), \ + #x "::" #y " is not at the right offset. Must be at " #offset) + // Protobuf #include "proto/session.pb.h" #include "proto/party.pb.h" @@ -101,28 +112,31 @@ #pragma warning(pop) -#include "Utils/IO.hpp" -#include "Utils/CSV.hpp" -#include "Utils/Time.hpp" +#include "Utils/Memory.hpp" // Breaks order on purpose + #include "Utils/Cache.hpp" #include "Utils/Chain.hpp" +#include "Utils/Compression.hpp" +#include "Utils/Concurrency.hpp" +#include "Utils/Cryptography.hpp" +#include "Utils/CSV.hpp" +#include "Utils/Entities.hpp" +#include "Utils/Hooking.hpp" +#include "Utils/InfoString.hpp" +#include "Utils/IO.hpp" +#include "Utils/Library.hpp" +#include "Utils/String.hpp" +#include "Utils/Thread.hpp" +#include "Utils/Time.hpp" #include "Utils/Utils.hpp" #include "Utils/WebIO.hpp" -#include "Utils/Memory.hpp" -#include "Utils/String.hpp" -#include "Utils/Hooking.hpp" -#include "Utils/Library.hpp" -#include "Utils/Entities.hpp" -#include "Utils/InfoString.hpp" -#include "Utils/Compression.hpp" -#include "Utils/Cryptography.hpp" -#include "Steam/Steam.hpp" +#include "Steam/Steam.hpp" // Some definitions are used in functions and structs #include "Game/Structs.hpp" #include "Game/Functions.hpp" -#include "Utils/Stream.hpp" +#include "Utils/Stream.hpp" // Breaks order on purpose #include "Components/Loader.hpp" @@ -139,20 +153,11 @@ #pragma comment(lib, "dbghelp.lib") #pragma comment(lib, "ntdll.lib") -// Enable additional literals -using namespace std::literals; - #endif -#define STRINGIZE_(x) #x -#define STRINGIZE(x) STRINGIZE_(x) - #define BASEGAME "iw4x" #define CLIENT_CONFIG "iw4x_config.cfg" -#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.") -#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.") - // Resource stuff #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS diff --git a/src/Utils/Concurrency.hpp b/src/Utils/Concurrency.hpp new file mode 100644 index 00000000..2a4e4ecf --- /dev/null +++ b/src/Utils/Concurrency.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace Utils::Concurrency +{ + template + class Container + { + public: + template + R access(F&& accessor) const + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R access(F&& accessor) + { + std::lock_guard _{mutex_}; + return accessor(object_); + } + + template + R accessWithLock(F&& accessor) const + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + template + R accessWithLock(F&& accessor) + { + std::unique_lock lock{mutex_}; + return accessor(object_, lock); + } + + T& getRaw() {return object_;} + const T& getRaw() const {return object_;} + + private: + mutable MutexType mutex_{}; + T object_{}; + }; +} diff --git a/src/Utils/Library.cpp b/src/Utils/Library.cpp index 1b10bf40..2ff31f9a 100644 --- a/src/Utils/Library.cpp +++ b/src/Utils/Library.cpp @@ -19,14 +19,14 @@ namespace Utils return Library(handle); } - Library::Library(const std::string& name, bool _freeOnDestroy) : _module(nullptr), freeOnDestroy(_freeOnDestroy) + Library::Library(const std::string& name, bool _freeOnDestroy) : module_(nullptr), freeOnDestroy(_freeOnDestroy) { - this->_module = LoadLibraryExA(name.data(), nullptr, 0); + this->module_ = LoadLibraryExA(name.data(), nullptr, 0); } Library::Library(const HMODULE handle) { - this->_module = handle; + this->module_ = handle; this->freeOnDestroy = true; } @@ -38,23 +38,38 @@ namespace Utils } } + bool Library::operator==(const Library& obj) const + { + return this->module_ == obj.module_; + } + + Library::operator bool() const + { + return this->isValid(); + } + + Library::operator HMODULE() const + { + return this->getModule(); + } + bool Library::isValid() const { - return this->_module != nullptr; + return this->module_ != nullptr; } HMODULE Library::getModule() const { - return this->_module; + return this->module_; } void Library::free() { if (this->isValid()) { - FreeLibrary(this->_module); + FreeLibrary(this->module_); } - this->_module = nullptr; + this->module_ = nullptr; } } diff --git a/src/Utils/Library.hpp b/src/Utils/Library.hpp index 3ef0db41..8c999f67 100644 --- a/src/Utils/Library.hpp +++ b/src/Utils/Library.hpp @@ -9,12 +9,18 @@ namespace Utils static Library Load(const std::filesystem::path& path); static Library GetByAddress(void* address); - Library() : _module(nullptr), freeOnDestroy(false) {}; + Library() : module_(nullptr), freeOnDestroy(false) {}; Library(const std::string& name, bool freeOnDestroy); - explicit Library(const std::string& name) : _module(GetModuleHandleA(name.data())), freeOnDestroy(true) {}; + explicit Library(const std::string& name) : module_(GetModuleHandleA(name.data())), freeOnDestroy(true) {}; explicit Library(HMODULE handle); ~Library(); + bool operator!=(const Library& obj) const { return !(*this == obj); } + bool operator==(const Library& obj) const; + + operator bool() const; + operator HMODULE() const; + bool isValid() const; HMODULE getModule() const; @@ -22,7 +28,7 @@ namespace Utils T getProc(const std::string& process) const { if (!this->isValid()) T{}; - return reinterpret_cast(GetProcAddress(this->_module, process.data())); + return reinterpret_cast(GetProcAddress(this->module_, process.data())); } template @@ -59,7 +65,7 @@ namespace Utils void free(); private: - HMODULE _module; + HMODULE module_; bool freeOnDestroy; }; } diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index fde99eb7..cc0bb48c 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -34,7 +34,7 @@ namespace Utils this->refMemory.clear(); - for (auto& data : this->pool) + for (const auto& data : this->pool) { Memory::Free(data); } diff --git a/src/Utils/Thread.cpp b/src/Utils/Thread.cpp new file mode 100644 index 00000000..e328ff30 --- /dev/null +++ b/src/Utils/Thread.cpp @@ -0,0 +1,122 @@ +#include + +namespace Utils::Thread +{ + bool setName(const HANDLE t, const std::string& name) + { + const Library kernel32("kernel32.dll"); + if (!kernel32) + { + return false; + } + + const auto setDescription = kernel32.getProc("SetThreadDescription"); + if (!setDescription) + { + return false; + } + + return SUCCEEDED(setDescription(t, String::Convert(name).data())); + } + + bool setName(const DWORD id, const std::string& name) + { + auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id); + if (!t) return false; + + const auto _ = gsl::finally([t]() + { + CloseHandle(t); + }); + + return setName(t, name); + } + + bool setName(std::thread& t, const std::string& name) + { + return setName(t.native_handle(), name); + } + + bool setName(const std::string& name) + { + return setName(GetCurrentThread(), name); + } + + std::vector getThreadIds() + { + auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId()); + if (h == INVALID_HANDLE_VALUE) + { + return {}; + } + + const auto _ = gsl::finally([h]() + { + CloseHandle(h); + }); + + THREADENTRY32 entry{}; + entry.dwSize = sizeof(entry); + if (!Thread32First(h, &entry)) + { + return {}; + } + + std::vector ids{}; + + do + { + const auto check_size = entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + + sizeof(entry.th32OwnerProcessID); + entry.dwSize = sizeof(entry); + + if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId()) + { + ids.emplace_back(entry.th32ThreadID); + } + } while (Thread32Next(h, &entry)); + + return ids; + } + + void forEachThread(const std::function& callback) + { + const auto ids = getThreadIds(); + + for (const auto& id : ids) + { + auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id); + if (thread != nullptr) + { + const auto _ = gsl::finally([thread]() + { + CloseHandle(thread); + }); + + callback(thread); + } + } + } + + void suspendOtherThreads() + { + forEachThread([](const HANDLE thread) + { + if (GetThreadId(thread) != GetCurrentThreadId()) + { + SuspendThread(thread); + } + }); + } + + void resumeOtherThreads() + { + forEachThread([](const HANDLE thread) + { + if (GetThreadId(thread) != GetCurrentThreadId()) + { + ResumeThread(thread); + } + }); + } +} diff --git a/src/Utils/Thread.hpp b/src/Utils/Thread.hpp new file mode 100644 index 00000000..807633f4 --- /dev/null +++ b/src/Utils/Thread.hpp @@ -0,0 +1,23 @@ +#pragma once + +namespace Utils::Thread +{ + bool setName(HANDLE t, const std::string& name); + bool setName(DWORD id, const std::string& name); + bool setName(std::thread& t, const std::string& name); + bool setName(const std::string& name); + + template + std::thread createNamedThread(const std::string& name, Args&&... args) + { + auto t = std::thread(std::forward(args)...); + setName(t, name); + return t; + } + + std::vector getThreadIds(); + void forEachThread(const std::function& callback); + + void suspendOtherThreads(); + void resumeOtherThreads(); +}