[Threading]: Initialize tls stuff, making va thread safe (#657)

This commit is contained in:
Edo 2022-12-18 22:47:59 +01:00 committed by GitHub
parent 24ccd9af3b
commit d8166a4166
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 230 additions and 129 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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