Attempt to refactor scheduler *gone insane*

This commit is contained in:
FutureRave 2022-05-05 15:03:14 +01:00 committed by Diavolo
parent db51c06a12
commit b0dfd16759
41 changed files with 626 additions and 400 deletions

3
.gitmodules vendored
View File

@ -32,3 +32,6 @@
[submodule "deps/dxsdk"] [submodule "deps/dxsdk"]
path = deps/dxsdk path = deps/dxsdk
url = https://github.com/devKlausS/dxsdk.git url = https://github.com/devKlausS/dxsdk.git
[submodule "deps/GSL"]
path = deps/GSL
url = https://github.com/microsoft/GSL.git

1
deps/GSL vendored Submodule

@ -0,0 +1 @@
Subproject commit da01eb28dbb75bed11a51acff0f80ecedd622573

19
deps/premake/gsl.lua vendored Normal file
View File

@ -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)

View File

@ -5,8 +5,8 @@ namespace Components
class Component class Component
{ {
public: public:
Component() {}; Component() {}
virtual ~Component() {}; virtual ~Component() {}
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) #if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
virtual std::string getName() 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. // 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. // 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. // 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 void preDestroy() {}
virtual bool unitTest() { return true; }; // Unit testing entry virtual bool unitTest() { return true; } // Unit testing entry
}; };
class Loader class Loader

View File

@ -514,18 +514,19 @@ namespace Components
if (!ZoneBuilder::IsEnabled()) Utils::Hook(0x5BB3F2, AssetHandler::MissingAssetError, HOOK_CALL).install()->quick(); if (!ZoneBuilder::IsEnabled()) Utils::Hook(0x5BB3F2, AssetHandler::MissingAssetError, HOOK_CALL).install()->quick();
// Log missing empty assets // Log missing empty assets
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
if (FastFiles::Ready() && !AssetHandler::EmptyAssets.empty()) if (FastFiles::Ready() && !AssetHandler::EmptyAssets.empty())
{ {
for (auto& asset : AssetHandler::EmptyAssets) for (auto& asset : AssetHandler::EmptyAssets)
{ {
Game::Com_PrintWarning(25, reinterpret_cast<const char*>(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data()); Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_FILES,
reinterpret_cast<const char*>(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data());
} }
AssetHandler::EmptyAssets.clear(); AssetHandler::EmptyAssets.clear();
} }
}); }, Scheduler::Pipeline::MAIN);
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*) AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*)
{ {

View File

@ -429,10 +429,13 @@ namespace Components
Auth::LoadKey(true); Auth::LoadKey(true);
Steam::SteamUser()->GetSteamID(); Steam::SteamUser()->GetSteamID();
Scheduler::OnFrame(Auth::Frame); Scheduler::Loop(Auth::Frame, Scheduler::Pipeline::MAIN);
// Register dvar // Register dvar
Dvar::Register<int>("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_SERVERINFO, "Security level for GUID certificates (POW)"); Dvar::OnInit([]
{
Dvar::Register<int>("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_SERVERINFO, "Security level for GUID certificates (POW)");
});
// Install registration hook // Install registration hook
Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick();

View File

@ -268,10 +268,10 @@ namespace Components
}); });
// Verify the list on startup // Verify the list on startup
Scheduler::Once([]() Scheduler::OnGameInitialized([]()
{ {
Bans::BanList list; Bans::BanList list;
Bans::LoadBans(&list); Bans::LoadBans(&list);
}); }, Scheduler::Pipeline::SERVER);
} }
} }

View File

@ -85,26 +85,28 @@ namespace Components
{ {
for (auto i = 0u; i < count; ++i) for (auto i = 0u; i < count; ++i)
{ {
Scheduler::OnDelay([]() Scheduler::Once([]
{ {
auto* ent = Game::SV_AddTestClient(); auto* ent = Game::SV_AddTestClient();
if (ent == nullptr) if (ent == nullptr)
return; return;
Scheduler::OnDelay([ent]() Scheduler::Once([ent]()
{ {
Game::Scr_AddString("autoassign"); Game::Scr_AddString("autoassign");
Game::Scr_AddString("team_marinesopfor"); Game::Scr_AddString("team_marinesopfor");
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2); 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(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
Game::Scr_AddString("changeclass"); Game::Scr_AddString("changeclass");
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2); Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
}, 1s); }, Scheduler::Pipeline::SERVER, 1s);
}, 1s);
}, 500ms * (i + 1)); }, Scheduler::Pipeline::SERVER, 1s);
}, Scheduler::Pipeline::SERVER, 500ms * (i + 1));
} }
} }

View File

@ -213,7 +213,7 @@ namespace Components
}); });
// Invocation handler // Invocation handler
Scheduler::OnReady(ConnectProtocol::Invocation); Scheduler::OnGameInitialized(ConnectProtocol::Invocation, Scheduler::Pipeline::CLIENT);
ConnectProtocol::InstallProtocol(); ConnectProtocol::InstallProtocol();
ConnectProtocol::EvaluateProtocol(); ConnectProtocol::EvaluateProtocol();
@ -232,10 +232,10 @@ namespace Components
// Only skip intro here, invocation will be done later. // Only skip intro here, invocation will be done later.
Utils::Hook::Set<BYTE>(0x60BECF, 0xEB); Utils::Hook::Set<BYTE>(0x60BECF, 0xEB);
Scheduler::OnDelay([]() Scheduler::Once([]()
{ {
Command::Execute("openmenu popup_reconnectingtoparty", false); Command::Execute("openmenu popup_reconnectingtoparty", false);
}, 8s); }, Scheduler::Pipeline::CLIENT, 8s);
} }
} }
} }

View File

