diff --git a/CHANGELOG.md b/CHANGELOG.md index 4906ef22..a32cf953 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. - Knife charge is fixed for controller players (#259) - Fixed internet, local and favorites filters (#260) -- `sv_lanOnly` Dvar now prevents the server from sending heatbeats to master if set to true (#246) +- `sv_lanOnly` Dvar now prevents the server from sending heartbeats to master if set to true (#246) ### Known issue diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 582bebb0..d002594f 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -6,6 +6,8 @@ namespace Components Dvar::Var Dedicated::SVRandomMapRotation; Dvar::Var Dedicated::SVLanOnly; + Dvar::Var Dedicated::SVDontRotate; + Dvar::Var Dedicated::COMLogFilter; bool Dedicated::IsEnabled() { @@ -35,7 +37,7 @@ namespace Components std::memcpy(reinterpret_cast(0x66E1CB0), &fastfiles, sizeof(fastfiles)); Game::R_LoadGraphicsAssets(); - if (Dvar::Var("com_logFilter").get()) + if (COMLogFilter.get()) { Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines @@ -129,21 +131,22 @@ namespace Components const auto tokens = Utils::String::Split(rotation, ' '); std::vector> mapRotationPair; - for (auto i = 0u; i < (tokens.size() - 1); i += 2) + for (std::size_t i = 0; i < (tokens.size() - 1); i += 2) { if (i + 1 >= tokens.size()) break; const auto& key = tokens[i]; const auto& value = tokens[i + 1]; - mapRotationPair.push_back(std::make_pair(key, value)); + mapRotationPair.emplace_back(std::make_pair(key, value)); } const auto seed = Utils::Cryptography::Rand::GenerateInt(); - std::shuffle(std::begin(mapRotationPair), std::end(mapRotationPair), std::default_random_engine(seed)); + std::shuffle(mapRotationPair.begin(), mapRotationPair.end(), std::default_random_engine(seed)); // Rebuild map rotation using the randomized key/values rotation.clear(); - for (auto j = 0u; j < mapRotationPair.size(); j++) + + for (std::size_t j = 0; j < mapRotationPair.size(); j++) { const auto& pair = mapRotationPair[j]; rotation.append(pair.first); @@ -151,33 +154,79 @@ namespace Components rotation.append(pair.second); if (j != mapRotationPair.size() - 1) - rotation.append(" "); + rotation.append(" "); // No space on last element } Dvar::Var("sv_mapRotationCurrent").set(rotation); } + void Dedicated::ApplyMapRotation() + { + auto rotation = Dvar::Var("sv_mapRotationCurrent").get(); + const auto tokens = Utils::String::Split(rotation, ' '); + + for (std::size_t i = 0; i < (tokens.size() - 1); i += 2) + { + if (i + 1 >= tokens.size()) + { + Dvar::Var("sv_mapRotationCurrent").set(""); + Command::Execute("map_rotate", true); + return; + } + + const auto& key = tokens[i]; + const auto& value = tokens[i + 1]; + + if (key == "map") + { + // Rebuild map rotation string + rotation.clear(); + for (std::size_t j = (i + 2); j < tokens.size(); ++j) + { + if (j != (i + 2)) rotation += " "; + rotation += tokens[j]; + } + + Dvar::Var("sv_mapRotationCurrent").set(rotation); + + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER,"Loading new map: %s\n", value.data()); + Command::Execute(Utils::String::VA("map %s", value.data()), true); + break; + } + + if (key == "gametype") + { + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Applying new gametype: %s\n", value.data()); + Dvar::Var("g_gametype").set(value); + } + else + { + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Unsupported maprotation key '%s'!\n", key.data()); + } + } + } + void Dedicated::MapRotate() { - if (!Dedicated::IsEnabled() && Dvar::Var("sv_dontrotate").get()) + if (!Dedicated::IsEnabled() && Dedicated::SVDontRotate.get()) { - Dvar::Var("sv_dontrotate").set(false); + Dedicated::SVDontRotate.set(false); return; } if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) { - Logger::Print("Not performing map rotation as we are hosting a party!\n"); + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Not performing map rotation as we are hosting a party!\n"); return; } - Logger::Print("Rotating map...\n"); + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Rotating map...\n"); const auto mapRotation = Dvar::Var("sv_mapRotation").get(); // if nothing, just restart if (mapRotation.empty()) { - Logger::Print("No rotation defined, restarting map.\n"); + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "No rotation defined, restarting map.\n"); if (!Dvar::Var("sv_cheats").get()) { @@ -194,11 +243,11 @@ namespace Components // First, check if the string contains nothing if (Dvar::Var("sv_mapRotationCurrent").get().empty()) { - Logger::Print("Current map rotation has finished, reloading...\n"); + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Current map rotation has finished, reloading...\n"); if (Dedicated::SVRandomMapRotation.get()) { - Logger::Print("Randomizing map rotation\n"); + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Randomizing map rotation\n"); Dedicated::RandomizeMapRotation(); } else @@ -207,48 +256,7 @@ namespace Components } } - auto rotation = Dvar::Var("sv_mapRotationCurrent").get(); - - auto tokens = Utils::String::Split(rotation, ' '); - - for (unsigned int i = 0; i < (tokens.size() - 1); i += 2) - { - if (i + 1 >= tokens.size()) - { - Dvar::Var("sv_mapRotationCurrent").set(""); - Command::Execute("map_rotate", true); - return; - } - - std::string key = tokens[i]; - std::string value = tokens[i + 1]; - - if (key == "map") - { - // Rebuild map rotation string - rotation.clear(); - for (unsigned int j = (i + 2); j < tokens.size(); ++j) - { - if (j != (i + 2)) rotation += " "; - rotation += tokens[j]; - } - - Dvar::Var("sv_mapRotationCurrent").set(rotation); - - Logger::Print("Loading new map: %s\n", value.data()); - Command::Execute(Utils::String::VA("map %s", value.data()), true); - break; - } - else if (key == "gametype") - { - Logger::Print("Applying new gametype: %s\n", value.data()); - Dvar::Var("g_gametype").set(value); - } - else - { - Logger::Print("Unsupported maprotation key '%s', motherfucker!\n", key.data()); - } - } + Dedicated::ApplyMapRotation(); } void Dedicated::Heartbeat() @@ -264,7 +272,7 @@ namespace Components Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort)); - Logger::Print("Sending heartbeat to master: %s:%u\n", masterServerName, masterPort); + Logger::Print(Game::conChannel_t::CON_CHANNEL_SERVER, "Sending heartbeat to master: %s:%u\n", masterServerName, masterPort); Network::SendCommand(master, "heartbeat", "IW4"); } @@ -290,8 +298,13 @@ namespace Components { // Map rotation Utils::Hook::Set(0x4152E8, Dedicated::MapRotate); - Dvar::Register("sv_dontrotate", false, Game::dvar_flag::DVAR_CHEAT, ""); - Dvar::Register("com_logFilter", true, Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); + Dvar::OnInit([] + { + Dedicated::SVDontRotate = Dvar::Register("sv_dontRotate", false, + Game::dvar_flag::DVAR_NONE, ""); + Dedicated::COMLogFilter = Dvar::Register("com_logFilter", true, + Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); + }); if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) { diff --git a/src/Components/Modules/Dedicated.hpp b/src/Components/Modules/Dedicated.hpp index 80b39795..a006b635 100644 --- a/src/Components/Modules/Dedicated.hpp +++ b/src/Components/Modules/Dedicated.hpp @@ -9,6 +9,8 @@ namespace Components static SteamID PlayerGuids[18][2]; static Dvar::Var SVLanOnly; + static Dvar::Var SVDontRotate; + static Dvar::Var COMLogFilter; static bool IsEnabled(); @@ -18,6 +20,7 @@ namespace Components static Dvar::Var SVRandomMapRotation; static void RandomizeMapRotation(); + static void ApplyMapRotation(); static void MapRotate(); static void InitDedicatedServer(); diff --git a/src/Components/Modules/Security.cpp b/src/Components/Modules/Security.cpp index aefa116e..f60922a3 100644 --- a/src/Components/Modules/Security.cpp +++ b/src/Components/Modules/Security.cpp @@ -45,13 +45,29 @@ namespace Components if (params.size() >= 4) { - const auto* dvarName = params[3]; - const auto* dvar = Game::Dvar_FindVar(dvarName); - - if (Command::Find(dvarName) || - (dvar != nullptr && dvar->flags & (Game::DVAR_WRITEPROTECTED | Game::DVAR_CHEAT | Game::DVAR_READONLY))) + const auto* name = params.get(3); + // If it's a command don't execute it + if (Command::Find(name) != nullptr) { - Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: illegal parameter\n"); + Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: parameter is a command\n"); + return; + } + + const auto* dvar = Game::Dvar_FindVar(name); + if (dvar == nullptr) + { + // If it's not a dvar let it continue + Game::CL_SelectStringTableEntryInDvar_f(); + return; + } + + constexpr auto disallowedFlags = (Game::DVAR_CHEAT | Game::DVAR_WRITEPROTECTED + | Game::DVAR_READONLY | Game::DVAR_EXTERNAL | Game::DVAR_LATCH); + + // If it's a dvar check that it does not have disallowed flags + if ((dvar->flags & disallowedFlags) != 0) + { + Logger::Print(0, "CL_SelectStringTableEntryInDvar_f: parameter is a protected dvar\n"); return; } } diff --git a/src/Components/Modules/SlowMotion.cpp b/src/Components/Modules/SlowMotion.cpp index 12f74e7e..b1247bd9 100644 --- a/src/Components/Modules/SlowMotion.cpp +++ b/src/Components/Modules/SlowMotion.cpp @@ -45,7 +45,7 @@ namespace Components if (Game::Scr_GetNumParam() >= 3u) { - duration = static_cast(Game::Scr_GetFloat(2) * 1000.0); + duration = static_cast(Game::Scr_GetFloat(2) * 1000.0f); } int delay = 0; @@ -70,16 +70,16 @@ namespace Components // set snapshot num to 1 behind (T6 does this, why shouldn't we?) for (int i = 0; i < *Game::svs_clientCount; ++i) { - Game::svs_clients[i].snapNum = *reinterpret_cast(0x31D9384) - 1; + Game::svs_clients[i].snapNum = *Game::svs_time - 1; } } void SlowMotion::DrawConnectionInterruptedStub(int /*a1*/) { - // if (!*reinterpret_cast(0x1AD8ED0) && !*reinterpret_cast(0x1AD8EEC) && !*reinterpret_cast(0x1AD78F8)) - // { - // Utils::Hook::Call(0x454A70)(a1); - // } + // if (!*reinterpret_cast(0x1AD8ED0) && !*reinterpret_cast(0x1AD8EEC) && !*reinterpret_cast(0x1AD78F8)) + // { + // Utils::Hook::Call(0x454A70)(a1); + // } } SlowMotion::SlowMotion() diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 38dc86e1..eec32beb 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -503,7 +503,7 @@ namespace Game FxEffectDef*** varFxEffectDefHandle = reinterpret_cast(0x112ACC0); PhysCollmap*** varPhysCollmapPtr = reinterpret_cast(0x112B440); PhysPreset*** varPhysPresetPtr = reinterpret_cast(0x112B378); - Game::MaterialPass** varMaterialPass = reinterpret_cast(0x112A960); + MaterialPass** varMaterialPass = reinterpret_cast(0x112A960); snd_alias_list_t*** varsnd_alias_list_name = reinterpret_cast(0x112AF38); FxElemField* s_elemFields = reinterpret_cast(0x73B848); @@ -566,6 +566,8 @@ namespace Game int* g_waitingForKey = reinterpret_cast(0x63A50FC); + unsigned long* _tls_index = reinterpret_cast(0x66D94A8); + void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 672a6cc0..583077e5 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1170,6 +1170,8 @@ namespace Game extern int* g_waitingForKey; + extern unsigned long* _tls_index; + void Sys_LockRead(FastCriticalSection* critSect); void Sys_UnlockRead(FastCriticalSection* critSect); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index ec9595e3..c2e06727 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -135,24 +135,24 @@ namespace Game enum dvar_flag : unsigned __int16 { - DVAR_NONE = 0x0, // No flags - DVAR_ARCHIVE = 0x1, // Set to cause it to be saved to config_mp.cfg of the client - DVAR_LATCH = 0x2, // Will only change when C code next does a Dvar_Get(), so it can't be changed + DVAR_NONE = 0, // No flags + DVAR_ARCHIVE = 1 << 0, // Set to cause it to be saved to config_mp.cfg of the client + DVAR_LATCH = 1 << 1, // Will only change when C code next does a Dvar_Get(), so it can't be changed // without proper initialization. Modified will be set, even though the value hasn't changed yet - DVAR_CHEAT = 0x4, // Can not be changed if cheats are disabled - DVAR_CODINFO = 0x8, // On change, this is sent to all clients (if you are host) - DVAR_SCRIPTINFO = 0x10, - DVAR_UNKNOWN20 = 0x20, - DVAR_CHANGEABLE_RESET = 0x40, - DVAR_UNKNOWN80 = 0x80, - DVAR_EXTERNAL = 0x100, // Created by a set command - DVAR_USERINFO = 0x200, // Sent to server on connect or change - DVAR_SERVERINFO = 0x400, // Sent in response to front end requests - DVAR_WRITEPROTECTED = 0x800, - DVAR_SYSTEMINFO = 0x1000, // Will be duplicated on all clients - DVAR_READONLY = 0x2000, // Read only (same as DVAR_WRITEPROTECTED?) - DVAR_SAVED = 0x4000, - DVAR_AUTOEXEC = 0x8000, + DVAR_CHEAT = 1 << 2, // Can not be changed if cheats are disabled + DVAR_CODINFO = 1 << 3, // On change, this is sent to all clients (if you are host) + DVAR_SCRIPTINFO = 1 << 4, + DVAR_UNKNOWN20 = 1 << 5, + DVAR_CHANGEABLE_RESET = 1 << 6, + DVAR_UNKNOWN80 = 1 << 7, + DVAR_EXTERNAL = 1 << 8, // Created by a set command + DVAR_USERINFO = 1 << 9, // Sent to server on connect or change + DVAR_SERVERINFO = 1 << 10, // Sent in response to front end requests + DVAR_WRITEPROTECTED = 1 << 11, + DVAR_SYSTEMINFO = 1 << 12, // Will be duplicated on all clients + DVAR_READONLY = 1 << 13, // Read only (same as DVAR_WRITEPROTECTED?) + DVAR_SAVED = 1 << 14, + DVAR_AUTOEXEC = 1 << 15, // isLoadingAutoExecGlobalFlag is always false so it should be never set by the game }; enum ImageCategory : char @@ -234,6 +234,13 @@ namespace Game CS_ACTIVE = 0x5, } clientstate_t; + enum serverState_t + { + SS_DEAD = 0x0, + SS_LOADING = 0x1, + SS_GAME = 0x2, + }; + enum errorParm_t { ERR_FATAL = 0x0, @@ -1635,7 +1642,13 @@ namespace Game unsigned __int16 weapon; int legsAnim; int torsoAnim; - int un1; + union + { + int eventParm2; + int hintString; + int fxId; + int helicopterStage; + } un1; int un2; clientLinkInfo_t clientLinkInfo; unsigned int partBits[6]; @@ -2146,6 +2159,8 @@ namespace Game cLeaf_t leaf; }; + static_assert(sizeof(cmodel_t) == 0x44); + struct TriggerModel { int contents;