[Threading]: Initialize tls stuff, making va thread safe (#657)
This commit is contained in:
parent
24ccd9af3b
commit
d8166a4166
@ -246,13 +246,20 @@ namespace Components
|
|||||||
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !CheatsOk(ent))
|
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Scheduler::Once([ent]
|
auto** bgs = Game::Sys::GetTls<Game::bgs_t*>(Game::Sys::ThreadOffset::LEVEL_BGS);
|
||||||
{
|
|
||||||
|
assert(*bgs == nullptr);
|
||||||
|
|
||||||
|
*bgs = Game::level_bgs;
|
||||||
|
|
||||||
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
|
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
|
||||||
ent->health = 0;
|
ent->health = 0;
|
||||||
ent->client->ps.stats[0] = 0;
|
ent->client->ps.stats[0] = 0;
|
||||||
Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0);
|
Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0);
|
||||||
}, Scheduler::Pipeline::SERVER);
|
|
||||||
|
assert(*bgs == Game::level_bgs);
|
||||||
|
|
||||||
|
*bgs = nullptr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,39 +7,42 @@ namespace Components
|
|||||||
std::thread Discovery::Thread;
|
std::thread Discovery::Thread;
|
||||||
std::string Discovery::Challenge;
|
std::string Discovery::Challenge;
|
||||||
|
|
||||||
|
Dvar::Var Discovery::NetDiscoveryPortRangeMin;
|
||||||
|
Dvar::Var Discovery::NetDiscoveryPortRangeMax;
|
||||||
|
|
||||||
void Discovery::Perform()
|
void Discovery::Perform()
|
||||||
{
|
{
|
||||||
Discovery::IsPerforming = true;
|
IsPerforming = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Discovery::Discovery()
|
Discovery::Discovery()
|
||||||
{
|
{
|
||||||
Dvar::Register<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_ARCHIVE, "Minimum scan range port for local server discovery");
|
NetDiscoveryPortRangeMin = Dvar::Register<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_NONE, "Minimum scan range port for local server discovery");
|
||||||
Dvar::Register<int>("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_ARCHIVE, "Maximum scan range port for local server discovery");
|
NetDiscoveryPortRangeMax = Dvar::Register<int>("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_NONE, "Maximum scan range port for local server discovery");
|
||||||
|
|
||||||
// An additional thread prevents lags
|
// An additional thread prevents lags
|
||||||
// Not sure if that's the best way though
|
// Not sure if that's the best way though
|
||||||
Discovery::IsPerforming = false;
|
IsPerforming = false;
|
||||||
Discovery::IsTerminating = false;
|
IsTerminating = false;
|
||||||
Discovery::Thread = std::thread([]()
|
Thread = std::thread([]
|
||||||
{
|
{
|
||||||
while (!Discovery::IsTerminating)
|
while (!IsTerminating)
|
||||||
{
|
{
|
||||||
if (Discovery::IsPerforming)
|
if (IsPerforming)
|
||||||
{
|
{
|
||||||
int start = Game::Sys_Milliseconds();
|
const auto start = Game::Sys_Milliseconds();
|
||||||
|
|
||||||
Logger::Print("Starting local server discovery...\n");
|
Logger::Print("Starting local server discovery...\n");
|
||||||
|
|
||||||
Discovery::Challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
Challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
||||||
|
|
||||||
unsigned int minPort = Dvar::Var("net_discoveryPortRangeMin").get<unsigned int>();
|
const auto minPort = NetDiscoveryPortRangeMin.get<unsigned int>();
|
||||||
unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get<unsigned int>();
|
const auto maxPort = NetDiscoveryPortRangeMax.get<unsigned int>();
|
||||||
Network::BroadcastRange(minPort, maxPort, Utils::String::VA("discovery %s", Discovery::Challenge.data()));
|
Network::BroadcastRange(minPort, maxPort, std::format("discovery {}", Challenge));
|
||||||
|
|
||||||
Logger::Print("Discovery sent within {}ms, awaiting responses...\n", Game::Sys_Milliseconds() - start);
|
Logger::Print("Discovery sent within {}ms, awaiting responses...\n", Game::Sys_Milliseconds() - start);
|
||||||
|
|
||||||
Discovery::IsPerforming = false;
|
IsPerforming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(50ms);
|
std::this_thread::sleep_for(50ms);
|
||||||
@ -70,7 +73,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils::ParseChallenge(data) != Discovery::Challenge)
|
if (Utils::ParseChallenge(data) != Challenge)
|
||||||
{
|
{
|
||||||
Logger::Print("Received discovery with invalid challenge from: {}\n", address.getString());
|
Logger::Print("Received discovery with invalid challenge from: {}\n", address.getString());
|
||||||
return;
|
return;
|
||||||
@ -87,12 +90,12 @@ namespace Components
|
|||||||
|
|
||||||
void Discovery::preDestroy()
|
void Discovery::preDestroy()
|
||||||
{
|
{
|
||||||
Discovery::IsPerforming = false;
|
IsPerforming = false;
|
||||||
Discovery::IsTerminating = true;
|
IsTerminating = true;
|
||||||
|
|
||||||
if (Discovery::Thread.joinable())
|
if (Thread.joinable())
|
||||||
{
|
{
|
||||||
Discovery::Thread.join();
|
Thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,8 @@ namespace Components
|
|||||||
static bool IsPerforming;
|
static bool IsPerforming;
|
||||||
static std::thread Thread;
|
static std::thread Thread;
|
||||||
static std::string Challenge;
|
static std::string Challenge;
|
||||||
|
|
||||||
|
static Dvar::Var NetDiscoveryPortRangeMin;
|
||||||
|
static Dvar::Var NetDiscoveryPortRangeMax;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -232,11 +232,11 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (!download) download = &CLDownload;
|
if (!download) download = &CLDownload;
|
||||||
|
|
||||||
auto host = "http://" + download->target.getString();
|
const auto host = "http://" + download->target.getString();
|
||||||
|
|
||||||
auto listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : "");
|
const auto listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : "");
|
||||||
|
|
||||||
auto list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get();
|
const auto list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get();
|
||||||
if (list.empty())
|
if (list.empty())
|
||||||
{
|
{
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
@ -276,7 +276,7 @@ namespace Components
|
|||||||
static std::string mod;
|
static std::string mod;
|
||||||
mod = download->mod;
|
mod = download->mod;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < download->files.size(); ++i)
|
for (std::size_t i = 0; i < download->files.size(); ++i)
|
||||||
{
|
{
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
|
|
||||||
@ -319,7 +319,6 @@ namespace Components
|
|||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Game::Dvar_SetString(*Game::fs_gameDirVar, mod.data());
|
Game::Dvar_SetString(*Game::fs_gameDirVar, mod.data());
|
||||||
const_cast<Game::dvar_t*>(*Game::fs_gameDirVar)->modified = true;
|
|
||||||
|
|
||||||
mod.clear();
|
mod.clear();
|
||||||
|
|
||||||
@ -673,6 +672,10 @@ namespace Components
|
|||||||
|
|
||||||
Download::Download()
|
Download::Download()
|
||||||
{
|
{
|
||||||
|
AssertSize(Game::va_info_t, 0x804);
|
||||||
|
AssertSize(jmp_buf, 0x40);
|
||||||
|
AssertSize(Game::TraceThreadInfo, 0x8);
|
||||||
|
|
||||||
if (Dedicated::IsEnabled())
|
if (Dedicated::IsEnabled())
|
||||||
{
|
{
|
||||||
mg_mgr_init(&Mgr);
|
mg_mgr_init(&Mgr);
|
||||||
@ -690,6 +693,8 @@ namespace Components
|
|||||||
Terminate = false;
|
Terminate = false;
|
||||||
ServerThread = Utils::Thread::CreateNamedThread("Mongoose", []
|
ServerThread = Utils::Thread::CreateNamedThread("Mongoose", []
|
||||||
{
|
{
|
||||||
|
Com_InitThreadData();
|
||||||
|
|
||||||
while (!Terminate)
|
while (!Terminate)
|
||||||
{
|
{
|
||||||
mg_mgr_poll(&Mgr, 100);
|
mg_mgr_poll(&Mgr, 100);
|
||||||
|
@ -13,12 +13,12 @@ namespace Components
|
|||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
|
||||||
if (News::Thread.joinable())
|
if (Thread.joinable())
|
||||||
{
|
{
|
||||||
Logger::Debug("Awaiting thread termination...");
|
Logger::Debug("Awaiting thread termination...");
|
||||||
News::Thread.join();
|
Thread.join();
|
||||||
|
|
||||||
if (!strcmp(Localization::Get("MPUI_MOTD_TEXT"), NEWS_MOTD_DEFAULT))
|
if (!std::strcmp(Localization::Get("MPUI_MOTD_TEXT"), NEWS_MOTD_DEFAULT))
|
||||||
{
|
{
|
||||||
Logger::Print("Failed to fetch motd!\n");
|
Logger::Print("Failed to fetch motd!\n");
|
||||||
result = false;
|
result = false;
|
||||||
@ -43,14 +43,12 @@ namespace Components
|
|||||||
|
|
||||||
Dvar::Register<bool>("g_firstLaunch", true, Game::DVAR_ARCHIVE, "");
|
Dvar::Register<bool>("g_firstLaunch", true, Game::DVAR_ARCHIVE, "");
|
||||||
|
|
||||||
Dvar::Register<int>("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_INIT, "Current version number.");
|
|
||||||
|
|
||||||
UIScript::Add("checkFirstLaunch", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
UIScript::Add("checkFirstLaunch", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
if (Dvar::Var("g_firstLaunch").get<bool>())
|
if (Dvar::Var("g_firstLaunch").get<bool>())
|
||||||
{
|
{
|
||||||
Command::Execute("openmenu menu_first_launch", false);
|
Command::Execute("openmenu menu_first_launch", false);
|
||||||
//Dvar::Var("g_firstLaunch").set(false);
|
//Dvar::Var("g_firstLaunch").set(false); // The menus should set it
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -67,15 +65,15 @@ namespace Components
|
|||||||
|
|
||||||
// hook for getting the news ticker string
|
// hook for getting the news ticker string
|
||||||
Utils::Hook::Nop(0x6388BB, 2); // skip the "if (item->text[0] == '@')" localize check
|
Utils::Hook::Nop(0x6388BB, 2); // skip the "if (item->text[0] == '@')" localize check
|
||||||
Utils::Hook(0x6388C1, News::GetNewsText, HOOK_CALL).install()->quick();
|
Utils::Hook(0x6388C1, GetNewsText, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
if (!Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests())
|
if (!Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests())
|
||||||
{
|
{
|
||||||
News::Terminate = false;
|
Terminate = false;
|
||||||
News::Thread = std::thread([]()
|
Thread = std::thread([]()
|
||||||
{
|
{
|
||||||
Changelog::LoadChangelog();
|
Changelog::LoadChangelog();
|
||||||
if (News::Terminate) return;
|
if (Terminate) return;
|
||||||
|
|
||||||
std::string data = Utils::Cache::GetFile("/iw4/motd.txt");
|
std::string data = Utils::Cache::GetFile("/iw4/motd.txt");
|
||||||
if (!data.empty())
|
if (!data.empty())
|
||||||
@ -83,12 +81,12 @@ namespace Components
|
|||||||
Localization::Set("MPUI_MOTD_TEXT", data);
|
Localization::Set("MPUI_MOTD_TEXT", data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Loader::IsPerformingUnitTests() && !News::Terminate)
|
if (!Loader::IsPerformingUnitTests() && !Terminate)
|
||||||
{
|
{
|
||||||
while (!News::Terminate)
|
while (!Terminate)
|
||||||
{
|
{
|
||||||
// Sleep for 3 minutes
|
// Sleep for 3 minutes
|
||||||
for (int i = 0; i < 180 && !News::Terminate; ++i)
|
for (int i = 0; i < 180 && !Terminate; ++i)
|
||||||
{
|
{
|
||||||
std::this_thread::sleep_for(1s);
|
std::this_thread::sleep_for(1s);
|
||||||
}
|
}
|
||||||
@ -100,13 +98,11 @@ namespace Components
|
|||||||
|
|
||||||
void News::preDestroy()
|
void News::preDestroy()
|
||||||
{
|
{
|
||||||
News::Terminate = true;
|
Terminate = true;
|
||||||
|
|
||||||
if (News::Thread.joinable())
|
if (Thread.joinable())
|
||||||
{
|
{
|
||||||
News::Thread.join();
|
Thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
News::Thread = std::thread();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,9 +154,6 @@ namespace Components
|
|||||||
// dvar setting function, unknown stuff related to server thread sync
|
// dvar setting function, unknown stuff related to server thread sync
|
||||||
Utils::Hook::Set<std::uint8_t>(0x647781, 0xEB);
|
Utils::Hook::Set<std::uint8_t>(0x647781, 0xEB);
|
||||||
|
|
||||||
// make VA thread safe
|
|
||||||
Utils::Hook(0x4785B0, Utils::String::VA, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
Utils::Hook(0x627695, 0x627040, HOOK_CALL).install()->quick();
|
Utils::Hook(0x627695, 0x627040, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x43D1C7, PacketEventStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x43D1C7, PacketEventStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x6272E3, FrameEpilogueStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x6272E3, FrameEpilogueStub, HOOK_JUMP).install()->quick();
|
||||||
|
@ -11,7 +11,8 @@ namespace Components
|
|||||||
|
|
||||||
bool ZoneBuilder::MainThreadInterrupted;
|
bool ZoneBuilder::MainThreadInterrupted;
|
||||||
DWORD ZoneBuilder::InterruptingThreadId;
|
DWORD ZoneBuilder::InterruptingThreadId;
|
||||||
volatile bool ZoneBuilder::Terminate = false;
|
|
||||||
|
volatile bool ZoneBuilder::CommandThreadTerminate = false;
|
||||||
std::thread ZoneBuilder::CommandThread;
|
std::thread ZoneBuilder::CommandThread;
|
||||||
|
|
||||||
Dvar::Var ZoneBuilder::PreferDiskAssetsDvar;
|
Dvar::Var ZoneBuilder::PreferDiskAssetsDvar;
|
||||||
@ -120,7 +121,7 @@ namespace Components
|
|||||||
this->writeZone();
|
this->writeZone();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneBuilder::Zone::loadFastFiles()
|
void ZoneBuilder::Zone::loadFastFiles() const
|
||||||
{
|
{
|
||||||
Logger::Print("Loading required FastFiles...\n");
|
Logger::Print("Loading required FastFiles...\n");
|
||||||
|
|
||||||
@ -793,7 +794,8 @@ namespace Components
|
|||||||
return GetCurrentThreadId() == Utils::Hook::Get<DWORD>(0x1CDE7FC);
|
return GetCurrentThreadId() == Utils::Hook::Get<DWORD>(0x1CDE7FC);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Game::XZoneInfo baseZones_old[] = {
|
static Game::XZoneInfo baseZones_old[] =
|
||||||
|
{
|
||||||
{ "code_pre_gfx_mp", Game::DB_ZONE_CODE, 0 },
|
{ "code_pre_gfx_mp", Game::DB_ZONE_CODE, 0 },
|
||||||
{ "localized_code_pre_gfx_mp", Game::DB_ZONE_CODE_LOC, 0 },
|
{ "localized_code_pre_gfx_mp", Game::DB_ZONE_CODE_LOC, 0 },
|
||||||
{ "code_post_gfx_mp", Game::DB_ZONE_CODE, 0 },
|
{ "code_post_gfx_mp", Game::DB_ZONE_CODE, 0 },
|
||||||
@ -805,7 +807,8 @@ namespace Components
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static Game::XZoneInfo baseZones[] = {
|
static Game::XZoneInfo baseZones[] =
|
||||||
|
{
|
||||||
{ "defaults", Game::DB_ZONE_CODE, 0 },
|
{ "defaults", Game::DB_ZONE_CODE, 0 },
|
||||||
{ "techsets", Game::DB_ZONE_CODE, 0 },
|
{ "techsets", Game::DB_ZONE_CODE, 0 },
|
||||||
{ "common_mp", Game::DB_ZONE_COMMON, 0 },
|
{ "common_mp", Game::DB_ZONE_COMMON, 0 },
|
||||||
@ -819,7 +822,20 @@ namespace Components
|
|||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int APIENTRY ZoneBuilder::EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
|
void ZoneBuilder::CommandThreadCallback()
|
||||||
|
{
|
||||||
|
Com_InitThreadData();
|
||||||
|
|
||||||
|
while (!ZoneBuilder::CommandThreadTerminate)
|
||||||
|
{
|
||||||
|
ZoneBuilder::AssumeMainThreadRole();
|
||||||
|
Utils::Hook::Call<void(int, int)>(0x4E2C80)(0, 0); // Cbuf_Execute
|
||||||
|
ZoneBuilder::ResetThreadRole();
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL APIENTRY ZoneBuilder::EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
|
||||||
{
|
{
|
||||||
Utils::Hook::Call<void()>(0x42F0A0)(); // Com_InitCriticalSections
|
Utils::Hook::Call<void()>(0x42F0A0)(); // Com_InitCriticalSections
|
||||||
Utils::Hook::Call<void()>(0x4301B0)(); // Com_InitMainThread
|
Utils::Hook::Call<void()>(0x4301B0)(); // Com_InitMainThread
|
||||||
@ -835,11 +851,14 @@ namespace Components
|
|||||||
Utils::Hook::Call<void()>(0x4A62A0)(); // LargeLocalInit
|
Utils::Hook::Call<void()>(0x4A62A0)(); // LargeLocalInit
|
||||||
Utils::Hook::Call<void()>(0x4DCC10)(); // Sys_InitCmdEvents
|
Utils::Hook::Call<void()>(0x4DCC10)(); // Sys_InitCmdEvents
|
||||||
Utils::Hook::Call<void()>(0x64A020)(); // PMem_Init
|
Utils::Hook::Call<void()>(0x64A020)(); // PMem_Init
|
||||||
|
|
||||||
if (!Flags::HasFlag("stdout"))
|
if (!Flags::HasFlag("stdout"))
|
||||||
{
|
{
|
||||||
Console::ShowAsyncConsole();
|
Console::ShowAsyncConsole();
|
||||||
Utils::Hook::Call<void()>(0x43D140)(); // Com_EventLoop
|
Utils::Hook::Call<void()>(0x43D140)(); // Com_EventLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Utils::Hook::Call<void(unsigned int)>(0x502580)(static_cast<unsigned int>(__rdtsc())); // Netchan_Init
|
Utils::Hook::Call<void(unsigned int)>(0x502580)(static_cast<unsigned int>(__rdtsc())); // Netchan_Init
|
||||||
Utils::Hook::Call<void()>(0x429080)(); // FS_InitFileSystem
|
Utils::Hook::Call<void()>(0x429080)(); // FS_InitFileSystem
|
||||||
Utils::Hook::Call<void()>(0x4BFBE0)(); // Con_InitChannels
|
Utils::Hook::Call<void()>(0x4BFBE0)(); // Con_InitChannels
|
||||||
@ -848,21 +867,10 @@ namespace Components
|
|||||||
Game::NET_Init();
|
Game::NET_Init();
|
||||||
Utils::Hook::Call<void()>(0x4F5090)(); // SND_InitDriver
|
Utils::Hook::Call<void()>(0x4F5090)(); // SND_InitDriver
|
||||||
Utils::Hook::Call<void()>(0x46A630)(); // SND_Init
|
Utils::Hook::Call<void()>(0x46A630)(); // SND_Init
|
||||||
//Utils::Hook::Call<void()>(0x4D3660)(); // SV_Init
|
|
||||||
//Utils::Hook::Call<void()>(0x4121E0)(); // SV_InitServerThread
|
|
||||||
//Utils::Hook::Call<void()>(0x464A90)(); // Com_ParseCommandLine
|
|
||||||
Utils::Hook::Call<void()>(0x43D140)(); // Com_EventLoop
|
Utils::Hook::Call<void()>(0x43D140)(); // Com_EventLoop
|
||||||
|
|
||||||
ZoneBuilder::CommandThread = std::thread([]
|
ZoneBuilder::CommandThread = Utils::Thread::CreateNamedThread("Command Thread", ZoneBuilder::CommandThreadCallback);
|
||||||
{
|
ZoneBuilder::CommandThread.detach();
|
||||||
while (!ZoneBuilder::Terminate)
|
|
||||||
{
|
|
||||||
ZoneBuilder::AssumeMainThreadRole();
|
|
||||||
Utils::Hook::Call<void(int, int)>(0x4E2C80)(0, 0); // Cbuf_Execute
|
|
||||||
ZoneBuilder::ResetThreadRole();
|
|
||||||
std::this_thread::sleep_for(1ms);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Command::Add("quit", ZoneBuilder::Com_Quitf_t);
|
Command::Add("quit", ZoneBuilder::Com_Quitf_t);
|
||||||
|
|
||||||
@ -873,8 +881,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER,
|
Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing new init zones (defaults.ff & techsets.ff). You will need to load fastfiles to manually obtain techsets.\n");
|
||||||
"Missing new init zones (defaults.ff & techsets.ff). You will need to load fastfiles to manually obtain techsets.\n");
|
|
||||||
Game::DB_LoadXAssets(baseZones_old, ARRAYSIZE(baseZones_old), 0);
|
Game::DB_LoadXAssets(baseZones_old, ARRAYSIZE(baseZones_old), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -908,7 +915,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print(" --------------------------------------------------------------------------------\n");
|
Logger::Print(" --------------------------------------------------------------------------------\n");
|
||||||
Logger::Print(" IW4x ZoneBuilder (" VERSION ")\n");
|
Logger::Print(" IW4x ZoneBuilder ({})\n", VERSION);
|
||||||
Logger::Print(" Commands:\n");
|
Logger::Print(" Commands:\n");
|
||||||
Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n");
|
Logger::Print("\t-buildzone [zone]: builds a zone from a csv located in zone_source\n");
|
||||||
Logger::Print("\t-buildall: builds all zones in zone_source\n");
|
Logger::Print("\t-buildall: builds all zones in zone_source\n");
|
||||||
@ -1006,8 +1013,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
assert(data);
|
assert(data);
|
||||||
auto* sound = Utils::Hook::Get<Game::MssSound*>(0x112AE04);
|
auto* sound = Utils::Hook::Get<Game::MssSound*>(0x112AE04);
|
||||||
auto length = sound->info.data_len;
|
const auto length = sound->info.data_len;
|
||||||
auto allocatedSpace = Game::Z_Malloc(length);
|
const auto allocatedSpace = Game::Z_Malloc(static_cast<int>(length));
|
||||||
memcpy_s(allocatedSpace, length, data, length);
|
memcpy_s(allocatedSpace, length, data, length);
|
||||||
|
|
||||||
data = allocatedSpace;
|
data = allocatedSpace;
|
||||||
@ -1022,8 +1029,6 @@ namespace Components
|
|||||||
AssertSize(Game::XFile, 40);
|
AssertSize(Game::XFile, 40);
|
||||||
static_assert(Game::MAX_XFILE_COUNT == 8, "XFile block enum is invalid!");
|
static_assert(Game::MAX_XFILE_COUNT == 8, "XFile block enum is invalid!");
|
||||||
|
|
||||||
ZoneBuilder::EndAssetTrace();
|
|
||||||
|
|
||||||
if (ZoneBuilder::IsEnabled())
|
if (ZoneBuilder::IsEnabled())
|
||||||
{
|
{
|
||||||
// Prevent loading textures (preserves loaddef)
|
// Prevent loading textures (preserves loaddef)
|
||||||
@ -1035,10 +1040,10 @@ namespace Components
|
|||||||
// Release the loaddef
|
// Release the loaddef
|
||||||
Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_IMAGE] = ZoneBuilder::ReleaseTexture;
|
Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_IMAGE] = ZoneBuilder::ReleaseTexture;
|
||||||
|
|
||||||
//r_loadForrenderer = 0
|
// r_loadForrenderer = 0
|
||||||
Utils::Hook::Set<BYTE>(0x519DDF, 0);
|
Utils::Hook::Set<BYTE>(0x519DDF, 0);
|
||||||
|
|
||||||
//r_delayloadimage retn
|
// r_delayloadimage ret
|
||||||
Utils::Hook::Set<BYTE>(0x51F450, 0xC3);
|
Utils::Hook::Set<BYTE>(0x51F450, 0xC3);
|
||||||
|
|
||||||
// r_registerDvars hack
|
// r_registerDvars hack
|
||||||
@ -1056,7 +1061,7 @@ namespace Components
|
|||||||
// Don't mark assets
|
// Don't mark assets
|
||||||
//Utils::Hook::Nop(0x5BB632, 5);
|
//Utils::Hook::Nop(0x5BB632, 5);
|
||||||
|
|
||||||
// Don't load sounds
|
// Load sounds
|
||||||
Utils::Hook(0x492EFC, ReallocateLoadedSounds, HOOK_CALL).install()->quick();
|
Utils::Hook(0x492EFC, ReallocateLoadedSounds, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Don't display errors when assets are missing (we might manually build those)
|
// Don't display errors when assets are missing (we might manually build those)
|
||||||
@ -1092,14 +1097,14 @@ namespace Components
|
|||||||
Utils::Hook::Set<DWORD>(0x64A029, 0x38400000); // 900 MiB
|
Utils::Hook::Set<DWORD>(0x64A029, 0x38400000); // 900 MiB
|
||||||
Utils::Hook::Set<DWORD>(0x64A057, 0x38400000);
|
Utils::Hook::Set<DWORD>(0x64A057, 0x38400000);
|
||||||
|
|
||||||
// change fs_game domain func
|
// change FS_GameDirDomainFunc
|
||||||
Utils::Hook::Set<int(*)(Game::dvar_t*, Game::DvarValue)>(0x643203, [](Game::dvar_t* dvar, Game::DvarValue value)
|
Utils::Hook::Set<int(*)(Game::dvar_t*, Game::DvarValue)>(0x643203, [](Game::dvar_t* dvar, Game::DvarValue value)
|
||||||
{
|
{
|
||||||
int result = Utils::Hook::Call<int(Game::dvar_t*, Game::DvarValue)>(0x642FC0)(dvar, value);
|
int result = Utils::Hook::Call<int(Game::dvar_t*, Game::DvarValue)>(0x642FC0)(dvar, value);
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
if (std::string(value.string) != dvar->current.string)
|
if (std::strcmp(value.string, dvar->current.string) != 0)
|
||||||
{
|
{
|
||||||
dvar->current.string = value.string;
|
dvar->current.string = value.string;
|
||||||
Game::FS_Restart(0, 0);
|
Game::FS_Restart(0, 0);
|
||||||
@ -1118,7 +1123,7 @@ namespace Components
|
|||||||
// handle Com_error Calls
|
// handle Com_error Calls
|
||||||
Utils::Hook(Game::Com_Error, ZoneBuilder::HandleError, HOOK_JUMP).install()->quick();
|
Utils::Hook(Game::Com_Error, ZoneBuilder::HandleError, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// thread fuckery hooks
|
// Sys_IsMainThread hook
|
||||||
Utils::Hook(0x4C37D0, ZoneBuilder::IsThreadMainThreadHook, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4C37D0, ZoneBuilder::IsThreadMainThreadHook, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Don't exec startup config in fs_restart
|
// Don't exec startup config in fs_restart
|
||||||
@ -1134,31 +1139,16 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
|
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
|
||||||
{
|
{
|
||||||
ZoneBuilder::TraceAssets.push_back({ type, name });
|
ZoneBuilder::TraceAssets.emplace_back(std::make_pair(type, name));
|
||||||
OutputDebugStringA((name + "\n").data());
|
#ifdef _DEBUG
|
||||||
|
OutputDebugStringA(Utils::String::Format("%s\n", name));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::Add("verifyzone", [](Command::Params* params)
|
Command::Add("verifyzone", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (params->size() < 2) return;
|
||||||
/*
|
|
||||||
Utils::Hook(0x4AE9C2, []
|
|
||||||
{
|
|
||||||
Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4;
|
|
||||||
Game::WeaponCompleteDef* var = *varPtr;
|
|
||||||
OutputDebugStringA("");
|
|
||||||
Utils::Hook::Call<void()>(0x4D1D60)(); // DB_PopStreamPos
|
|
||||||
}, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
Utils::Hook(0x4AE9B4, []
|
|
||||||
{
|
|
||||||
Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4;
|
|
||||||
Game::WeaponCompleteDef* var = *varPtr;
|
|
||||||
OutputDebugStringA("");
|
|
||||||
Utils::Hook::Call<void()>(0x4D1D60)(); // DB_PopStreamPos
|
|
||||||
}, HOOK_JUMP).install()->quick();
|
|
||||||
*/
|
|
||||||
|
|
||||||
std::string zone = params->get(1);
|
std::string zone = params->get(1);
|
||||||
|
|
||||||
@ -1601,7 +1591,7 @@ namespace Components
|
|||||||
|
|
||||||
ZoneBuilder::~ZoneBuilder()
|
ZoneBuilder::~ZoneBuilder()
|
||||||
{
|
{
|
||||||
ZoneBuilder::Terminate = true;
|
ZoneBuilder::CommandThreadTerminate = true;
|
||||||
if (ZoneBuilder::CommandThread.joinable())
|
if (ZoneBuilder::CommandThread.joinable())
|
||||||
{
|
{
|
||||||
ZoneBuilder::CommandThread.join();
|
ZoneBuilder::CommandThread.join();
|
||||||
|
@ -74,7 +74,7 @@ namespace Components
|
|||||||
bool isPrimaryAsset() { return this->assetDepth <= 1; }
|
bool isPrimaryAsset() { return this->assetDepth <= 1; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void loadFastFiles();
|
void loadFastFiles() const;
|
||||||
|
|
||||||
bool loadAssets();
|
bool loadAssets();
|
||||||
bool loadAssetByName(const std::string& type, std::string name, bool isSubAsset = true);
|
bool loadAssetByName(const std::string& type, std::string name, bool isSubAsset = true);
|
||||||
@ -140,7 +140,7 @@ namespace Components
|
|||||||
static std::string FindMaterialByTechnique(const std::string& name);
|
static std::string FindMaterialByTechnique(const std::string& name);
|
||||||
static void ReallocateLoadedSounds(void*& data, void* a2);
|
static void ReallocateLoadedSounds(void*& data, void* a2);
|
||||||
|
|
||||||
static int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/);
|
static BOOL APIENTRY EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/);
|
||||||
static void HandleError(Game::errorParm_t code, const char* fmt, ...);
|
static void HandleError(Game::errorParm_t code, const char* fmt, ...);
|
||||||
static void SoftErrorAssetOverflow();
|
static void SoftErrorAssetOverflow();
|
||||||
|
|
||||||
@ -151,10 +151,12 @@ namespace Components
|
|||||||
|
|
||||||
static void Com_Quitf_t();
|
static void Com_Quitf_t();
|
||||||
|
|
||||||
|
static void CommandThreadCallback();
|
||||||
|
|
||||||
static bool MainThreadInterrupted;
|
static bool MainThreadInterrupted;
|
||||||
static DWORD InterruptingThreadId;
|
static DWORD InterruptingThreadId;
|
||||||
|
|
||||||
static volatile bool Terminate;
|
static volatile bool CommandThreadTerminate;
|
||||||
static std::thread CommandThread;
|
static std::thread CommandThread;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -76,3 +76,19 @@ namespace Game
|
|||||||
|
|
||||||
extern const char* Com_LoadInfoString_FastFile(const char* fileName, const char* fileDesc, const char* ident, char* loadBuffer);
|
extern const char* Com_LoadInfoString_FastFile(const char* fileName, const char* fileDesc, const char* ident, char* loadBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define Com_InitThreadData() \
|
||||||
|
{ \
|
||||||
|
static Game::ProfileStack profile_stack{}; \
|
||||||
|
static Game::va_info_t va_info{}; \
|
||||||
|
static jmp_buf g_com_error{}; \
|
||||||
|
static Game::TraceThreadInfo g_trace_thread_info{}; \
|
||||||
|
static void* g_thread_values[Game::THREAD_VALUE_COUNT]{}; \
|
||||||
|
auto** thread_data = \
|
||||||
|
Game::Sys::GetTls<void*>(Game::Sys::ThreadOffset::THREAD_VALUES); \
|
||||||
|
*thread_data = g_thread_values; \
|
||||||
|
Game::Sys_SetValue(Game::THREAD_VALUE_PROF_STACK, &profile_stack); \
|
||||||
|
Game::Sys_SetValue(Game::THREAD_VALUE_VA, &va_info); \
|
||||||
|
Game::Sys_SetValue(Game::THREAD_VALUE_COM_ERROR, &g_com_error); \
|
||||||
|
Game::Sys_SetValue(Game::THREAD_VALUE_TRACE, &g_trace_thread_info); \
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@ namespace Game
|
|||||||
Cbuf_AddServerText_f_t Cbuf_AddServerText_f = Cbuf_AddServerText_f_t(0x4BB9B0);
|
Cbuf_AddServerText_f_t Cbuf_AddServerText_f = Cbuf_AddServerText_f_t(0x4BB9B0);
|
||||||
Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20);
|
Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20);
|
||||||
Cbuf_InsertText_t Cbuf_InsertText = Cbuf_InsertText_t(0x4940B0);
|
Cbuf_InsertText_t Cbuf_InsertText = Cbuf_InsertText_t(0x4940B0);
|
||||||
|
Cbuf_Execute_t Cbuf_Execute = Cbuf_Execute_t(0x4E2C80);
|
||||||
|
|
||||||
CG_DrawDisconnect_t CG_DrawDisconnect = CG_DrawDisconnect_t(0x454A70);
|
CG_DrawDisconnect_t CG_DrawDisconnect = CG_DrawDisconnect_t(0x454A70);
|
||||||
CG_NextWeapon_f_t CG_NextWeapon_f = CG_NextWeapon_f_t(0x449DE0);
|
CG_NextWeapon_f_t CG_NextWeapon_f = CG_NextWeapon_f_t(0x449DE0);
|
||||||
@ -381,7 +382,9 @@ namespace Game
|
|||||||
|
|
||||||
Material** whiteMaterial = reinterpret_cast<Material**>(0x8EE4B8);
|
Material** whiteMaterial = reinterpret_cast<Material**>(0x8EE4B8);
|
||||||
|
|
||||||
unsigned long* _tls_index = reinterpret_cast<unsigned long*>(0x66D94A8);
|
unsigned long* g_dwTlsIndex = reinterpret_cast<unsigned long*>(0x66D94A8);
|
||||||
|
|
||||||
|
bgs_t* level_bgs = reinterpret_cast<bgs_t*>(0x19BD680);
|
||||||
|
|
||||||
unsigned int* playerCardUIStringIndex = reinterpret_cast<unsigned int*>(0x62CD7A8);
|
unsigned int* playerCardUIStringIndex = reinterpret_cast<unsigned int*>(0x62CD7A8);
|
||||||
char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast<char(*)[PLAYER_CARD_UI_STRING_COUNT][38]>(0x62CB4F8);
|
char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast<char(*)[PLAYER_CARD_UI_STRING_COUNT][38]>(0x62CB4F8);
|
||||||
|
@ -15,6 +15,9 @@ namespace Game
|
|||||||
typedef void(*Cbuf_InsertText_t)(int localClientNum, const char* text);
|
typedef void(*Cbuf_InsertText_t)(int localClientNum, const char* text);
|
||||||
extern Cbuf_InsertText_t Cbuf_InsertText;
|
extern Cbuf_InsertText_t Cbuf_InsertText;
|
||||||
|
|
||||||
|
typedef void(*Cbuf_Execute_t)(int localClientNum, int controllerIndex);
|
||||||
|
extern Cbuf_Execute_t Cbuf_Execute;
|
||||||
|
|
||||||
typedef void(*CG_DrawDisconnect_t)(int localClientNum);
|
typedef void(*CG_DrawDisconnect_t)(int localClientNum);
|
||||||
extern CG_DrawDisconnect_t CG_DrawDisconnect;
|
extern CG_DrawDisconnect_t CG_DrawDisconnect;
|
||||||
|
|
||||||
@ -712,7 +715,9 @@ namespace Game
|
|||||||
|
|
||||||
extern Material** whiteMaterial;
|
extern Material** whiteMaterial;
|
||||||
|
|
||||||
extern unsigned long* _tls_index;
|
extern unsigned long* g_dwTlsIndex;
|
||||||
|
|
||||||
|
extern bgs_t* level_bgs;
|
||||||
|
|
||||||
constexpr std::size_t PLAYER_CARD_UI_STRING_COUNT = 18;
|
constexpr std::size_t PLAYER_CARD_UI_STRING_COUNT = 18;
|
||||||
extern unsigned int* playerCardUIStringIndex;
|
extern unsigned int* playerCardUIStringIndex;
|
||||||
|
@ -10333,6 +10333,59 @@ namespace Game
|
|||||||
TraceCheckCount checkcount;
|
TraceCheckCount checkcount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ProfileAtom
|
||||||
|
{
|
||||||
|
unsigned int value[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
volatile struct ProfileReadable
|
||||||
|
{
|
||||||
|
unsigned int hits;
|
||||||
|
ProfileAtom total;
|
||||||
|
ProfileAtom self;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfileWritable
|
||||||
|
{
|
||||||
|
int nesting;
|
||||||
|
unsigned int hits;
|
||||||
|
ProfileAtom start[3];
|
||||||
|
ProfileAtom total;
|
||||||
|
ProfileAtom child;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct profile_t
|
||||||
|
{
|
||||||
|
ProfileWritable write;
|
||||||
|
ProfileReadable read;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct profile_guard_t
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
profile_t** ppStack;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProfileStack
|
||||||
|
{
|
||||||
|
profile_t prof_root;
|
||||||
|
profile_t* prof_pStack[16384];
|
||||||
|
profile_t** prof_ppStack;
|
||||||
|
profile_t prof_array[443];
|
||||||
|
ProfileAtom prof_overhead_internal;
|
||||||
|
ProfileAtom prof_overhead_external;
|
||||||
|
profile_guard_t prof_guardstack[32];
|
||||||
|
int prof_guardpos;
|
||||||
|
float prof_timescale;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bgs_t
|
||||||
|
{
|
||||||
|
unsigned char __pad0[0x82950];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(bgs_t) == 0x82950);
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#ifndef IDA
|
#ifndef IDA
|
||||||
|
@ -21,6 +21,7 @@ namespace Game
|
|||||||
Sys_SendPacket_t Sys_SendPacket = Sys_SendPacket_t(0x60FDC0);
|
Sys_SendPacket_t Sys_SendPacket = Sys_SendPacket_t(0x60FDC0);
|
||||||
Sys_ShowConsole_t Sys_ShowConsole = Sys_ShowConsole_t(0x4305E0);
|
Sys_ShowConsole_t Sys_ShowConsole = Sys_ShowConsole_t(0x4305E0);
|
||||||
Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads = Sys_SuspendOtherThreads_t(0x45A190);
|
Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads = Sys_SuspendOtherThreads_t(0x45A190);
|
||||||
|
Sys_SetValue_t Sys_SetValue = Sys_SetValue_t(0x4B2F50);
|
||||||
|
|
||||||
char(*sys_exitCmdLine)[1024] = reinterpret_cast<char(*)[1024]>(0x649FB68);
|
char(*sys_exitCmdLine)[1024] = reinterpret_cast<char(*)[1024]>(0x649FB68);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Game
|
namespace Game
|
||||||
{
|
{
|
||||||
|
|
||||||
typedef void(*Sys_Error_t)(const char* error, ...);
|
typedef void(*Sys_Error_t)(const char* error, ...);
|
||||||
extern Sys_Error_t Sys_Error;
|
extern Sys_Error_t Sys_Error;
|
||||||
|
|
||||||
@ -60,6 +59,9 @@ namespace Game
|
|||||||
typedef void(*Sys_SuspendOtherThreads_t)();
|
typedef void(*Sys_SuspendOtherThreads_t)();
|
||||||
extern Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads;
|
extern Sys_SuspendOtherThreads_t Sys_SuspendOtherThreads;
|
||||||
|
|
||||||
|
typedef void(*Sys_SetValue_t)(int valueIndex, void* data);
|
||||||
|
extern Sys_SetValue_t Sys_SetValue;
|
||||||
|
|
||||||
extern char(*sys_exitCmdLine)[1024];
|
extern char(*sys_exitCmdLine)[1024];
|
||||||
|
|
||||||
extern RTL_CRITICAL_SECTION* s_criticalSection;
|
extern RTL_CRITICAL_SECTION* s_criticalSection;
|
||||||
@ -68,4 +70,22 @@ namespace Game
|
|||||||
extern void Sys_UnlockRead(FastCriticalSection* critSect);
|
extern void Sys_UnlockRead(FastCriticalSection* critSect);
|
||||||
|
|
||||||
extern bool Sys_TryEnterCriticalSection(CriticalSection critSect);
|
extern bool Sys_TryEnterCriticalSection(CriticalSection critSect);
|
||||||
|
|
||||||
|
class Sys
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ThreadOffset : unsigned int
|
||||||
|
{
|
||||||
|
LEVEL_BGS = 0xC,
|
||||||
|
THREAD_VALUES = 0x14,
|
||||||
|
DVAR_MODIFIED_FLAGS = 0x18,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T* GetTls(ThreadOffset offset)
|
||||||
|
{
|
||||||
|
const auto* tls = reinterpret_cast<std::uintptr_t*>(__readfsdword(0x2C));
|
||||||
|
return reinterpret_cast<T*>(tls[*g_dwTlsIndex] + offset);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user