@ -580,7 +580,7 @@ namespace Components
if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) 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! // Code below is not necessary when performing unit tests!
@ -622,10 +622,10 @@ namespace Components
} }
}, HOOK_CALL).install()->quick(); }, HOOK_CALL).install()->quick();
Scheduler::OnFrame([]() Scheduler::OnGameInitialized([]
{ {
Console::LastRefresh = Game::Sys_Milliseconds(); Console::LastRefresh = Game::Sys_Milliseconds();
}); }, Scheduler::Pipeline::MAIN);
} }
else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/) else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/)
{ {

View File

@ -268,19 +268,6 @@ namespace Components
Network::SendCommand(master, "heartbeat", "IW4"); 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) 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); return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::dvar_flag::DVAR_NONE, description);
@ -296,7 +283,7 @@ namespace Components
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
{ {
// Make sure all callbacks are handled // Make sure all callbacks are handled
Scheduler::OnFrame(Steam::SteamAPI_RunCallbacks); Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::MAIN);
Dvar::OnInit([] Dvar::OnInit([]
{ {
@ -361,9 +348,6 @@ namespace Components
// don't load the config // don't load the config
Utils::Hook::Set<BYTE>(0x4B4D19, 0xEB); Utils::Hook::Set<BYTE>(0x4B4D19, 0xEB);
// Dedicated frame handler
Utils::Hook(0x4B0F81, Dedicated::FrameStub, HOOK_CALL).install()->quick();
// Intercept time wrapping // Intercept time wrapping
Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick(); Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick();
//Utils::Hook::Set<DWORD>(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks) //Utils::Hook::Set<DWORD>(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks)
@ -374,30 +358,20 @@ namespace Components
Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick();
// Transmit custom data // Transmit custom data
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
static Utils::Time::Interval interval; CardTitles::SendCustomTitlesToClients();
if (interval.elapsed(10s)) //Clantags::SendClantagsToClients();
{ }, Scheduler::Pipeline::SERVER, 10s);
interval.update();
CardTitles::SendCustomTitlesToClients();
//Clantags::SendClantagsToClients();
}
});
// Heartbeats // Heartbeats
Scheduler::Once(Dedicated::Heartbeat); Scheduler::Loop([]
Scheduler::OnFrame([]()
{ {
static Utils::Time::Interval interval; if (Dvar::Var("sv_maxclients").get<int>() > 0)
if (Dvar::Var("sv_maxclients").get<int>() > 0 && interval.elapsed(2min))
{ {
interval.update();
Dedicated::Heartbeat(); Dedicated::Heartbeat();
} }
}); }, Scheduler::Pipeline::SERVER);
Dvar::OnInit([]() Dvar::OnInit([]()
{ {
@ -495,18 +469,12 @@ namespace Components
}); });
} }
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
if (Dvar::Var("sv_running").get<bool>()) if (Dvar::Var("sv_running").get<bool>())
{ {
static Utils::Time::Interval interval; Dedicated::TransmitGuids();
if (interval.elapsed(15s))
{
interval.update();
Dedicated::TransmitGuids();
}
} }
}); }, Scheduler::Pipeline::SERVER, 15s);
} }
} }

View File

@ -24,8 +24,6 @@ namespace Components
static void PostInitialization(); static void PostInitialization();
static void PostInitializationStub(); static void PostInitializationStub();
static void FrameStub();
static void TransmitGuids(); static void TransmitGuids();
static void TimeWrapStub(Game::errorParm_t code, const char* message); static void TimeWrapStub(Game::errorParm_t code, const char* message);

View File

