Attempt to refactor scheduler *gone insane*

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

3
.gitmodules vendored
View File

@ -32,3 +32,6 @@
[submodule "deps/dxsdk"]
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

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

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

@ -0,0 +1,19 @@
gsl = {
source = path.join(dependencies.basePath, "GSL"),
}
function gsl.import()
gsl.includes()
end
function gsl.includes()
includedirs {
path.join(gsl.source, "include")
}
end
function gsl.project()
end
table.insert(dependencies, gsl)

View File

@ -5,8 +5,8 @@ namespace Components
class Component
{
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

View File

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

View File

@ -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::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();

View File

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

View File

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

View File

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

View File

@ -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()*/)
{

View File

@ -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();
}
});
}, 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();
}
}
});
}, Scheduler::Pipeline::SERVER, 15s);
}
}

View File

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

View File

@ -924,7 +924,7 @@ namespace Components
Dvar::Register<bool>("mod_force_download_server", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches).");
});
Scheduler::OnFrame([]()
Scheduler::Loop([]
{
int workingCount = 0;
@ -957,7 +957,8 @@ namespace Components
++workingCount;
}
}
});
}, Scheduler::Pipeline::MAIN);
Script::OnVMShutdown([]()
{

View File

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

View File

@ -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();
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
newCallbacks_.access([&task](taskList& tasks)
{
tasks.emplace_back(std::move(task));
});
}
void Scheduler::OnShutdown(Utils::Slot<Scheduler::Callback> callback)
void Scheduler::TaskPipeline::execute()
{
Scheduler::ShutdownSignal.connect(callback);
}
callbacks_.access([&](taskList& tasks)
{
this->mergeCallbacks();
void Scheduler::OnFrame(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
for (auto i = tasks.begin(); i != tasks.end();)
{
if (clientOnly && (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())) return;
Scheduler::FrameSignal.connect(callback);
}
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - i->lastCall;
void Scheduler::OnReady(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
if (diff < i->interval)
{
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);
}
else
{
Scheduler::ReadyPassed = true;
Scheduler::ReadySignal();
Scheduler::ReadySignal.clear();
}
}
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))
{
signal.connect(i->callback);
i = Scheduler::DelayedSlots.erase(i);
++i;
continue;
}
i->lastCall = now;
const auto res = i->handler();
if (res == COND_END)
{
i = tasks.erase(i);
}
else
{
++i;
}
signal();
}
});
}
void Scheduler::ShutdownStub(int num)
void Scheduler::TaskPipeline::mergeCallbacks()
{
Scheduler::ShutdownSignal();
Utils::Hook::Call<void(int)>(0x46B370)(num);
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::OnFrameAsync(Utils::Slot<Scheduler::Callback> callback)
void Scheduler::Execute(Pipeline type)
{
Scheduler::AsyncFrameSignal.connect(callback);
AssertCount(type, Pipeline::COUNT);
Pipelines[type].execute();
}
void Scheduler::OnceAsync(Utils::Slot<Scheduler::Callback> callback)
void Scheduler::REndFrame_Hk()
{
Scheduler::AsyncFrameOnceSignal.connect(callback);
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;
}
return COND_CONTINUE;
}, Pipeline::MAIN); // Once Com_Frame_Try_Block_Function is called we know the game is 'ready'
}
void Scheduler::OnGameShutdown(const std::function<void()>& 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();
Utils::Signal<Scheduler::Callback> copy(Scheduler::AsyncFrameOnceSignal);
Scheduler::AsyncFrameOnceSignal.clear();
copy();
std::this_thread::sleep_for(16ms);
Execute(Pipeline::ASYNC);
std::this_thread::sleep_for(10ms);
}
});
}
}
Scheduler::~Scheduler()
{
Scheduler::ReadySignal.clear();
Scheduler::ShutdownSignal.clear();
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();
Scheduler::FrameSignal.clear();
Scheduler::FrameOnceSignal.clear();
Scheduler::DelayedSlots.clear();
// Hook G_Glass_Update so we may fix TLS issues
Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick();
Scheduler::AsyncFrameSignal.clear();
Scheduler::AsyncFrameOnceSignal.clear();
// CL_CheckTimeout
Utils::Hook(0x4B0F81, ClientFrame_Hk, HOOK_CALL).install()->quick();
Scheduler::ReadyPassed = false;
// Com_Frame_Try_Block_Function
Utils::Hook(0x4B724F, MainFrame_Hk, HOOK_CALL).install()->quick();
// 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();
}
}
}

View File

