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"]
|
||||
path = deps/dxsdk
|
||||
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
|
||||
{
|
||||
public:
|
||||
Component() {};
|
||||
virtual ~Component() {};
|
||||
Component() {}
|
||||
virtual ~Component() {}
|
||||
|
||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||
virtual std::string getName()
|
||||
@ -20,8 +20,8 @@ namespace Components
|
||||
// It's illegal to spawn threads in DLLMain, and apparently it causes problems if they are destroyed there as well.
|
||||
// This method is called before DLLMain (if possible) and should to destroy threads.
|
||||
// It's not 100% guaranteed that it's called outside DLLMain, as it depends on the game, but it's 100% guaranteed, that it is called at all.
|
||||
virtual void preDestroy() {};
|
||||
virtual bool unitTest() { return true; }; // Unit testing entry
|
||||
virtual void preDestroy() {}
|
||||
virtual bool unitTest() { return true; } // Unit testing entry
|
||||
};
|
||||
|
||||
class Loader
|
||||
|
@ -514,18 +514,19 @@ namespace Components
|
||||
if (!ZoneBuilder::IsEnabled()) Utils::Hook(0x5BB3F2, AssetHandler::MissingAssetError, HOOK_CALL).install()->quick();
|
||||
|
||||
// Log missing empty assets
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
if (FastFiles::Ready() && !AssetHandler::EmptyAssets.empty())
|
||||
{
|
||||
for (auto& asset : AssetHandler::EmptyAssets)
|
||||
{
|
||||
Game::Com_PrintWarning(25, reinterpret_cast<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();
|
||||
}
|
||||
});
|
||||
}, Scheduler::Pipeline::MAIN);
|
||||
|
||||
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*)
|
||||
{
|
||||
|
@ -429,10 +429,13 @@ namespace Components
|
||||
Auth::LoadKey(true);
|
||||
Steam::SteamUser()->GetSteamID();
|
||||
|
||||
Scheduler::OnFrame(Auth::Frame);
|
||||
Scheduler::Loop(Auth::Frame, Scheduler::Pipeline::MAIN);
|
||||
|
||||
// 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
|
||||
Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick();
|
||||
|
@ -268,10 +268,10 @@ namespace Components
|
||||
});
|
||||
|
||||
// Verify the list on startup
|
||||
Scheduler::Once([]()
|
||||
Scheduler::OnGameInitialized([]()
|
||||
{
|
||||
Bans::BanList list;
|
||||
Bans::LoadBans(&list);
|
||||
});
|
||||
}, Scheduler::Pipeline::SERVER);
|
||||
}
|
||||
}
|
||||
|
@ -85,26 +85,28 @@ namespace Components
|
||||
{
|
||||
for (auto i = 0u; i < count; ++i)
|
||||
{
|
||||
Scheduler::OnDelay([]()
|
||||
Scheduler::Once([]
|
||||
{
|
||||
auto* ent = Game::SV_AddTestClient();
|
||||
if (ent == nullptr)
|
||||
return;
|
||||
|
||||
Scheduler::OnDelay([ent]()
|
||||
Scheduler::Once([ent]()
|
||||
{
|
||||
Game::Scr_AddString("autoassign");
|
||||
Game::Scr_AddString("team_marinesopfor");
|
||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
||||
|
||||
Scheduler::OnDelay([ent]()
|
||||
Scheduler::Once([ent]()
|
||||
{
|
||||
Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
|
||||
Game::Scr_AddString("changeclass");
|
||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
||||
}, 1s);
|
||||
}, 1s);
|
||||
}, 500ms * (i + 1));
|
||||
}, Scheduler::Pipeline::SERVER, 1s);
|
||||
|
||||
}, Scheduler::Pipeline::SERVER, 1s);
|
||||
|
||||
}, Scheduler::Pipeline::SERVER, 500ms * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ namespace Components
|
||||
});
|
||||
|
||||
// Invocation handler
|
||||
Scheduler::OnReady(ConnectProtocol::Invocation);
|
||||
Scheduler::OnGameInitialized(ConnectProtocol::Invocation, Scheduler::Pipeline::CLIENT);
|
||||
|
||||
ConnectProtocol::InstallProtocol();
|
||||
ConnectProtocol::EvaluateProtocol();
|
||||
@ -232,10 +232,10 @@ namespace Components
|
||||
// Only skip intro here, invocation will be done later.
|
||||
Utils::Hook::Set<BYTE>(0x60BECF, 0xEB);
|
||||
|
||||
Scheduler::OnDelay([]()
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
Command::Execute("openmenu popup_reconnectingtoparty", false);
|
||||
}, 8s);
|
||||
}, Scheduler::Pipeline::CLIENT, 8s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -580,7 +580,7 @@ namespace Components
|
||||
|
||||
if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Scheduler::OnFrame(Console::RefreshStatus);
|
||||
Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN);
|
||||
}
|
||||
|
||||
// Code below is not necessary when performing unit tests!
|
||||
@ -622,10 +622,10 @@ namespace Components
|
||||
}
|
||||
}, HOOK_CALL).install()->quick();
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::OnGameInitialized([]
|
||||
{
|
||||
Console::LastRefresh = Game::Sys_Milliseconds();
|
||||
});
|
||||
}, Scheduler::Pipeline::MAIN);
|
||||
}
|
||||
else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/)
|
||||
{
|
||||
|
@ -268,19 +268,6 @@ namespace Components
|
||||
Network::SendCommand(master, "heartbeat", "IW4");
|
||||
}
|
||||
|
||||
__declspec(naked) void Dedicated::FrameStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
call Scheduler::FrameHandler
|
||||
popad
|
||||
|
||||
push 5A8E80h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* Dedicated::Dvar_RegisterSVNetworkFps(const char* dvarName, int, int min, int, int, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::dvar_flag::DVAR_NONE, description);
|
||||
@ -296,7 +283,7 @@ namespace Components
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
// Make sure all callbacks are handled
|
||||
Scheduler::OnFrame(Steam::SteamAPI_RunCallbacks);
|
||||
Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::MAIN);
|
||||
|
||||
Dvar::OnInit([]
|
||||
{
|
||||
@ -361,9 +348,6 @@ namespace Components
|
||||
// don't load the config
|
||||
Utils::Hook::Set<BYTE>(0x4B4D19, 0xEB);
|
||||
|
||||
// Dedicated frame handler
|
||||
Utils::Hook(0x4B0F81, Dedicated::FrameStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Intercept time wrapping
|
||||
Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick();
|
||||
//Utils::Hook::Set<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();
|
||||
|
||||
// Transmit custom data
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
static Utils::Time::Interval interval;
|
||||
if (interval.elapsed(10s))
|
||||
{
|
||||
interval.update();
|
||||
|
||||
CardTitles::SendCustomTitlesToClients();
|
||||
//Clantags::SendClantagsToClients();
|
||||
}
|
||||
});
|
||||
CardTitles::SendCustomTitlesToClients();
|
||||
//Clantags::SendClantagsToClients();
|
||||
}, Scheduler::Pipeline::SERVER, 10s);
|
||||
|
||||
// Heartbeats
|
||||
Scheduler::Once(Dedicated::Heartbeat);
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
static Utils::Time::Interval interval;
|
||||
|
||||
if (Dvar::Var("sv_maxclients").get<int>() > 0 && interval.elapsed(2min))
|
||||
if (Dvar::Var("sv_maxclients").get<int>() > 0)
|
||||
{
|
||||
interval.update();
|
||||
Dedicated::Heartbeat();
|
||||
}
|
||||
});
|
||||
}, Scheduler::Pipeline::SERVER);
|
||||
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
@ -495,18 +469,12 @@ namespace Components
|
||||
});
|
||||
}
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
if (Dvar::Var("sv_running").get<bool>())
|
||||
{
|
||||
static Utils::Time::Interval interval;
|
||||
|
||||
if (interval.elapsed(15s))
|
||||
{
|
||||
interval.update();
|
||||
Dedicated::TransmitGuids();
|
||||
}
|
||||
Dedicated::TransmitGuids();
|
||||
}
|
||||
});
|
||||
}, Scheduler::Pipeline::SERVER, 15s);
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,6 @@ namespace Components
|
||||
static void PostInitialization();
|
||||
static void PostInitializationStub();
|
||||
|
||||
static void FrameStub();
|
||||
|
||||
static void TransmitGuids();
|
||||
|
||||
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).");
|
||||
});
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
int workingCount = 0;
|
||||
|
||||
@ -957,7 +957,8 @@ namespace Components
|
||||
++workingCount;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}, Scheduler::Pipeline::MAIN);
|
||||
|
||||
Script::OnVMShutdown([]()
|
||||
{
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Utils::Signal<Scheduler::Callback> Dvar::RegistrationSignal;
|
||||
Utils::Signal<Dvar::Callback> Dvar::RegistrationSignal;
|
||||
const char* Dvar::ArchiveDvarPath = "userraw/archivedvars.cfg";
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void Dvar::OnInit(Utils::Slot<Scheduler::Callback> callback)
|
||||
void Dvar::OnInit(Utils::Slot<Dvar::Callback> callback)
|
||||
{
|
||||
Dvar::RegistrationSignal.connect(callback);
|
||||
}
|
||||
@ -216,16 +216,13 @@ namespace Components
|
||||
Utils::IO::RemoveFile(Dvar::ArchiveDvarPath);
|
||||
}
|
||||
|
||||
Game::dvar_t* Dvar::RegisterName(const char* name, const char* /*default*/, Game::dvar_flag flag, const char* description)
|
||||
Game::dvar_t* Dvar::Dvar_RegisterName(const char* name, const char* /*default*/, unsigned __int16 flags, const char* description)
|
||||
{
|
||||
// Run callbacks
|
||||
Dvar::RegistrationSignal();
|
||||
|
||||
// Name watcher
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
static std::string lastValidName = "Unknown Soldier";
|
||||
std::string name = Dvar::Var("name").get<const char*>();
|
||||
auto name = Dvar::Var("name").get<std::string>();
|
||||
|
||||
// Don't perform any checks if name didn't change
|
||||
if (name == lastValidName) return;
|
||||
@ -241,7 +238,7 @@ namespace Components
|
||||
lastValidName = name;
|
||||
Friends::UpdateName();
|
||||
}
|
||||
}, true);
|
||||
}, Scheduler::MAIN, 3s); // Don't need to do this every frame
|
||||
|
||||
std::string username = "Unknown Soldier";
|
||||
|
||||
@ -255,7 +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)
|
||||
@ -272,7 +269,7 @@ namespace Components
|
||||
"ui_mptype",
|
||||
};
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(exceptions); ++i)
|
||||
for (std::size_t i = 0; i < ARRAYSIZE(exceptions); ++i)
|
||||
{
|
||||
if (Utils::String::ToLower(dvarName) == Utils::String::ToLower(exceptions[i]))
|
||||
{
|
||||
@ -313,6 +310,12 @@ namespace Components
|
||||
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()
|
||||
{
|
||||
// set flags of cg_drawFPS to archive
|
||||
@ -352,7 +355,7 @@ namespace Components
|
||||
Utils::Hook::Xor<BYTE>(0x6312DE, Game::dvar_flag::DVAR_CHEAT);
|
||||
|
||||
// Hook dvar 'name' registration
|
||||
Utils::Hook(0x40531C, Dvar::RegisterName, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x40531C, Dvar::Dvar_RegisterName, HOOK_CALL).install()->quick();
|
||||
|
||||
// un-cheat safeArea_* and add archive flags
|
||||
Utils::Hook::Xor<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>(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
|
||||
Utils::Hook(0x63C897, 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();
|
||||
|
||||
// If the game closed abruptly, the dvars would not have been restored
|
||||
Dvar::OnInit([]
|
||||
{
|
||||
Dvar::ResetDvarsValue();
|
||||
});
|
||||
Dvar::OnInit(Dvar::ResetDvarsValue);
|
||||
}
|
||||
|
||||
Dvar::~Dvar()
|
||||
|
@ -8,8 +8,8 @@ namespace Components
|
||||
class Flag
|
||||
{
|
||||
public:
|
||||
Flag(Game::dvar_flag flag) : val(flag) {};
|
||||
Flag(unsigned __int16 flag) : Flag(static_cast<Game::dvar_flag>(flag)) {};
|
||||
Flag(Game::dvar_flag flag) : val(flag) {}
|
||||
Flag(unsigned __int16 flag) : Flag(static_cast<Game::dvar_flag>(flag)) {}
|
||||
|
||||
Game::dvar_flag val;
|
||||
};
|
||||
@ -17,10 +17,10 @@ namespace Components
|
||||
class Var
|
||||
{
|
||||
public:
|
||||
Var() : dvar(nullptr) {};
|
||||
Var(const Var& obj) { this->dvar = obj.dvar; };
|
||||
Var(Game::dvar_t* _dvar) : dvar(_dvar) {};
|
||||
Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {};
|
||||
Var() : dvar(nullptr) {}
|
||||
Var(const Var& obj) { this->dvar = obj.dvar; }
|
||||
Var(Game::dvar_t* _dvar) : dvar(_dvar) {}
|
||||
Var(DWORD ppdvar) : Var(*reinterpret_cast<Game::dvar_t**>(ppdvar)) {}
|
||||
Var(const std::string& dvarName);
|
||||
|
||||
template<typename T> T get();
|
||||
@ -43,7 +43,9 @@ namespace Components
|
||||
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
|
||||
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();
|
||||
|
||||
private:
|
||||
static Utils::Signal<Scheduler::Callback> RegistrationSignal;
|
||||
static Utils::Signal<Dvar::Callback> RegistrationSignal;
|
||||
static const char* ArchiveDvarPath;
|
||||
|
||||
static Game::dvar_t* RegisterName(const char* name, const char* defaultVal, Game::dvar_flag flag, const char* description);
|
||||
static Game::dvar_t* Dvar_RegisterName(const char* name, const char* defaultVal, unsigned __int16 flags, const char* description);
|
||||
|
||||
static void SetFromStringByNameExternal(const char* dvar, const char* value);
|
||||
static void SetFromStringByNameSafeExternal(const char* dvar, const char* value);
|
||||
|
||||
static void SaveArchiveDvar(const Game::dvar_t* var);
|
||||
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\\dlc\\");
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]()
|
||||
{
|
||||
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) };
|
||||
|
||||
std::uint32_t FFTotalSize = *reinterpret_cast<std::uint32_t*>(0x10AA5D8);
|
||||
std::uint32_t FFCurrentOffset = *reinterpret_cast<std::uint32_t*>(0x10AA608);
|
||||
auto FFTotalSize = *reinterpret_cast<std::uint32_t*>(0x10AA5D8);
|
||||
auto FFCurrentOffset = *reinterpret_cast<std::uint32_t*>(0x10AA608);
|
||||
|
||||
float fastfileLoadProgress = (float(FFCurrentOffset) / float(FFTotalSize)) * 100.0f;
|
||||
if (fastfileLoadProgress == INFINITY)
|
||||
if (isinf(fastfileLoadProgress))
|
||||
{
|
||||
fastfileLoadProgress = 100.0f;
|
||||
}
|
||||
else if (fastfileLoadProgress == NAN)
|
||||
else if (isnan(fastfileLoadProgress))
|
||||
{
|
||||
fastfileLoadProgress = 0.0f;
|
||||
}
|
||||
|
||||
Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s [%0.1f%%]", FastFiles::Current().data(), fastfileLoadProgress), 0x7FFFFFFF, font, 5.0f, static_cast<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)
|
||||
{
|
||||
|
@ -653,7 +653,7 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
static Utils::Time::Interval timeInterval;
|
||||
static Utils::Time::Interval sortInterval;
|
||||
@ -692,11 +692,11 @@ namespace Components
|
||||
Friends::SortList(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, Scheduler::Pipeline::CLIENT);
|
||||
|
||||
UIFeeder::Add(61.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend);
|
||||
|
||||
Scheduler::OnShutdown([]()
|
||||
Scheduler::OnGameShutdown([]
|
||||
{
|
||||
Friends::ClearPresence("iw4x_server");
|
||||
Friends::ClearPresence("iw4x_playing");
|
||||
|
@ -245,7 +245,7 @@ namespace Components
|
||||
|
||||
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(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick();
|
||||
|
@ -879,11 +879,11 @@ namespace Components
|
||||
|
||||
Command::Add("delayReconnect", [](Command::Params*)
|
||||
{
|
||||
Scheduler::OnDelay([]()
|
||||
Scheduler::Once([]
|
||||
{
|
||||
Command::Execute("closemenu popup_reconnectingtoparty", false);
|
||||
Command::Execute("reconnect", false);
|
||||
}, 10s, true);
|
||||
}, Scheduler::Pipeline::CLIENT, 10s);
|
||||
});
|
||||
|
||||
if(Dedicated::IsEnabled())
|
||||
@ -908,9 +908,9 @@ namespace Components
|
||||
// Allow hiding specific smodels
|
||||
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
Game::GfxWorld*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||
auto*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||
if (!Game::CL_IsCgameInitialized() || !gameWorld || !Dvar::Var("r_listSModels").get<bool>()) return;
|
||||
|
||||
std::map<std::string, int> models;
|
||||
@ -926,16 +926,16 @@ namespace Components
|
||||
}
|
||||
|
||||
Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0);
|
||||
int height = Game::R_TextHeight(font);
|
||||
float scale = 0.75;
|
||||
float color[4] = { 0, 1.0f, 0, 1.0f };
|
||||
auto height = Game::R_TextHeight(font);
|
||||
auto scale = 0.75f;
|
||||
float color[4] = {0.0f, 1.0f, 0.0f, 1.0f};
|
||||
|
||||
unsigned int i = 0;
|
||||
for (auto& model : models)
|
||||
{
|
||||
Game::R_AddCmdDrawText(Utils::String::VA("%d %s", model.second, model.first.data()), 0x7FFFFFFF, font, 15.0f, (height * scale + 1) * (i++ + 1) + 15.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
|
||||
}
|
||||
}, true);
|
||||
}, Scheduler::Pipeline::RENDERER);
|
||||
}
|
||||
|
||||
Maps::~Maps()
|
||||
|
@ -320,11 +320,11 @@ namespace Components
|
||||
size_t i = 0;
|
||||
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());
|
||||
Session::Send(address, "nodeListResponse", nodeListData);
|
||||
}, NODE_SEND_RATE * i++);
|
||||
}, Scheduler::Pipeline::MAIN, NODE_SEND_RATE * i++);
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,12 +339,13 @@ namespace Components
|
||||
if (ZoneBuilder::IsEnabled()) return;
|
||||
Dvar::Register<bool>("net_natFix", false, 0, "Fix node registration for certain firewalls/routers");
|
||||
|
||||
Scheduler::OnFrameAsync([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
Node::StoreNodes(false);
|
||||
});
|
||||
|
||||
Scheduler::OnFrame(Node::RunFrame);
|
||||
Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::MAIN);
|
||||
|
||||
Session::Handle("nodeListResponse", Node::HandleResponse);
|
||||
Session::Handle("nodeListRequest", [](Network::Address address, const std::string&)
|
||||
{
|
||||
|
@ -278,7 +278,7 @@ namespace Components
|
||||
Party::Connect(Party::Container.target);
|
||||
});
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
if (Party::Container.valid)
|
||||
{
|
||||
@ -297,7 +297,8 @@ namespace Components
|
||||
Party::ConnectError("Playlist request timed out.");
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
}, Scheduler::Pipeline::CLIENT);
|
||||
|
||||
// Basic info handler
|
||||
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();
|
||||
|
||||
// Make sure preDestroy is called when the game shuts down
|
||||
Scheduler::OnShutdown(Loader::PreDestroy);
|
||||
Scheduler::OnGameShutdown(Loader::PreDestroy);
|
||||
|
||||
// protocol version (workaround for hacks)
|
||||
Utils::Hook::Set<int>(0x4FB501, PROTOCOL);
|
||||
@ -469,10 +469,10 @@ namespace Components
|
||||
|
||||
// Fix mouse lag
|
||||
Utils::Hook::Nop(0x4731F5, 8);
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
SetThreadExecutionState(ES_DISPLAY_REQUIRED);
|
||||
});
|
||||
}, Scheduler::Pipeline::RENDERER);
|
||||
|
||||
// Fix mouse pitch adjustments
|
||||
Dvar::Register<bool>("ui_mousePitch", false, Game::DVAR_ARCHIVE, "");
|
||||
@ -709,13 +709,13 @@ namespace Components
|
||||
// Constantly draw the mini console
|
||||
Utils::Hook::Set<BYTE>(0x412A45, 0xEB);
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]()
|
||||
{
|
||||
if (*reinterpret_cast<Game::Font_s**>(0x62E4BAC))
|
||||
{
|
||||
Game::Con_DrawMiniConsole(0, 2, 4, (Game::CL_IsCgameInitialized() ? 1.0f : 0.4f));
|
||||
}
|
||||
}, true);
|
||||
}, Scheduler::Pipeline::RENDERER);
|
||||
#else
|
||||
// Remove missing tag message
|
||||
Utils::Hook::Nop(0x4EBF1A, 5);
|
||||
|
@ -5,8 +5,8 @@ namespace Components
|
||||
Utils::Signal<Renderer::BackendCallback> Renderer::BackendFrameSignal;
|
||||
Utils::Signal<Renderer::BackendCallback> Renderer::SingleBackendFrameSignal;
|
||||
|
||||
Utils::Signal<Scheduler::Callback> Renderer::EndRecoverDeviceSignal;
|
||||
Utils::Signal<Scheduler::Callback> Renderer::BeginRecoverDeviceSignal;
|
||||
Utils::Signal<Renderer::Callback> Renderer::EndRecoverDeviceSignal;
|
||||
Utils::Signal<Renderer::Callback> Renderer::BeginRecoverDeviceSignal;
|
||||
|
||||
Dvar::Var Renderer::r_drawTriggers;
|
||||
Dvar::Var Renderer::r_drawSceneModelCollisions;
|
||||
@ -32,19 +32,6 @@ namespace Components
|
||||
float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
|
||||
float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
|
||||
|
||||
__declspec(naked) void Renderer::FrameStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
pushad
|
||||
call Scheduler::FrameHandler
|
||||
popad
|
||||
|
||||
push 5AC950h
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Renderer::BackendFrameStub()
|
||||
{
|
||||
__asm
|
||||
@ -87,12 +74,12 @@ namespace Components
|
||||
Renderer::BackendFrameSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnDeviceRecoveryEnd(Utils::Slot<Scheduler::Callback> callback)
|
||||
void Renderer::OnDeviceRecoveryEnd(Utils::Slot<Renderer::Callback> 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);
|
||||
}
|
||||
@ -463,7 +450,8 @@ namespace Components
|
||||
{
|
||||
if (Dedicated::IsEnabled()) return;
|
||||
|
||||
Scheduler::OnFrame([]() {
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
{
|
||||
DebugDrawAABBTrees();
|
||||
@ -472,35 +460,7 @@ namespace Components
|
||||
DebugDrawSceneModelCollisions();
|
||||
DebugDrawTriggers();
|
||||
}
|
||||
});
|
||||
|
||||
// Renderer::OnBackendFrame([] (IDirect3DDevice9* device)
|
||||
// {
|
||||
// if (Game::Sys_Milliseconds() % 2)
|
||||
// {
|
||||
// device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0);
|
||||
// }
|
||||
//
|
||||
// return;
|
||||
//
|
||||
// IDirect3DSurface9* buffer = nullptr;
|
||||
//
|
||||
// device->CreateOffscreenPlainSurface(Renderer::Width(), Renderer::Height(), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &buffer, nullptr);
|
||||
// device->GetFrontBufferData(0, buffer);
|
||||
//
|
||||
// if (buffer)
|
||||
// {
|
||||
// D3DSURFACE_DESC desc;
|
||||
// D3DLOCKED_RECT lockedRect;
|
||||
//
|
||||
// buffer->GetDesc(&desc);
|
||||
//
|
||||
// HRESULT res = buffer->LockRect(&lockedRect, NULL, D3DLOCK_READONLY);
|
||||
//
|
||||
//
|
||||
// buffer->UnlockRect();
|
||||
// }
|
||||
// });
|
||||
}, Scheduler::Pipeline::RENDERER);
|
||||
|
||||
// Log broken materials
|
||||
Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick();
|
||||
@ -510,9 +470,6 @@ namespace Components
|
||||
Utils::Hook::Set(0x005086DA, "^3solid^7");
|
||||
Utils::Hook(0x00580F53, Renderer::DrawTechsetForMaterial, HOOK_CALL).install()->quick();
|
||||
|
||||
// Frame hook
|
||||
Utils::Hook(0x5ACB99, Renderer::FrameStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Begin device recovery (not D3D9Ex)
|
||||
|
@ -6,6 +6,7 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
typedef void(BackendCallback)(IDirect3DDevice9*);
|
||||
typedef void(Callback)();
|
||||
|
||||
Renderer();
|
||||
~Renderer();
|
||||
@ -15,12 +16,11 @@ namespace Components
|
||||
|
||||
static void OnBackendFrame(Utils::Slot<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:
|
||||
static void FrameStub();
|
||||
|
||||
static void BackendFrameStub();
|
||||
static void BackendFrameHandler();
|
||||
|
||||
@ -40,8 +40,8 @@ namespace Components
|
||||
static void DebugDrawModelNames();
|
||||
static void DebugDrawAABBTrees();
|
||||
|
||||
static Utils::Signal<Scheduler::Callback> EndRecoverDeviceSignal;
|
||||
static Utils::Signal<Scheduler::Callback> BeginRecoverDeviceSignal;
|
||||
static Utils::Signal<Renderer::Callback> EndRecoverDeviceSignal;
|
||||
static Utils::Signal<Renderer::Callback> BeginRecoverDeviceSignal;
|
||||
|
||||
static Utils::Signal<BackendCallback> BackendFrameSignal;
|
||||
static Utils::Signal<BackendCallback> SingleBackendFrameSignal;
|
||||
|
@ -1,162 +1,193 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
constexpr bool COND_CONTINUE = false;
|
||||
constexpr bool COND_END = true;
|
||||
|
||||
namespace Components
|
||||
{
|
||||
bool Scheduler::AsyncTerminate;
|
||||
std::thread Scheduler::AsyncThread;
|
||||
std::thread Scheduler::Thread;
|
||||
volatile bool Scheduler::Kill = false;
|
||||
Scheduler::TaskPipeline Scheduler::Pipelines[Pipeline::COUNT];
|
||||
|
||||
bool Scheduler::ReadyPassed = false;
|
||||
Utils::Signal<Scheduler::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)
|
||||
void Scheduler::TaskPipeline::add(Task&& task)
|
||||
{
|
||||
if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return;
|
||||
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())
|
||||
newCallbacks_.access([&task](taskList& tasks)
|
||||
{
|
||||
Scheduler::Once(Scheduler::ReadyHandler);
|
||||
}
|
||||
else
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
}
|
||||
|
||||
void Scheduler::TaskPipeline::execute()
|
||||
{
|
||||
callbacks_.access([&](taskList& tasks)
|
||||
{
|
||||
Scheduler::ReadyPassed = true;
|
||||
Scheduler::ReadySignal();
|
||||
Scheduler::ReadySignal.clear();
|
||||
}
|
||||
}
|
||||
this->mergeCallbacks();
|
||||
|
||||
void Scheduler::FrameHandler()
|
||||
{
|
||||
Scheduler::DelaySignal();
|
||||
Scheduler::FrameSignal();
|
||||
|
||||
Utils::Signal<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))
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
signal.connect(i->callback);
|
||||
i = Scheduler::DelayedSlots.erase(i);
|
||||
continue;
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - i->lastCall;
|
||||
|
||||
if (diff < i->interval)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->lastCall = now;
|
||||
|
||||
const auto res = i->handler();
|
||||
if (res == COND_END)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Scheduler::TaskPipeline::mergeCallbacks()
|
||||
{
|
||||
callbacks_.access([&](taskList& tasks)
|
||||
{
|
||||
newCallbacks_.access([&](taskList& new_tasks)
|
||||
{
|
||||
tasks.insert(tasks.end(), std::move_iterator<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;
|
||||
}
|
||||
|
||||
signal();
|
||||
return COND_CONTINUE;
|
||||
}, Pipeline::MAIN); // Once Com_Frame_Try_Block_Function is called we know the game is 'ready'
|
||||
}
|
||||
|
||||
void Scheduler::ShutdownStub(int num)
|
||||
void Scheduler::OnGameShutdown(const std::function<void()>& callback)
|
||||
{
|
||||
Scheduler::ShutdownSignal();
|
||||
Utils::Hook::Call<void(int)>(0x46B370)(num);
|
||||
}
|
||||
|
||||
void Scheduler::OnFrameAsync(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Scheduler::AsyncFrameSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Scheduler::OnceAsync(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Scheduler::AsyncFrameOnceSignal.connect(callback);
|
||||
Schedule([callback]
|
||||
{
|
||||
callback();
|
||||
return COND_END;
|
||||
}, Pipeline::QUIT, 0ms);
|
||||
}
|
||||
|
||||
Scheduler::Scheduler()
|
||||
{
|
||||
Scheduler::ReadyPassed = false;
|
||||
Scheduler::Once(Scheduler::ReadyHandler);
|
||||
|
||||
Utils::Hook(0x4D697A, Scheduler::ShutdownStub, HOOK_CALL).install()->quick();
|
||||
|
||||
if (!Loader::IsPerformingUnitTests())
|
||||
Thread = Utils::Thread::createNamedThread("Async Scheduler", []
|
||||
{
|
||||
Scheduler::AsyncTerminate = false;
|
||||
Scheduler::AsyncThread = std::thread([]()
|
||||
while (!Kill)
|
||||
{
|
||||
while (!Scheduler::AsyncTerminate)
|
||||
{
|
||||
Scheduler::AsyncFrameSignal();
|
||||
Execute(Pipeline::ASYNC);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
});
|
||||
|
||||
Utils::Signal<Scheduler::Callback> copy(Scheduler::AsyncFrameOnceSignal);
|
||||
Scheduler::AsyncFrameOnceSignal.clear();
|
||||
copy();
|
||||
Utils::Hook(0x4DBE9A, REndFrame_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x518D5C, REndFrame_Hk, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x5ACBA3, REndFrame_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
std::this_thread::sleep_for(16ms);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Hook G_Glass_Update so we may fix TLS issues
|
||||
Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
Scheduler::~Scheduler()
|
||||
{
|
||||
Scheduler::ReadySignal.clear();
|
||||
Scheduler::ShutdownSignal.clear();
|
||||
// CL_CheckTimeout
|
||||
Utils::Hook(0x4B0F81, ClientFrame_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
Scheduler::FrameSignal.clear();
|
||||
Scheduler::FrameOnceSignal.clear();
|
||||
Scheduler::DelayedSlots.clear();
|
||||
// Com_Frame_Try_Block_Function
|
||||
Utils::Hook(0x4B724F, MainFrame_Hk, HOOK_CALL).install()->quick();
|
||||
|
||||
Scheduler::AsyncFrameSignal.clear();
|
||||
Scheduler::AsyncFrameOnceSignal.clear();
|
||||
|
||||
Scheduler::ReadyPassed = false;
|
||||
// Sys_Quit
|
||||
Utils::Hook(0x4D697A, SysSetBlockSystemHotkeys_Hk, HOOK_CALL).install()->quick();
|
||||
}
|
||||
|
||||
void Scheduler::preDestroy()
|
||||
{
|
||||
Scheduler::AsyncTerminate = true;
|
||||
if (Scheduler::AsyncThread.joinable())
|
||||
Kill = true;
|
||||
if (Thread.joinable())
|
||||
{
|
||||
Scheduler::AsyncThread.join();
|
||||
Thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,50 +5,64 @@ namespace Components
|
||||
class Scheduler : public Component
|
||||
{
|
||||
public:
|
||||
typedef void(Callback)();
|
||||
enum Pipeline
|
||||
{
|
||||
ASYNC,
|
||||
RENDERER,
|
||||
SERVER,
|
||||
CLIENT,
|
||||
MAIN,
|
||||
QUIT,
|
||||
COUNT,
|
||||
};
|
||||
|
||||
Scheduler();
|
||||
~Scheduler();
|
||||
|
||||
void preDestroy() override;
|
||||
|
||||
static void OnShutdown(Utils::Slot<Callback> callback);
|
||||
static void OnFrame(Utils::Slot<Callback> callback, bool clientOnly = false);
|
||||
static void OnReady(Utils::Slot<Callback> callback, bool clientOnly = false);
|
||||
static void Once(Utils::Slot<Callback> callback, bool clientOnly = false);
|
||||
static void OnDelay(Utils::Slot<Callback> callback, std::chrono::nanoseconds delay, bool clientOnly = false);
|
||||
|
||||
static void OnFrameAsync(Utils::Slot<Callback> callback);
|
||||
static void OnceAsync(Utils::Slot<Callback> callback);
|
||||
|
||||
static void FrameHandler();
|
||||
static void Schedule(const std::function<bool()>& callback, Pipeline type = Pipeline::ASYNC,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
static void Loop(const std::function<void()>& callback, Pipeline type = Pipeline::ASYNC,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
static void Once(const std::function<void()>& callback, Pipeline type = Pipeline::ASYNC,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
static void OnGameInitialized(const std::function<void()>& callback, Pipeline type = Pipeline::ASYNC,
|
||||
std::chrono::milliseconds delay = 0ms);
|
||||
static void OnGameShutdown(const std::function<void()>& callback);
|
||||
|
||||
private:
|
||||
class DelayedSlot
|
||||
struct Task
|
||||
{
|
||||
public:
|
||||
std::chrono::nanoseconds delay;
|
||||
Utils::Time::Interval interval;
|
||||
Utils::Slot<Callback> callback;
|
||||
std::function<bool()> handler{};
|
||||
std::chrono::milliseconds interval{};
|
||||
std::chrono::high_resolution_clock::time_point lastCall{};
|
||||
};
|
||||
|
||||
static bool AsyncTerminate;
|
||||
static std::thread AsyncThread;
|
||||
using taskList = std::vector<Task>;
|
||||
|
||||
static Utils::Signal<Callback> FrameSignal;
|
||||
static Utils::Signal<Callback> FrameOnceSignal;
|
||||
static std::vector<DelayedSlot> DelayedSlots;
|
||||
class TaskPipeline
|
||||
{
|
||||
public:
|
||||
void add(Task&& task);
|
||||
void execute();
|
||||
|
||||
static bool ReadyPassed;
|
||||
static Utils::Signal<Callback> ReadySignal;
|
||||
static Utils::Signal<Callback> ShutdownSignal;
|
||||
private:
|
||||
Utils::Concurrency::Container<taskList> newCallbacks_;
|
||||
Utils::Concurrency::Container<taskList, std::recursive_mutex> callbacks_;
|
||||
|
||||
static Utils::Signal<Callback> AsyncFrameSignal;
|
||||
static Utils::Signal<Callback> AsyncFrameOnceSignal;
|
||||
void mergeCallbacks();
|
||||
};
|
||||
|
||||
static void ReadyHandler();
|
||||
static void DelaySignal();
|
||||
static volatile bool Kill;
|
||||
static std::thread Thread;
|
||||
static TaskPipeline Pipelines[];
|
||||
|
||||
static void ShutdownStub(int num);
|
||||
static void Execute(Pipeline type);
|
||||
|
||||
static void REndFrame_Hk();
|
||||
static void ServerFrame_Hk();
|
||||
static void ClientFrame_Hk(int localClientNum);
|
||||
static void MainFrame_Hk();
|
||||
static void SysSetBlockSystemHotkeys_Hk(int block);
|
||||
};
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace Components
|
||||
const char* Script::ReplacedPos = nullptr;
|
||||
int Script::LastFrameTime = -1;
|
||||
|
||||
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
||||
Utils::Signal<Script::Callback> Script::VMShutdownSignal;
|
||||
|
||||
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::VMShutdownSignal.connect(std::move(callback));
|
||||
@ -718,7 +718,7 @@ namespace Components
|
||||
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_LoadGame
|
||||
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick(); // G_ShutdownGame
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]()
|
||||
{
|
||||
if (!Game::SV_Loaded())
|
||||
return;
|
||||
@ -735,7 +735,7 @@ namespace Components
|
||||
}
|
||||
|
||||
Script::LastFrameTime = nowMs;
|
||||
});
|
||||
}, Scheduler::Pipeline::SERVER);
|
||||
|
||||
#ifdef _DEBUG
|
||||
Script::AddFunction("DebugBox", []
|
||||
|
@ -9,12 +9,14 @@ namespace Components
|
||||
Script();
|
||||
~Script();
|
||||
|
||||
typedef void(Callback)();
|
||||
|
||||
static int LoadScriptAndLabel(const std::string& script, const std::string& label);
|
||||
|
||||
static void AddFunction(const char* name, Game::BuiltinFunction func, int type = 0);
|
||||
static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0);
|
||||
|
||||
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback);
|
||||
static void OnVMShutdown(Utils::Slot<Script::Callback> callback);
|
||||
|
||||
static Game::client_t* GetClient(const Game::gentity_t* gentity);
|
||||
|
||||
@ -31,7 +33,7 @@ namespace Components
|
||||
static const char* ReplacedPos;
|
||||
static int LastFrameTime;
|
||||
|
||||
static Utils::Signal<Scheduler::Callback> VMShutdownSignal;
|
||||
static Utils::Signal<Script::Callback> VMShutdownSignal;
|
||||
|
||||
static void CompileError(unsigned int offset, const char* message, ...);
|
||||
static void PrintSourcePos(const char* filename, unsigned int offset);
|
||||
|
@ -922,7 +922,7 @@ namespace Components
|
||||
UIScript::AddOwnerDraw(253, ServerList::UpdateGameType);
|
||||
|
||||
// Add frame callback
|
||||
Scheduler::OnFrame(ServerList::Frame);
|
||||
Scheduler::OnGameInitialized(ServerList::Frame, Scheduler::Pipeline::CLIENT);
|
||||
}
|
||||
|
||||
ServerList::~ServerList()
|
||||
|
@ -33,11 +33,11 @@ namespace Components
|
||||
|
||||
Network::SendCommand(target, command, data);
|
||||
|
||||
Scheduler::OnDelay([delayData]()
|
||||
Scheduler::Once([delayData]()
|
||||
{
|
||||
Network::SendCommand(delayData->target, delayData->command, delayData->data);
|
||||
delete delayData;
|
||||
}, 500ms + std::chrono::milliseconds(rand() % 200));
|
||||
}, Scheduler::Pipeline::MAIN, 500ms + std::chrono::milliseconds(rand() % 200));
|
||||
#else
|
||||
std::lock_guard<std::recursive_mutex> _(Session::Mutex);
|
||||
|
||||
|
@ -148,10 +148,7 @@ namespace Components
|
||||
{
|
||||
if (Dedicated::IsEnabled() || Monitor::IsEnabled()) return;
|
||||
|
||||
Scheduler::OnReady([]()
|
||||
{
|
||||
Scheduler::OnFrame(Toast::Handler);
|
||||
});
|
||||
Scheduler::OnGameInitialized(Toast::Handler, Scheduler::Pipeline::RENDERER);
|
||||
|
||||
Command::Add("testtoast", [](Command::Params*)
|
||||
{
|
||||
|
@ -178,7 +178,7 @@ namespace Components
|
||||
Utils::Hook(0x48E5D3, Window::DrawCursorStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Draw the cursor if necessary
|
||||
Scheduler::OnFrame([]()
|
||||
Scheduler::Loop([]
|
||||
{
|
||||
if (Window::NativeCursor.get<bool>() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow))
|
||||
{
|
||||
@ -198,7 +198,7 @@ namespace Components
|
||||
|
||||
Window::CursorVisible = FALSE;
|
||||
}
|
||||
});
|
||||
}, Scheduler::Pipeline::RENDERER);
|
||||
|
||||
// Don't let the game interact with the native cursor
|
||||
Utils::Hook::Set(0x6D7348, Window::ShowCursorHook);
|
||||
|
@ -873,10 +873,10 @@ namespace Game
|
||||
typedef void(__cdecl * Sys_FreeFileList_t)(char** list);
|
||||
extern Sys_FreeFileList_t Sys_FreeFileList;
|
||||
|
||||
typedef bool(__cdecl * Sys_IsDatabaseReady_t)();
|
||||
typedef int(__cdecl * Sys_IsDatabaseReady_t)();
|
||||
extern Sys_IsDatabaseReady_t Sys_IsDatabaseReady;
|
||||
|
||||
typedef bool(__cdecl * Sys_IsDatabaseReady2_t)();
|
||||
typedef int(__cdecl * Sys_IsDatabaseReady2_t)();
|
||||
extern Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2;
|
||||
|
||||
typedef bool(__cdecl * Sys_IsMainThread_t)();
|
||||
|
@ -353,7 +353,7 @@ namespace Game
|
||||
const char** argv[8];
|
||||
};
|
||||
|
||||
static_assert(sizeof(CmdArgs) == 132);
|
||||
static_assert(sizeof(CmdArgs) == 0x84);
|
||||
|
||||
typedef struct cmd_function_s
|
||||
{
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define VLD_FORCE_ENABLE
|
||||
//#include <vld.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#include <timeapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <Wininet.h>
|
||||
@ -29,7 +29,7 @@
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4091)
|
||||
#pragma warning(disable: 4244)
|
||||
#include <dbghelp.h>
|
||||
#include <DbgHelp.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
@ -45,13 +45,14 @@
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#include <d3dx9tex.h>
|
||||
#pragma comment(lib, "D3dx9.lib")
|
||||
|
||||
#include <Xinput.h>
|
||||
#include <XInput.h>
|
||||
#pragma comment (lib, "xinput.lib")
|
||||
|
||||
// Ignore the warnings
|
||||
@ -63,7 +64,6 @@
|
||||
#pragma warning(disable: 4389)
|
||||
#pragma warning(disable: 4702)
|
||||
#pragma warning(disable: 4800)
|
||||
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
|
||||
#pragma warning(disable: 5054)
|
||||
#pragma warning(disable: 6001)
|
||||
#pragma warning(disable: 6011)
|
||||
@ -74,22 +74,36 @@
|
||||
#pragma warning(disable: 6387)
|
||||
#pragma warning(disable: 26812)
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <curses.h>
|
||||
#include <mongoose.h>
|
||||
#include <gsl/gsl>
|
||||
#include <json11.hpp>
|
||||
#include <tomcrypt.h>
|
||||
#include <mongoose.h>
|
||||
#include <udis86.h>
|
||||
#include <zlib.h>
|
||||
|
||||
// Enable additional literals
|
||||
using namespace std::literals;
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#define AssertSize(x, size) \
|
||||
static_assert(sizeof(x) == (size), \
|
||||
"Structure has an invalid size. " #x " must be " #size " bytes")
|
||||
|
||||
#define AssertOffset(x, y, offset) \
|
||||
static_assert(offsetof(x, y) == (offset), \
|
||||
#x "::" #y " is not at the right offset. Must be at " #offset)
|
||||
|
||||
#define AssertCount(expr, count) \
|
||||
assert((#expr " doesn't index " #count, (expr) < (count)))
|
||||
|
||||
// Protobuf
|
||||
#include "proto/session.pb.h"
|
||||
#include "proto/party.pb.h"
|
||||
@ -101,28 +115,31 @@
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#include "Utils/IO.hpp"
|
||||
#include "Utils/CSV.hpp"
|
||||
#include "Utils/Time.hpp"
|
||||
#include "Utils/Memory.hpp" // Breaks order on purpose
|
||||
|
||||
#include "Utils/Cache.hpp"
|
||||
#include "Utils/Chain.hpp"
|
||||
#include "Utils/Compression.hpp"
|
||||
#include "Utils/Concurrency.hpp"
|
||||
#include "Utils/Cryptography.hpp"
|
||||
#include "Utils/CSV.hpp"
|
||||
#include "Utils/Entities.hpp"
|
||||
#include "Utils/Hooking.hpp"
|
||||
#include "Utils/InfoString.hpp"
|
||||
#include "Utils/IO.hpp"
|
||||
#include "Utils/Library.hpp"
|
||||
#include "Utils/String.hpp"
|
||||
#include "Utils/Thread.hpp"
|
||||
#include "Utils/Time.hpp"
|
||||
#include "Utils/Utils.hpp"
|
||||
#include "Utils/WebIO.hpp"
|
||||
#include "Utils/Memory.hpp"
|
||||
#include "Utils/String.hpp"
|
||||
#include "Utils/Hooking.hpp"
|
||||
#include "Utils/Library.hpp"
|
||||
#include "Utils/Entities.hpp"
|
||||
#include "Utils/InfoString.hpp"
|
||||
#include "Utils/Compression.hpp"
|
||||
#include "Utils/Cryptography.hpp"
|
||||
|
||||
#include "Steam/Steam.hpp"
|
||||
#include "Steam/Steam.hpp" // Some definitions are used in functions and structs
|
||||
|
||||
#include "Game/Structs.hpp"
|
||||
#include "Game/Functions.hpp"
|
||||
|
||||
#include "Utils/Stream.hpp"
|
||||
#include "Utils/Stream.hpp" // Breaks order on purpose
|
||||
|
||||
#include "Components/Loader.hpp"
|
||||
|
||||
@ -139,20 +156,11 @@
|
||||
#pragma comment(lib, "dbghelp.lib")
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
// Enable additional literals
|
||||
using namespace std::literals;
|
||||
|
||||
#endif
|
||||
|
||||
#define STRINGIZE_(x) #x
|
||||
#define STRINGIZE(x) STRINGIZE_(x)
|
||||
|
||||
#define BASEGAME "iw4x"
|
||||
#define CLIENT_CONFIG "iw4x_config.cfg"
|
||||
|
||||
#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.")
|
||||
#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.")
|
||||
|
||||
// Resource stuff
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
|
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);
|
||||
}
|
||||
|
||||
Library::Library(const std::string& name, bool _freeOnDestroy) : _module(nullptr), freeOnDestroy(_freeOnDestroy)
|
||||
Library::Library(const std::string& name, bool _freeOnDestroy) : module_(nullptr), freeOnDestroy(_freeOnDestroy)
|
||||
{
|
||||
this->_module = LoadLibraryExA(name.data(), nullptr, 0);
|
||||
this->module_ = LoadLibraryExA(name.data(), nullptr, 0);
|
||||
}
|
||||
|
||||
Library::Library(const HMODULE handle)
|
||||
{
|
||||
this->_module = handle;
|
||||
this->module_ = handle;
|
||||
this->freeOnDestroy = true;
|
||||
}
|
||||
|
||||
@ -38,23 +38,38 @@ namespace Utils
|
||||
}
|
||||
}
|
||||
|
||||
bool Library::operator==(const Library& obj) const
|
||||
{
|
||||
return this->module_ == obj.module_;
|
||||
}
|
||||
|
||||
Library::operator bool() const
|
||||
{
|
||||
return this->isValid();
|
||||
}
|
||||
|
||||
Library::operator HMODULE() const
|
||||
{
|
||||
return this->getModule();
|
||||
}
|
||||
|
||||
bool Library::isValid() const
|
||||
{
|
||||
return this->_module != nullptr;
|
||||
return this->module_ != nullptr;
|
||||
}
|
||||
|
||||
HMODULE Library::getModule() const
|
||||
{
|
||||
return this->_module;
|
||||
return this->module_;
|
||||
}
|
||||
|
||||
void Library::free()
|
||||
{
|
||||
if (this->isValid())
|
||||
{
|
||||
FreeLibrary(this->_module);
|
||||
FreeLibrary(this->module_);
|
||||
}
|
||||
|
||||
this->_module = nullptr;
|
||||
this->module_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,18 @@ namespace Utils
|
||||
static Library Load(const std::filesystem::path& path);
|
||||
static Library GetByAddress(void* address);
|
||||
|
||||
Library() : _module(nullptr), freeOnDestroy(false) {};
|
||||
Library() : module_(nullptr), freeOnDestroy(false) {};
|
||||
Library(const std::string& name, bool freeOnDestroy);
|
||||
explicit Library(const std::string& name) : _module(GetModuleHandleA(name.data())), freeOnDestroy(true) {};
|
||||
explicit Library(const std::string& name) : module_(GetModuleHandleA(name.data())), freeOnDestroy(true) {};
|
||||
explicit Library(HMODULE handle);
|
||||
~Library();
|
||||
|
||||
bool operator!=(const Library& obj) const { return !(*this == obj); }
|
||||
bool operator==(const Library& obj) const;
|
||||
|
||||
operator bool() const;
|
||||
operator HMODULE() const;
|
||||
|
||||
bool isValid() const;
|
||||
HMODULE getModule() const;
|
||||
|
||||
@ -22,7 +28,7 @@ namespace Utils
|
||||
T getProc(const std::string& process) const
|
||||
{
|
||||
if (!this->isValid()) T{};
|
||||
return reinterpret_cast<T>(GetProcAddress(this->_module, process.data()));
|
||||
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -59,7 +65,7 @@ namespace Utils
|
||||
void free();
|
||||
|
||||
private:
|
||||
HMODULE _module;
|
||||
HMODULE module_;
|
||||
bool freeOnDestroy;
|
||||
};
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace Utils
|
||||
|
||||
this->refMemory.clear();
|
||||
|
||||
for (auto& data : this->pool)
|
||||
for (const auto& data : this->pool)
|
||||
{
|
||||
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