@ -924,7 +924,7 @@ namespace Components
Dvar::Register<bool>("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)."); Dvar::Register<bool>("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::OnFrame([]() Scheduler::Loop([]
{ {
int workingCount = 0; int workingCount = 0;
@ -957,7 +957,8 @@ namespace Components
++workingCount; ++workingCount;
} }
} }
});
}, Scheduler::Pipeline::MAIN);
Script::OnVMShutdown([]() Script::OnVMShutdown([]()
{ {

View File

@ -2,7 +2,7 @@
namespace Components namespace Components
{ {
Utils::Signal<Scheduler::Callback> Dvar::RegistrationSignal; Utils::Signal<Dvar::Callback> Dvar::RegistrationSignal;
const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg"; const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg";
Dvar::Var::Var(const std::string& dvarName) : Var() Dvar::Var::Var(const std::string& dvarName) : Var()
@ -201,7 +201,7 @@ namespace Components
return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag.val, description); return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag.val, description);
} }
void Dvar::OnInit(Utils::Slot<Scheduler::Callback> callback) void Dvar::OnInit(Utils::Slot<Dvar::Callback> callback)
{ {
Dvar::RegistrationSignal.connect(callback); Dvar::RegistrationSignal.connect(callback);
} }
@ -216,16 +216,13 @@ namespace Components
Utils::IO::RemoveFile(Dvar::ArchiveDvarPath); 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 // Name watcher
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
static std::string lastValidName = "Unknown Soldier"; static std::string lastValidName = "Unknown Soldier";
std::string name = Dvar::Var("name").get<const char*>(); auto name = Dvar::Var("name").get<std::string>();
// Don't perform any checks if name didn't change // Don't perform any checks if name didn't change
if (name == lastValidName) return; if (name == lastValidName) return;
@ -241,7 +238,7 @@ namespace Components
lastValidName = name; lastValidName = name;
Friends::UpdateName(); Friends::UpdateName();
} }
}, true); }, Scheduler::MAIN, 3s); // Don't need to do this every frame
std::string username = "Unknown Soldier"; std::string username = "Unknown Soldier";
@ -255,7 +252,7 @@ namespace Components
} }
} }
return Dvar::Register<const char*>(name, username.data(), Dvar::Flag(flag | Game::dvar_flag::DVAR_ARCHIVE).val, description).get<Game::dvar_t*>(); return Dvar::Register<const char*>(name, username.data(), flags | Game::dvar_flag::DVAR_ARCHIVE, description).get<Game::dvar_t*>();
} }
void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string) void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string)
@ -272,7 +269,7 @@ namespace Components
"ui_mptype", "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])) if (Utils::String::ToLower(dvarName) == Utils::String::ToLower(exceptions[i]))
{ {
@ -313,6 +310,12 @@ namespace Components
Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value); Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value);
} }
void Dvar::Com_InitDvars_Hk()
{
Utils::Hook::Call<void()>(0x60AD10)();
Dvar::RegistrationSignal();
}
Dvar::Dvar() Dvar::Dvar()
{ {
// set flags of cg_drawFPS to archive // set flags of cg_drawFPS to archive
@ -352,7 +355,7 @@ namespace Components
Utils::Hook::Xor<BYTE>(0x6312DE, Game::dvar_flag::DVAR_CHEAT); Utils::Hook::Xor<BYTE>(0x6312DE, Game::dvar_flag::DVAR_CHEAT);
// Hook dvar 'name' registration // 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 // un-cheat safeArea_* and add archive flags
Utils::Hook::Xor<INT>(0x42E3F5, Game::dvar_flag::DVAR_READONLY | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_horizontal Utils::Hook::Xor<INT>(0x42E3F5, Game::dvar_flag::DVAR_READONLY | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_horizontal
@ -360,6 +363,8 @@ namespace Components
Utils::Hook::Xor<BYTE>(0x42E398, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_horizontal Utils::Hook::Xor<BYTE>(0x42E398, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_horizontal
Utils::Hook::Xor<BYTE>(0x42E3C4, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_vertical Utils::Hook::Xor<BYTE>(0x42E3C4, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_vertical
Utils::Hook(0x60BB3A, Dvar::Com_InitDvars_Hk, HOOK_CALL).install()->quick();
// Don't allow setting cheat protected dvars via menus // Don't allow setting cheat protected dvars via menus
Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick(); Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
Utils::Hook(0x63CA96, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick(); Utils::Hook(0x63CA96, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
@ -385,10 +390,7 @@ namespace Components
Utils::Hook(0x59386A, Dvar::DvarSetFromStringByNameStub, HOOK_CALL).install()->quick(); Utils::Hook(0x59386A, Dvar::DvarSetFromStringByNameStub, HOOK_CALL).install()->quick();
// If the game closed abruptly, the dvars would not have been restored // If the game closed abruptly, the dvars would not have been restored
Dvar::OnInit([] Dvar::OnInit(Dvar::ResetDvarsValue);
{
Dvar::ResetDvarsValue();
});
} }
Dvar::~Dvar() Dvar::~Dvar()

View File

@ -8,8 +8,8 @@ namespace Components
class Flag class Flag
{ {
public: public:
Flag(Game::dvar_flag flag) : val(flag) {}; Flag(Game::dvar_flag flag) : val(flag) {}
Flag(unsigned __int16 flag) : Flag(static_cast<Game::dvar_flag>(flag)) {}; Flag(unsigned __int16 flag) : Flag(static_cast<Game::dvar_flag>(flag)) {}
Game::dvar_flag val; Game::dvar_flag val;
}; };
@ -17,10 +17,10 @@ namespace Components
class Var class Var
{ {
public: public:
Var() : dvar(nullptr) {}; Var() : dvar(nullptr) {}
Var(const Var& obj) { this->dvar = obj.dvar; }; Var(const Var& obj) { this->dvar = obj.dvar; }
Var(Game::dvar_t* _dvar) : dvar(_dvar) {}; Var(Game::dvar_t* _dvar) : dvar(_dvar) {}
Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {}; Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {}
Var(const std::string& dvarName); Var(const std::string& dvarName);
template<typename T> T get(); template<typename T> T get();
@ -43,7 +43,9 @@ namespace Components
Dvar(); Dvar();
~Dvar(); ~Dvar();
static void OnInit(Utils::Slot<Scheduler::Callback> callback); typedef void(Callback)();
static void OnInit(Utils::Slot<Dvar::Callback> callback);
// Only strings and bools use this type of declaration // Only strings and bools use this type of declaration
template<typename T> static Var Register(const char* dvarName, T value, Flag flag, const char* description); template<typename T> static Var Register(const char* dvarName, T value, Flag flag, const char* description);
@ -52,15 +54,18 @@ namespace Components
static void ResetDvarsValue(); static void ResetDvarsValue();
private: private:
static Utils::Signal<Scheduler::Callback> RegistrationSignal; static Utils::Signal<Dvar::Callback> RegistrationSignal;
static const char* ArchiveDvarPath; 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 SetFromStringByNameExternal(const char* dvar, const char* value);
static void SetFromStringByNameSafeExternal(const char* dvar, const char* value); static void SetFromStringByNameSafeExternal(const char* dvar, const char* value);
static void SaveArchiveDvar(const Game::dvar_t* var); static void SaveArchiveDvar(const Game::dvar_t* var);
static void DvarSetFromStringByNameStub(const char* dvarName, const char* value); static void DvarSetFromStringByNameStub(const char* dvarName, const char* value);
// Unable to do any earlier because SL system will not be active for string dvars
static void Com_InitDvars_Hk();
}; };
} }

View File

@ -569,28 +569,28 @@ namespace Components
FastFiles::AddZonePath("zone\\patch\\"); FastFiles::AddZonePath("zone\\patch\\");
FastFiles::AddZonePath("zone\\dlc\\"); FastFiles::AddZonePath("zone\\dlc\\");
Scheduler::OnFrame([]() Scheduler::Loop([]()
{ {
if (FastFiles::Current().empty() || !Dvar::Var("ui_zoneDebug").get<bool>()) return; if (FastFiles::Current().empty() || !Dvar::Var("ui_zoneDebug").get<bool>()) return;
Game::Font_s* font = Game::R_RegisterFont("fonts/consoleFont", 0); 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) }; float color[4] = { 1.0f, 1.0f, 1.0f, (Game::CL_IsCgameInitialized() ? 0.3f : 1.0f) };
std::uint32_t FFTotalSize = *reinterpret_cast<std::uint32_t*>(0x10AA5D8); auto FFTotalSize = *reinterpret_cast<std::uint32_t*>(0x10AA5D8);
std::uint32_t FFCurrentOffset = *reinterpret_cast<std::uint32_t*>(0x10AA608); auto FFCurrentOffset = *reinterpret_cast<std::uint32_t*>(0x10AA608);
float fastfileLoadProgress = (float(FFCurrentOffset) / float(FFTotalSize)) * 100.0f; float fastfileLoadProgress = (float(FFCurrentOffset) / float(FFTotalSize)) * 100.0f;
if (fastfileLoadProgress == INFINITY) if (isinf(fastfileLoadProgress))
{ {
fastfileLoadProgress = 100.0f; fastfileLoadProgress = 100.0f;
} }
else if (fastfileLoadProgress == NAN) else if (isnan(fastfileLoadProgress))
{ {
fastfileLoadProgress = 0.0f; fastfileLoadProgress = 0.0f;
} }
Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s [%0.1f%%]", FastFiles::Current().data(), fastfileLoadProgress), 0x7FFFFFFF, font, 5.0f, static_cast<float>(Renderer::Height() - 5), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s [%0.1f%%]", FastFiles::Current().data(), fastfileLoadProgress), 0x7FFFFFFF, font, 5.0f, static_cast<float>(Renderer::Height() - 5), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
}, true); }, Scheduler::Pipeline::RENDERER);
Command::Add("loadzone", [](Command::Params* params) Command::Add("loadzone", [](Command::Params* params)
{ {

View File

@ -653,7 +653,7 @@ namespace Components
} }
}); });
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
static Utils::Time::Interval timeInterval; static Utils::Time::Interval timeInterval;
static Utils::Time::Interval sortInterval; static Utils::Time::Interval sortInterval;
@ -692,11 +692,11 @@ namespace Components
Friends::SortList(true); Friends::SortList(true);
} }
} }
}); }, Scheduler::Pipeline::CLIENT);
UIFeeder::Add(61.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend); UIFeeder::Add(61.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend);
Scheduler::OnShutdown([]() Scheduler::OnGameShutdown([]
{ {
Friends::ClearPresence("iw4x_server"); Friends::ClearPresence("iw4x_server");
Friends::ClearPresence("iw4x_playing"); Friends::ClearPresence("iw4x_playing");

View File

@ -245,7 +245,7 @@ namespace Components
Logger::PipeOutput(nullptr); Logger::PipeOutput(nullptr);
Scheduler::OnFrame(Logger::Frame); Scheduler::OnGameInitialized(Logger::Frame, Scheduler::Pipeline::MAIN);
Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick(); Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick();
Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick(); Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick();

View File

@ -879,11 +879,11 @@ namespace Components
Command::Add("delayReconnect", [](Command::Params*) Command::Add("delayReconnect", [](Command::Params*)
{ {
Scheduler::OnDelay([]() Scheduler::Once([]
{ {
Command::Execute("closemenu popup_reconnectingtoparty", false); Command::Execute("closemenu popup_reconnectingtoparty", false);
Command::Execute("reconnect", false); Command::Execute("reconnect", false);
}, 10s, true); }, Scheduler::Pipeline::CLIENT, 10s);
}); });
if(Dedicated::IsEnabled()) if(Dedicated::IsEnabled())
@ -908,9 +908,9 @@ namespace Components
// Allow hiding specific smodels // Allow hiding specific smodels
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick(); Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
Game::GfxWorld*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94); auto*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
if (!Game::CL_IsCgameInitialized() || !gameWorld || !Dvar::Var("r_listSModels").get<bool>()) return; if (!Game::CL_IsCgameInitialized() || !gameWorld || !Dvar::Var("r_listSModels").get<bool>()) return;
std::map<std::string, int> models; std::map<std::string, int> models;
@ -926,16 +926,16 @@ namespace Components
} }
Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0); Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0);
int height = Game::R_TextHeight(font); auto height = Game::R_TextHeight(font);
float scale = 0.75; auto scale = 0.75f;
float color[4] = { 0, 1.0f, 0, 1.0f }; float color[4] = {0.0f, 1.0f, 0.0f, 1.0f};
unsigned int i = 0; unsigned int i = 0;
for (auto& model : models) 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); 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() Maps::~Maps()

