Attempt to refactor scheduler *gone insane*
This commit is contained in:
parent
db51c06a12
commit
b0dfd16759
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
1
deps/GSL
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit da01eb28dbb75bed11a51acff0f80ecedd622573
|
19
deps/premake/gsl.lua
vendored
Normal file
19
deps/premake/gsl.lua
vendored
Normal 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)
|
@ -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
|
||||||
|
@ -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*)
|
||||||
{
|
{
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()*/)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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([]()
|
||||||
{
|
{
|
||||||
|
@ -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()
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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");
|
||||||
|
@ -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();
|
||||||
|
@ -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()
|
||||||
|
@ -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&)
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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", []
|
||||||
|
@ -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);
|
||||||
|
@ -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()
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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*)
|
||||||
{
|
{
|
||||||
|
@ -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);
|
||||||
|
@ -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)();
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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
46
src/Utils/Concurrency.hpp
Normal 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_{};
|
||||||
|
};
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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
122
src/Utils/Thread.cpp
Normal 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
23
src/Utils/Thread.hpp
Normal 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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user