@ -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
{
std::function<bool()> handler{};
std::chrono::milliseconds interval{};
std::chrono::high_resolution_clock::time_point lastCall{};
};
using taskList = std::vector<Task>;
class TaskPipeline
{
public:
std::chrono::nanoseconds delay;
Utils::Time::Interval interval;
Utils::Slot<Callback> callback;
void add(Task&& task);
void execute();
private:
Utils::Concurrency::Container<taskList> newCallbacks_;
Utils::Concurrency::Container<taskList, std::recursive_mutex> callbacks_;
void mergeCallbacks();
};
static bool AsyncTerminate;
static std::thread AsyncThread;
static volatile bool Kill;
static std::thread Thread;
static TaskPipeline Pipelines[];
static Utils::Signal<Callback> FrameSignal;
static Utils::Signal<Callback> FrameOnceSignal;
static std::vector<DelayedSlot> DelayedSlots;
static void Execute(Pipeline type);
static bool ReadyPassed;
static Utils::Signal<Callback> ReadySignal;
static Utils::Signal<Callback> ShutdownSignal;
static Utils::Signal<Callback> AsyncFrameSignal;
static Utils::Signal<Callback> AsyncFrameOnceSignal;
static void ReadyHandler();
static void DelaySignal();
static void ShutdownStub(int num);
static void REndFrame_Hk();
static void ServerFrame_Hk();
static void ClientFrame_Hk(int localClientNum);
static void MainFrame_Hk();
static void SysSetBlockSystemHotkeys_Hk(int block);
};
}

View File

@ -14,7 +14,7 @@ namespace Components
const char* Script::ReplacedPos = nullptr;
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", []

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,13 +74,16 @@
#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
@ -90,6 +93,17 @@
#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
View File

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

View File

@ -19,14 +19,14 @@ namespace Utils
return Library(handle);
}
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;
}
}

View File

@ -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;
};
}

View File

@ -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
View File

@ -0,0 +1,122 @@
#include <STDInclude.hpp>
namespace Utils::Thread
{
bool setName(const HANDLE t, const std::string& name)
{
const Library kernel32("kernel32.dll");
if (!kernel32)
{
return false;
}
const auto setDescription = kernel32.getProc<HRESULT(WINAPI*)(HANDLE, PCWSTR)>("SetThreadDescription");
if (!setDescription)
{
return false;
}
return SUCCEEDED(setDescription(t, String::Convert(name).data()));
}
bool setName(const DWORD id, const std::string& name)
{
auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id);
if (!t) return false;
const auto _ = gsl::finally([t]()
{
CloseHandle(t);
});
return setName(t, name);
}
bool setName(std::thread& t, const std::string& name)
{
return setName(t.native_handle(), name);
}
bool setName(const std::string& name)
{
return setName(GetCurrentThread(), name);
}
std::vector<DWORD> getThreadIds()
{
auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
if (h == INVALID_HANDLE_VALUE)
{
return {};
}
const auto _ = gsl::finally([h]()
{
CloseHandle(h);
});
THREADENTRY32 entry{};
entry.dwSize = sizeof(entry);
if (!Thread32First(h, &entry))
{
return {};
}
std::vector<DWORD> ids{};
do
{
const auto check_size = entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID)
+ sizeof(entry.th32OwnerProcessID);
entry.dwSize = sizeof(entry);
if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId())
{
ids.emplace_back(entry.th32ThreadID);
}
} while (Thread32Next(h, &entry));
return ids;
}
void forEachThread(const std::function<void(HANDLE)>& callback)
{
const auto ids = getThreadIds();
for (const auto& id : ids)
{
auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
if (thread != nullptr)
{
const auto _ = gsl::finally([thread]()
{
CloseHandle(thread);
});
callback(thread);
}
}
}
void suspendOtherThreads()
{
forEachThread([](const HANDLE thread)
{
if (GetThreadId(thread) != GetCurrentThreadId())
{
SuspendThread(thread);
}
});
}
void resumeOtherThreads()
{
forEachThread([](const HANDLE thread)
{
if (GetThreadId(thread) != GetCurrentThreadId())
{
ResumeThread(thread);
}
});
}
}

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

@ -0,0 +1,23 @@
#pragma once
namespace Utils::Thread
{
bool setName(HANDLE t, const std::string& name);
bool setName(DWORD id, const std::string& name);
bool setName(std::thread& t, const std::string& name);
bool setName(const std::string& name);
template <typename ...Args>
std::thread createNamedThread(const std::string& name, Args&&... args)
{
auto t = std::thread(std::forward<Args>(args)...);
setName(t, name);
return t;
}
std::vector<DWORD> getThreadIds();
void forEachThread(const std::function<void(HANDLE)>& callback);
void suspendOtherThreads();
void resumeOtherThreads();
}