View File

@ -320,11 +320,11 @@ namespace Components
size_t i = 0; size_t i = 0;
for (auto& nodeListData : nodeListReponseMessages) for (auto& nodeListData : nodeListReponseMessages)
{ {
Scheduler::OnDelay([nodeListData, i, address]() Scheduler::Once([nodeListData, i, address]()
{ {
NODE_LOG("Sending %d nodeListResponse length to %s\n", nodeListData.length(), address.getCString()); NODE_LOG("Sending %d nodeListResponse length to %s\n", nodeListData.length(), address.getCString());
Session::Send(address, "nodeListResponse", nodeListData); 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; if (ZoneBuilder::IsEnabled()) return;
Dvar::Register<bool>("net_natFix", false, 0, "Fix node registration for certain firewalls/routers"); Dvar::Register<bool>("net_natFix", false, 0, "Fix node registration for certain firewalls/routers");
Scheduler::OnFrameAsync([]() Scheduler::Loop([]
{ {
Node::StoreNodes(false); Node::StoreNodes(false);
}); });
Scheduler::OnFrame(Node::RunFrame); Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::MAIN);
Session::Handle("nodeListResponse", Node::HandleResponse); Session::Handle("nodeListResponse", Node::HandleResponse);
Session::Handle("nodeListRequest", [](Network::Address address, const std::string&) Session::Handle("nodeListRequest", [](Network::Address address, const std::string&)
{ {

View File

@ -278,7 +278,7 @@ namespace Components
Party::Connect(Party::Container.target); Party::Connect(Party::Container.target);
}); });
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
if (Party::Container.valid) if (Party::Container.valid)
{ {
@ -297,7 +297,8 @@ namespace Components
Party::ConnectError("Playlist request timed out."); Party::ConnectError("Playlist request timed out.");
} }
} }
}, true);
}, Scheduler::Pipeline::CLIENT);
// Basic info handler // Basic info handler
Network::Handle("getInfo", [](Network::Address address, const std::string& data) Network::Handle("getInfo", [](Network::Address address, const std::string& data)

View File

@ -252,7 +252,7 @@ namespace Components
Utils::Hook(0x5063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x5063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick();
// Make sure preDestroy is called when the game shuts down // Make sure preDestroy is called when the game shuts down
Scheduler::OnShutdown(Loader::PreDestroy); Scheduler::OnGameShutdown(Loader::PreDestroy);
// protocol version (workaround for hacks) // protocol version (workaround for hacks)
Utils::Hook::Set<int>(0x4FB501, PROTOCOL); Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
@ -469,10 +469,10 @@ namespace Components
// Fix mouse lag // Fix mouse lag
Utils::Hook::Nop(0x4731F5, 8); Utils::Hook::Nop(0x4731F5, 8);
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
SetThreadExecutionState(ES_DISPLAY_REQUIRED); SetThreadExecutionState(ES_DISPLAY_REQUIRED);
}); }, Scheduler::Pipeline::RENDERER);
// Fix mouse pitch adjustments // Fix mouse pitch adjustments
Dvar::Register<bool>("ui_mousePitch", false, Game::DVAR_ARCHIVE, ""); Dvar::Register<bool>("ui_mousePitch", false, Game::DVAR_ARCHIVE, "");
@ -709,13 +709,13 @@ namespace Components
// Constantly draw the mini console // Constantly draw the mini console
Utils::Hook::Set<BYTE>(0x412A45, 0xEB); Utils::Hook::Set<BYTE>(0x412A45, 0xEB);
Scheduler::OnFrame([]() Scheduler::Loop([]()
{ {
if (*reinterpret_cast<Game::Font_s**>(0x62E4BAC)) if (*reinterpret_cast<Game::Font_s**>(0x62E4BAC))
{ {
Game::Con_DrawMiniConsole(0, 2, 4, (Game::CL_IsCgameInitialized() ? 1.0f : 0.4f)); Game::Con_DrawMiniConsole(0, 2, 4, (Game::CL_IsCgameInitialized() ? 1.0f : 0.4f));
} }
}, true); }, Scheduler::Pipeline::RENDERER);
#else #else
// Remove missing tag message // Remove missing tag message
Utils::Hook::Nop(0x4EBF1A, 5); Utils::Hook::Nop(0x4EBF1A, 5);

View File

@ -5,8 +5,8 @@ namespace Components
Utils::Signal<Renderer::BackendCallback> Renderer::BackendFrameSignal; Utils::Signal<Renderer::BackendCallback> Renderer::BackendFrameSignal;
Utils::Signal<Renderer::BackendCallback> Renderer::SingleBackendFrameSignal; Utils::Signal<Renderer::BackendCallback> Renderer::SingleBackendFrameSignal;
Utils::Signal<Scheduler::Callback> Renderer::EndRecoverDeviceSignal; Utils::Signal<Renderer::Callback> Renderer::EndRecoverDeviceSignal;
Utils::Signal<Scheduler::Callback> Renderer::BeginRecoverDeviceSignal; Utils::Signal<Renderer::Callback> Renderer::BeginRecoverDeviceSignal;
Dvar::Var Renderer::r_drawTriggers; Dvar::Var Renderer::r_drawTriggers;
Dvar::Var Renderer::r_drawSceneModelCollisions; Dvar::Var Renderer::r_drawSceneModelCollisions;
@ -32,19 +32,6 @@ namespace Components
float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f }; float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float multiple[4] = { 0.0f, 1.0f, 0.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() __declspec(naked) void Renderer::BackendFrameStub()
{ {
__asm __asm
@ -87,12 +74,12 @@ namespace Components
Renderer::BackendFrameSignal.connect(callback); Renderer::BackendFrameSignal.connect(callback);
} }
void Renderer::OnDeviceRecoveryEnd(Utils::Slot<Scheduler::Callback> callback) void Renderer::OnDeviceRecoveryEnd(Utils::Slot<Renderer::Callback> callback)
{ {
Renderer::EndRecoverDeviceSignal.connect(callback); Renderer::EndRecoverDeviceSignal.connect(callback);
} }
void Renderer::OnDeviceRecoveryBegin(Utils::Slot<Scheduler::Callback> callback) void Renderer::OnDeviceRecoveryBegin(Utils::Slot<Renderer::Callback> callback)
{ {
Renderer::BeginRecoverDeviceSignal.connect(callback); Renderer::BeginRecoverDeviceSignal.connect(callback);
} }
@ -463,7 +450,8 @@ namespace Components
{ {
if (Dedicated::IsEnabled()) return; if (Dedicated::IsEnabled()) return;
Scheduler::OnFrame([]() { Scheduler::Loop([]
{
if (Game::CL_IsCgameInitialized()) if (Game::CL_IsCgameInitialized())
{ {
DebugDrawAABBTrees(); DebugDrawAABBTrees();
@ -472,35 +460,7 @@ namespace Components
DebugDrawSceneModelCollisions(); DebugDrawSceneModelCollisions();
DebugDrawTriggers(); DebugDrawTriggers();
} }
}); }, Scheduler::Pipeline::RENDERER);
// 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();
// }
// });
// Log broken materials // Log broken materials
Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick(); Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick();
@ -510,9 +470,6 @@ namespace Components
Utils::Hook::Set(0x005086DA, "^3solid^7"); Utils::Hook::Set(0x005086DA, "^3solid^7");
Utils::Hook(0x00580F53, Renderer::DrawTechsetForMaterial, HOOK_CALL).install()->quick(); 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(); Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick();
// Begin device recovery (not D3D9Ex) // Begin device recovery (not D3D9Ex)

View File

@ -6,6 +6,7 @@ namespace Components
{ {
public: public:
typedef void(BackendCallback)(IDirect3DDevice9*); typedef void(BackendCallback)(IDirect3DDevice9*);
typedef void(Callback)();
Renderer(); Renderer();
~Renderer(); ~Renderer();
@ -15,12 +16,11 @@ namespace Components
static void OnBackendFrame(Utils::Slot<BackendCallback> callback); static void OnBackendFrame(Utils::Slot<BackendCallback> callback);
static void OnNextBackendFrame(Utils::Slot<BackendCallback> callback); static void OnNextBackendFrame(Utils::Slot<BackendCallback> callback);
static void OnDeviceRecoveryEnd(Utils::Slot<Scheduler::Callback> callback);
static void OnDeviceRecoveryBegin(Utils::Slot<Scheduler::Callback> callback); static void OnDeviceRecoveryEnd(Utils::Slot<Renderer::Callback> callback);
static void OnDeviceRecoveryBegin(Utils::Slot<Renderer::Callback> callback);
private: private:
static void FrameStub();
static void BackendFrameStub(); static void BackendFrameStub();
static void BackendFrameHandler(); static void BackendFrameHandler();
@ -40,8 +40,8 @@ namespace Components
static void DebugDrawModelNames(); static void DebugDrawModelNames();
static void DebugDrawAABBTrees(); static void DebugDrawAABBTrees();
static Utils::Signal<Scheduler::Callback> EndRecoverDeviceSignal; static Utils::Signal<Renderer::Callback> EndRecoverDeviceSignal;
static Utils::Signal<Scheduler::Callback> BeginRecoverDeviceSignal; static Utils::Signal<Renderer::Callback> BeginRecoverDeviceSignal;
static Utils::Signal<BackendCallback> BackendFrameSignal; static Utils::Signal<BackendCallback> BackendFrameSignal;
static Utils::Signal<BackendCallback> SingleBackendFrameSignal; static Utils::Signal<BackendCallback> SingleBackendFrameSignal;

View File

@ -1,162 +1,193 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
constexpr bool COND_CONTINUE = false;
constexpr bool COND_END = true;
namespace Components namespace Components
{ {
bool Scheduler::AsyncTerminate; std::thread Scheduler::Thread;
std::thread Scheduler::AsyncThread; volatile bool Scheduler::Kill = false;
Scheduler::TaskPipeline Scheduler::Pipelines[Pipeline::COUNT];
bool Scheduler::ReadyPassed = false; void Scheduler::TaskPipeline::add(Task&& task)
Utils::Signal<Scheduler::Callback> Scheduler::ReadySignal;
Utils::Signal<Scheduler::Callback> Scheduler::ShutdownSignal;
Utils::Signal<Scheduler::Callback> Scheduler::FrameSignal;
Utils::Signal<Scheduler::Callback> Scheduler::FrameOnceSignal;
std::vector<Scheduler::DelayedSlot> Scheduler::DelayedSlots;
Utils::Signal<Scheduler::Callback> Scheduler::AsyncFrameSignal;
Utils::Signal<Scheduler::Callback> Scheduler::AsyncFrameOnceSignal;
void Scheduler::Once(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
{ {
if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return; newCallbacks_.access([&task](taskList& tasks)
Scheduler::FrameOnceSignal.connect(callback);
}
void Scheduler::OnShutdown(Utils::Slot<Scheduler::Callback> callback)
{
Scheduler::ShutdownSignal.connect(callback);
}
void Scheduler::OnFrame(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
{
if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return;
Scheduler::FrameSignal.connect(callback);
}
void Scheduler::OnReady(Utils::Slot<Scheduler::Callback> 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())
{ {
Scheduler::Once(Scheduler::ReadyHandler); tasks.emplace_back(std::move(task));
} });
else }
void Scheduler::TaskPipeline::execute()
{
callbacks_.access([&](taskList& tasks)
{ {
Scheduler::ReadyPassed = true; this->mergeCallbacks();
Scheduler::ReadySignal();
Scheduler::ReadySignal.clear();
}
}
void Scheduler::FrameHandler() for (auto i = tasks.begin(); i != tasks.end();)
{
Scheduler::DelaySignal();
Scheduler::FrameSignal();
Utils::Signal<Scheduler::Callback> copy(Scheduler::FrameOnceSignal);
Scheduler::FrameOnceSignal.clear();
copy();
}
void Scheduler::OnDelay(Utils::Slot<Scheduler::Callback> 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<Scheduler::Callback> signal;
for (auto i = Scheduler::DelayedSlots.begin(); i != Scheduler::DelayedSlots.end();)
{
if (i->interval.elapsed(i->delay))
{ {
signal.connect(i->callback); const auto now = std::chrono::high_resolution_clock::now();
i = Scheduler::DelayedSlots.erase(i); const auto diff = now - i->lastCall;
continue;
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<taskList::iterator>(new_tasks.begin()), std::move_iterator<taskList::iterator>(new_tasks.end()));
new_tasks = {};
});
});
}
void Scheduler::Execute(Pipeline type)
{
AssertCount(type, Pipeline::COUNT);
Pipelines[type].execute();
}
void Scheduler::REndFrame_Hk()
{
Execute(Pipeline::RENDERER);
Utils::Hook::Call<void()>(0x50AB20)();
}
void Scheduler::ServerFrame_Hk()
{
Execute(Pipeline::SERVER);
Utils::Hook::Call<void()>(0x471C50)();
}
void Scheduler::ClientFrame_Hk(const int localClientNum)
{
Utils::Hook::Call<void(int)>(0x5A8E80)(localClientNum);
Execute(Pipeline::CLIENT);
}
void Scheduler::MainFrame_Hk()
{
Execute(Pipeline::MAIN);
Utils::Hook::Call<void()>(0x47DCA0)();
}
void Scheduler::SysSetBlockSystemHotkeys_Hk(int block)
{
Execute(Pipeline::QUIT);
Utils::Hook::Call<void(int)>(0x46B370)(block);
}
void Scheduler::Schedule(const std::function<bool()>& callback, const Pipeline type,
const std::chrono::milliseconds delay)
{
AssertCount(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<void()>& callback, const Pipeline type,
const std::chrono::milliseconds delay)
{
Schedule([callback]
{
callback();
return COND_CONTINUE;
}, type, delay);
}
void Scheduler::Once(const std::function<void()>& callback, const Pipeline type,
const std::chrono::milliseconds delay)
{
Schedule([callback]
{
callback();
return COND_END;
}, type, delay);
}
void Scheduler::OnGameInitialized(const std::function<void()>& callback, const Pipeline type,
const std::chrono::milliseconds delay)
{
Schedule([=]
{
if (Game::Sys_IsDatabaseReady2())
{
Once(callback, type, delay);
return COND_END;
} }
++i; return COND_CONTINUE;
} }, Pipeline::MAIN); // Once Com_Frame_Try_Block_Function is called we know the game is 'ready'
signal();
} }
void Scheduler::ShutdownStub(int num) void Scheduler::OnGameShutdown(const std::function<void()>& callback)
{ {
Scheduler::ShutdownSignal(); Schedule([callback]
Utils::Hook::Call<void(int)>(0x46B370)(num); {
} callback();
return COND_END;
void Scheduler::OnFrameAsync(Utils::Slot<Scheduler::Callback> callback) }, Pipeline::QUIT, 0ms);
{
Scheduler::AsyncFrameSignal.connect(callback);
}
void Scheduler::OnceAsync(Utils::Slot<Scheduler::Callback> callback)
{
Scheduler::AsyncFrameOnceSignal.connect(callback);
} }
Scheduler::Scheduler() Scheduler::Scheduler()
{ {
Scheduler::ReadyPassed = false; Thread = Utils::Thread::createNamedThread("Async Scheduler", []
Scheduler::Once(Scheduler::ReadyHandler);
Utils::Hook(0x4D697A, Scheduler::ShutdownStub, HOOK_CALL).install()->quick();
if (!Loader::IsPerformingUnitTests())
{ {
Scheduler::AsyncTerminate = false; while (!Kill)
Scheduler::AsyncThread = std::thread([]()
{ {
while (!Scheduler::AsyncTerminate) Execute(Pipeline::ASYNC);
{ std::this_thread::sleep_for(10ms);
Scheduler::AsyncFrameSignal(); }
});
Utils::Signal<Scheduler::Callback> copy(Scheduler::AsyncFrameOnceSignal); Utils::Hook(0x4DBE9A, REndFrame_Hk, HOOK_CALL).install()->quick();
Scheduler::AsyncFrameOnceSignal.clear(); Utils::Hook(0x518D5C, REndFrame_Hk, HOOK_CALL).install()->quick();
copy(); 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() // CL_CheckTimeout
{ Utils::Hook(0x4B0F81, ClientFrame_Hk, HOOK_CALL).install()->quick();
Scheduler::ReadySignal.clear();
Scheduler::ShutdownSignal.clear();
Scheduler::FrameSignal.clear(); // Com_Frame_Try_Block_Function
Scheduler::FrameOnceSignal.clear(); Utils::Hook(0x4B724F, MainFrame_Hk, HOOK_CALL).install()->quick();
Scheduler::DelayedSlots.clear();
Scheduler::AsyncFrameSignal.clear(); // Sys_Quit
Scheduler::AsyncFrameOnceSignal.clear(); Utils::Hook(0x4D697A, SysSetBlockSystemHotkeys_Hk, HOOK_CALL).install()->quick();
Scheduler::ReadyPassed = false;
} }
void Scheduler::preDestroy() void Scheduler::preDestroy()
{ {
Scheduler::AsyncTerminate = true; Kill = true;
if (Scheduler::AsyncThread.joinable()) if (Thread.joinable())
{ {
Scheduler::AsyncThread.join(); Thread.join();
} }
} }
} }

View File

@ -5,50 +5,64 @@ namespace Components
class Scheduler : public Component class Scheduler : public Component
{ {
public: public:
typedef void(Callback)(); enum Pipeline
{
ASYNC,
RENDERER,
SERVER,
CLIENT,
MAIN,
QUIT,
COUNT,
};
Scheduler(); Scheduler();
~Scheduler();
void preDestroy() override; void preDestroy() override;
static void OnShutdown(Utils::Slot<Callback> callback); static void Schedule(const std::function<bool()>& callback, Pipeline type = Pipeline::ASYNC,
static void OnFrame(Utils::Slot<Callback> callback, bool clientOnly = false); std::chrono::milliseconds delay = 0ms);
static void OnReady(Utils::Slot<Callback> callback, bool clientOnly = false); static void Loop(const std::function<void()>& callback, Pipeline type = Pipeline::ASYNC,
static void Once(Utils::Slot<Callback> callback, bool clientOnly = false); std::chrono::milliseconds delay = 0ms);
static void OnDelay(Utils::Slot<Callback> callback, std::chrono::nanoseconds delay, bool clientOnly = false); static void Once(const std::function<void()>& callback, Pipeline type = Pipeline::ASYNC,
std::chrono::milliseconds delay = 0ms);
static void OnFrameAsync(Utils::Slot<Callback> callback); static void OnGameInitialized(const std::function<void()>& callback, Pipeline type = Pipeline::ASYNC,
static void OnceAsync(Utils::Slot<Callback> callback); std::chrono::milliseconds delay = 0ms);
static void OnGameShutdown(const std::function<void()>& callback);
static void FrameHandler();
private: private:
class DelayedSlot struct Task
{ {
public: std::function<bool()> handler{};
std::chrono::nanoseconds delay; std::chrono::milliseconds interval{};
Utils::Time::Interval interval; std::chrono::high_resolution_clock::time_point lastCall{};
Utils::Slot<Callback> callback;
}; };
static bool AsyncTerminate; using taskList = std::vector<Task>;
static std::thread AsyncThread;
static Utils::Signal<Callback> FrameSignal; class TaskPipeline
static Utils::Signal<Callback> FrameOnceSignal; {
static std::vector<DelayedSlot> DelayedSlots; public:
void add(Task&& task);
void execute();
static bool ReadyPassed; private:
static Utils::Signal<Callback> ReadySignal; Utils::Concurrency::Container<taskList> newCallbacks_;
static Utils::Signal<Callback> ShutdownSignal; Utils::Concurrency::Container<taskList, std::recursive_mutex> callbacks_;
static Utils::Signal<Callback> AsyncFrameSignal; void mergeCallbacks();
static Utils::Signal<Callback> AsyncFrameOnceSignal; };
static void ReadyHandler(); static volatile bool Kill;
static void DelaySignal(); 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);
}; };
} }

View File

@ -14,7 +14,7 @@ namespace Components
const char* Script::ReplacedPos = nullptr; const char* Script::ReplacedPos = nullptr;
int Script::LastFrameTime = -1; int Script::LastFrameTime = -1;
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal; Utils::Signal<Script::Callback> Script::VMShutdownSignal;
void Script::FunctionError() void Script::FunctionError()
{ {
@ -411,7 +411,7 @@ namespace Components
} }
} }
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback) void Script::OnVMShutdown(Utils::Slot<Script::Callback> callback)
{ {
Script::ScriptBaseProgramNum.clear(); Script::ScriptBaseProgramNum.clear();
Script::VMShutdownSignal.connect(std::move(callback)); 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(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_LoadGame
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_ShutdownGame Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_ShutdownGame
Scheduler::OnFrame([]() Scheduler::Loop([]()
{ {
if (!Game::SV_Loaded()) if (!Game::SV_Loaded())
return; return;
@ -735,7 +735,7 @@ namespace Components
} }
Script::LastFrameTime = nowMs; Script::LastFrameTime = nowMs;
}); }, Scheduler::Pipeline::SERVER);
#ifdef _DEBUG #ifdef _DEBUG
Script::AddFunction("DebugBox", [] Script::AddFunction("DebugBox", []

View File

@ -9,12 +9,14 @@ namespace Components
Script(); Script();
~Script(); ~Script();
typedef void(Callback)();
static int LoadScriptAndLabel(const std::string& script, const std::string& label); 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 AddFunction(const char* name, Game::BuiltinFunction func, int type = 0);
static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0); static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0);
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback); static void OnVMShutdown(Utils::Slot<Script::Callback> callback);
static Game::client_t* GetClient(const Game::gentity_t* gentity); static Game::client_t* GetClient(const Game::gentity_t* gentity);
@ -31,7 +33,7 @@ namespace Components
static const char* ReplacedPos; static const char* ReplacedPos;
static int LastFrameTime; static int LastFrameTime;
static Utils::Signal<Scheduler::Callback> VMShutdownSignal; static Utils::Signal<Script::Callback> VMShutdownSignal;
static void CompileError(unsigned int offset, const char* message, ...); static void CompileError(unsigned int offset, const char* message, ...);
static void PrintSourcePos(const char* filename, unsigned int offset); static void PrintSourcePos(const char* filename, unsigned int offset);

View File

@ -922,7 +922,7 @@ namespace Components
UIScript::AddOwnerDraw(253, ServerList::UpdateGameType); UIScript::AddOwnerDraw(253, ServerList::UpdateGameType);
// Add frame callback // Add frame callback
Scheduler::OnFrame(ServerList::Frame); Scheduler::OnGameInitialized(ServerList::Frame, Scheduler::Pipeline::CLIENT);
} }
ServerList::~ServerList() ServerList::~ServerList()

View File

@ -33,11 +33,11 @@ namespace Components
Network::SendCommand(target, command, data); Network::SendCommand(target, command, data);
Scheduler::OnDelay([delayData]() Scheduler::Once([delayData]()
{ {
Network::SendCommand(delayData->target, delayData->command, delayData->data); Network::SendCommand(delayData->target, delayData->command, delayData->data);
delete delayData; delete delayData;
}, 500ms + std::chrono::milliseconds(rand() % 200)); }, Scheduler::Pipeline::MAIN, 500ms + std::chrono::milliseconds(rand() % 200));
#else #else
std::lock_guard<std::recursive_mutex> _(Session::Mutex); std::lock_guard<std::recursive_mutex> _(Session::Mutex);

View File

@ -148,10 +148,7 @@ namespace Components
{ {
if (Dedicated::IsEnabled() || Monitor::IsEnabled()) return; if (Dedicated::IsEnabled() || Monitor::IsEnabled()) return;
Scheduler::OnReady([]() Scheduler::OnGameInitialized(Toast::Handler, Scheduler::Pipeline::RENDERER);
{
Scheduler::OnFrame(Toast::Handler);
});
Command::Add("testtoast", [](Command::Params*) Command::Add("testtoast", [](Command::Params*)
{ {

View File

@ -178,7 +178,7 @@ namespace Components
Utils::Hook(0x48E5D3, Window::DrawCursorStub, HOOK_CALL).install()->quick(); Utils::Hook(0x48E5D3, Window::DrawCursorStub, HOOK_CALL).install()->quick();
// Draw the cursor if necessary // Draw the cursor if necessary
Scheduler::OnFrame([]() Scheduler::Loop([]
{ {
if (Window::NativeCursor.get<bool>() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow)) if (Window::NativeCursor.get<bool>() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow))
{ {
@ -198,7 +198,7 @@ namespace Components
Window::CursorVisible = FALSE; Window::CursorVisible = FALSE;
} }
}); }, Scheduler::Pipeline::RENDERER);
// Don't let the game interact with the native cursor // Don't let the game interact with the native cursor
Utils::Hook::Set(0x6D7348, Window::ShowCursorHook); Utils::Hook::Set(0x6D7348, Window::ShowCursorHook);

View File

@ -873,10 +873,10 @@ namespace Game
typedef void(__cdecl * Sys_FreeFileList_t)(char** list); typedef void(__cdecl * Sys_FreeFileList_t)(char** list);
extern Sys_FreeFileList_t Sys_FreeFileList; extern Sys_FreeFileList_t Sys_FreeFileList;
typedef bool(__cdecl * Sys_IsDatabaseReady_t)(); typedef int(__cdecl * Sys_IsDatabaseReady_t)();
extern Sys_IsDatabaseReady_t Sys_IsDatabaseReady; extern Sys_IsDatabaseReady_t Sys_IsDatabaseReady;
typedef bool(__cdecl * Sys_IsDatabaseReady2_t)(); typedef int(__cdecl * Sys_IsDatabaseReady2_t)();
extern Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2; extern Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2;
typedef bool(__cdecl * Sys_IsMainThread_t)(); typedef bool(__cdecl * Sys_IsMainThread_t)();

View File

@ -353,7 +353,7 @@ namespace Game
const char** argv[8]; const char** argv[8];
}; };
static_assert(sizeof(CmdArgs) == 132); static_assert(sizeof(CmdArgs) == 0x84);
typedef struct cmd_function_s typedef struct cmd_function_s
{ {

View File

@ -16,7 +16,7 @@
#define VLD_FORCE_ENABLE #define VLD_FORCE_ENABLE
//#include <vld.h> //#include <vld.h>
#include <windows.h> #include <Windows.h>
#include <timeapi.h> #include <timeapi.h>
#include <shellapi.h> #include <shellapi.h>
#include <Wininet.h> #include <Wininet.h>
@ -29,7 +29,7 @@
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4091) #pragma warning(disable: 4091)
#pragma warning(disable: 4244) #pragma warning(disable: 4244)
#include <dbghelp.h> #include <DbgHelp.h>
#include <sstream> #include <sstream>
#include <fstream> #include <fstream>
@ -45,13 +45,14 @@
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include <random> #include <random>
#include <chrono>
#pragma warning(pop) #pragma warning(pop)
#include <d3dx9tex.h> #include <d3dx9tex.h>
#pragma comment(lib, "D3dx9.lib") #pragma comment(lib, "D3dx9.lib")
#include <Xinput.h> #include <XInput.h>
#pragma comment (lib, "xinput.lib") #pragma comment (lib, "xinput.lib")
// Ignore the warnings // Ignore the warnings
@ -63,7 +64,6 @@
#pragma warning(disable: 4389) #pragma warning(disable: 4389)
#pragma warning(disable: 4702) #pragma warning(disable: 4702)
#pragma warning(disable: 4800) #pragma warning(disable: 4800)
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 5054) #pragma warning(disable: 5054)
#pragma warning(disable: 6001) #pragma warning(disable: 6001)
#pragma warning(disable: 6011) #pragma warning(disable: 6011)
@ -74,22 +74,36 @@
#pragma warning(disable: 6387) #pragma warning(disable: 6387)
#pragma warning(disable: 26812) #pragma warning(disable: 26812)
#include <zlib.h>
#include <curses.h> #include <curses.h>
#include <mongoose.h> #include <gsl/gsl>
#include <json11.hpp> #include <json11.hpp>
#include <tomcrypt.h> #include <tomcrypt.h>
#include <mongoose.h>
#include <udis86.h> #include <udis86.h>
#include <zlib.h>
// Enable additional literals
using namespace std::literals;
#ifdef max #ifdef max
#undef max #undef max
#endif #endif
#ifdef min #ifdef min
#undef min #undef min
#endif #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)
#define AssertCount(expr, count) \
assert((#expr " doesn't index " #count, (expr) < (count)))
// Protobuf // Protobuf
#include "proto/session.pb.h" #include "proto/session.pb.h"
#include "proto/party.pb.h" #include "proto/party.pb.h"
@ -101,28 +115,31 @@
#pragma warning(pop) #pragma warning(pop)
#include "Utils/IO.hpp" #include "Utils/Memory.hpp" // Breaks order on purpose
#include "Utils/CSV.hpp"
#include "Utils/Time.hpp"
#include "Utils/Cache.hpp" #include "Utils/Cache.hpp"
#include "Utils/Chain.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/Utils.hpp"
#include "Utils/WebIO.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/Structs.hpp"
#include "Game/Functions.hpp" #include "Game/Functions.hpp"
#include "Utils/Stream.hpp" #include "Utils/Stream.hpp" // Breaks order on purpose
#include "Components/Loader.hpp" #include "Components/Loader.hpp"
@ -139,20 +156,11 @@
#pragma comment(lib, "dbghelp.lib") #pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "ntdll.lib") #pragma comment(lib, "ntdll.lib")
// Enable additional literals
using namespace std::literals;
#endif #endif
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
#define BASEGAME "iw4x" #define BASEGAME "iw4x"
#define CLIENT_CONFIG "iw4x_config.cfg" #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 // Resource stuff
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS

46
src/Utils/Concurrency.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include <mutex>
namespace Utils::Concurrency
{
template <typename T, typename MutexType = std::mutex>
class Container
{
public:
template <typename R = void, typename F>
R access(F&& accessor) const
{
std::lock_guard<MutexType> _{mutex_};
return accessor(object_);
}
template <typename R = void, typename F>
R access(F&& accessor)
{
std::lock_guard<MutexType> _{mutex_};
return accessor(object_);
}
template <typename R = void, typename F>
R accessWithLock(F&& accessor) const
{
std::unique_lock<MutexType> lock{mutex_};
return accessor(object_, lock);
}
template <typename R = void, typename F>
R accessWithLock(F&& accessor)
{
std::unique_lock<MutexType> lock{mutex_};
return accessor(object_, lock);
}
T& getRaw() {return object_;}
const T& getRaw() const {return object_;}
private:
mutable MutexType mutex_{};
T object_{};
};
}

View File

@ -19,14 +19,14 @@ namespace Utils
return Library(handle); 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) Library::Library(const HMODULE handle)
{ {
this->_module = handle; this->module_ = handle;
this->freeOnDestroy = true; 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 bool Library::isValid() const
{ {
return this->_module != nullptr; return this->module_ != nullptr;
} }
HMODULE Library::getModule() const HMODULE Library::getModule() const
{ {
return this->_module; return this->module_;
} }
void Library::free() void Library::free()
{ {
if (this->isValid()) if (this->isValid())
{ {
FreeLibrary(this->_module); FreeLibrary(this->module_);
} }
this->_module = nullptr; this->module_ = nullptr;
} }
} }

View File

@ -9,12 +9,18 @@ namespace Utils
static Library Load(const std::filesystem::path& path); static Library Load(const std::filesystem::path& path);
static Library GetByAddress(void* address); static Library GetByAddress(void* address);
Library() : _module(nullptr), freeOnDestroy(false) {}; Library() : module_(nullptr), freeOnDestroy(false) {};
Library(const std::string& name, bool freeOnDestroy); 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); explicit Library(HMODULE handle);
~Library(); ~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; bool isValid() const;
HMODULE getModule() const; HMODULE getModule() const;
@ -22,7 +28,7 @@ namespace Utils
T getProc(const std::string& process) const T getProc(const std::string& process) const
{ {
if (!this->isValid()) T{}; if (!this->isValid()) T{};
return reinterpret_cast<T>(GetProcAddress(this->_module, process.data())); return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
} }
template <typename T> template <typename T>
@ -59,7 +65,7 @@ namespace Utils
void free(); void free();
private: private:
HMODULE _module; HMODULE module_;
bool freeOnDestroy; bool freeOnDestroy;
}; };
} }

View File

@ -34,7 +34,7 @@ namespace Utils
this->refMemory.clear(); this->refMemory.clear();
for (auto& data : this->pool) for (const auto& data : this->pool)
{ {
Memory::Free(data); Memory::Free(data);
} }

122
src/Utils/Thread.cpp Normal file
View File

@ -0,0 +1,122 @@
#include <STDInclude.hpp>
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<HRESULT(WINAPI*)(HANDLE, PCWSTR)>("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<DWORD> 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<DWORD> 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<void(HANDLE)>& 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);
}
});
}
}

23
src/Utils/Thread.hpp Normal file
View File

@ -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 <typename ...Args>
std::thread createNamedThread(const std::string& name, Args&&... args)
{
auto t = std::thread(std::forward<Args>(args)...);
setName(t, name);
return t;
}
std::vector<DWORD> getThreadIds();
void forEachThread(const std::function<void(HANDLE)>& callback);
void suspendOtherThreads();
void resumeOtherThreads();
}