[MapRotation] Allow people to circumvent 1024 dvar character limit

This commit is contained in:
Diavolo 2022-06-13 17:32:45 +02:00
parent 020201ab1f
commit bf4ae7f116
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
7 changed files with 130 additions and 37 deletions

View File

@ -175,13 +175,13 @@ namespace Components
Logger::Print("Performing unit tests for components:\n"); Logger::Print("Performing unit tests for components:\n");
for (auto component : Loader::Components) for (const auto component : Loader::Components)
{ {
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) #if defined(FORCE_UNIT_TESTS)
Logger::Print("Testing '{}'...\n", component->getName()); Logger::Debug("Testing '{}'...\n", component->getName());
#endif #endif
auto startTime = std::chrono::high_resolution_clock::now(); auto startTime = std::chrono::high_resolution_clock::now();
bool testRes = component->unitTest(); auto testRes = component->unitTest();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count();
Logger::Print("Test done ({}ms): {}\n\n", duration, (testRes ? "Success" : "Error")); Logger::Print("Test done ({}ms): {}\n\n", duration, (testRes ? "Success" : "Error"));
result &= testRes; result &= testRes;

View File

@ -5,8 +5,8 @@ namespace Components
class Component class Component
{ {
public: public:
Component() {} Component() = default;
virtual ~Component() {} virtual ~Component() = default;
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) #if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
virtual std::string getName() virtual std::string getName()

View File

@ -89,21 +89,21 @@ namespace Components
std::vector<std::string> idVector; std::vector<std::string> idVector;
std::vector<std::string> ipVector; std::vector<std::string> ipVector;
for (auto& idEntry : list->idList) for (const auto& idEntry : list->idList)
{ {
idVector.push_back(Utils::String::VA("%llX", idEntry.bits)); idVector.emplace_back(Utils::String::VA("%llX", idEntry.bits));
} }
for (auto& ipEntry : list->ipList) for (const auto& ipEntry : list->ipList)
{ {
ipVector.push_back(Utils::String::VA("%u.%u.%u.%u", ipVector.emplace_back(Utils::String::VA("%u.%u.%u.%u",
ipEntry.bytes[0] & 0xFF, ipEntry.bytes[0] & 0xFF,
ipEntry.bytes[1] & 0xFF, ipEntry.bytes[1] & 0xFF,
ipEntry.bytes[2] & 0xFF, ipEntry.bytes[2] & 0xFF,
ipEntry.bytes[3] & 0xFF)); ipEntry.bytes[3] & 0xFF));
} }
json11::Json bans = json11::Json::object const json11::Json bans = json11::Json::object
{ {
{ "ip", ipVector }, { "ip", ipVector },
{ "id", idVector }, { "id", idVector },

View File

@ -113,19 +113,19 @@ namespace Components
void Logger::Frame() void Logger::Frame()
{ {
std::lock_guard<std::mutex> _(Logger::MessageMutex); std::unique_lock _(Logger::MessageMutex);
for (std::size_t i = 0; i < Logger::MessageQueue.size(); ++i) for (auto i = Logger::MessageQueue.begin(); i != Logger::MessageQueue.end();)
{ {
Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, Logger::MessageQueue[i].data(), 0); Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0);
if (!Logger::IsConsoleReady()) if (!Logger::IsConsoleReady())
{ {
OutputDebugStringA(Logger::MessageQueue[i].data()); OutputDebugStringA(i->data());
} }
}
Logger::MessageQueue.clear(); i = Logger::MessageQueue.erase(i);
}
} }
void Logger::PipeOutput(void(*callback)(const std::string&)) void Logger::PipeOutput(void(*callback)(const std::string&))
@ -203,9 +203,8 @@ namespace Components
void Logger::EnqueueMessage(const std::string& message) void Logger::EnqueueMessage(const std::string& message)
{ {
Logger::MessageMutex.lock(); std::unique_lock _(Logger::MessageMutex);
Logger::MessageQueue.push_back(message); Logger::MessageQueue.push_back(message);
Logger::MessageMutex.unlock();
} }
void Logger::RedirectOSPath(const char* file, char* folder) void Logger::RedirectOSPath(const char* file, char* folder)
@ -375,9 +374,9 @@ namespace Components
Logger::LoggingAddresses[0].clear(); Logger::LoggingAddresses[0].clear();
Logger::LoggingAddresses[1].clear(); Logger::LoggingAddresses[1].clear();
Logger::MessageMutex.lock(); std::unique_lock lock(Logger::MessageMutex);
Logger::MessageQueue.clear(); Logger::MessageQueue.clear();
Logger::MessageMutex.unlock(); lock.unlock();
// Flush the console log // Flush the console log
if (const auto logfile = *reinterpret_cast<int*>(0x1AD8F28)) if (const auto logfile = *reinterpret_cast<int*>(0x1AD8F28))

View File

@ -61,6 +61,33 @@ namespace Components
} }
} }
json11::Json MapRotation::RotationData::to_json() const
{
std::vector<std::string> mapVector;
std::vector<std::string> gametypeVector;
for (const auto& [key, val] : this->rotationEntries_)
{
if (key == "map")
{
mapVector.emplace_back(val);
}
else if (key == "gametype")
{
gametypeVector.emplace_back(val);
}
}
json11::Json mapRotationJson = json11::Json::object
{
{"maps", mapVector},
{"gametypes", gametypeVector},
};
return mapRotationJson;
}
void MapRotation::LoadRotation(const std::string& data) void MapRotation::LoadRotation(const std::string& data)
{ {
static auto loaded = false; static auto loaded = false;
@ -77,10 +104,10 @@ namespace Components
} }
catch (const std::exception& ex) catch (const std::exception& ex)
{ {
Logger::Print(Game::CON_CHANNEL_SERVER, "%s: sv_mapRotation contains invalid data!\n", ex.what()); Logger::Print(Game::CON_CHANNEL_SERVER, "{}: sv_mapRotation contains invalid data!\n", ex.what());
} }
Logger::Print(Game::CON_CHANNEL_SERVER, "DedicatedRotation size after parsing is '%u'\n", DedicatedRotation.getEntriesSize()); Logger::DebugInfo("DedicatedRotation size after parsing is '{}'\n", DedicatedRotation.getEntriesSize());
// Shuffles values // Shuffles values
if (SVRandomMapRotation.get<bool>()) if (SVRandomMapRotation.get<bool>())
@ -92,6 +119,31 @@ namespace Components
loaded = true; loaded = true;
} }
void MapRotation::AddMapRotationCommands()
{
Command::Add("AddMap", [](Command::Params* params)
{
if (params->size() < 2)
{
Logger::Print("{} <map name> : add a map to the map rotation\n", params->get(0));
return;
}
DedicatedRotation.addEntry("map", params->get(1));
});
Command::Add("AddGametype", [](Command::Params* params)
{
if (params->size() < 2)
{
Logger::Print("{} <gametype> : add a game mode to the map rotation\n", params->get(0));
return;
}
DedicatedRotation.addEntry("gametype", params->get(1));
});
}
bool MapRotation::ShouldRotate() bool MapRotation::ShouldRotate()
{ {
if (!Dedicated::IsEnabled() && SVDontRotate.get<bool>()) if (!Dedicated::IsEnabled() && SVDontRotate.get<bool>())
@ -132,7 +184,7 @@ namespace Components
void MapRotation::ApplyMapRotation() void MapRotation::ApplyMapRotation()
{ {
// Continue to apply gamemode until a map is found // Continue to apply gametype until a map is found
auto foundMap = false; auto foundMap = false;
std::size_t i = 0; std::size_t i = 0;
@ -142,15 +194,15 @@ namespace Components
if (entry.first == "map") if (entry.first == "map")
{ {
Logger::Print("Loading new map: '%s'\n", entry.second.data()); Logger::DebugInfo("Loading new map: '{}'\n", entry.second);
Command::Execute(Utils::String::VA("map %s", entry.second.data()), true); Command::Execute(Utils::String::VA("map %s", entry.second.data()), true);
// Map was found so we exit the loop // Map was found so we exit the loop
foundMap = true; foundMap = true;
} }
else if (entry.first == "gamemode") else if (entry.first == "gametype")
{ {
Logger::Print("Applying new gametype: '%s'\n", entry.second.data()); Logger::DebugInfo("Applying new gametype: '{}'\n", entry.second);
Dvar::Var("g_gametype").set(entry.second); Dvar::Var("g_gametype").set(entry.second);
} }
@ -189,6 +241,7 @@ namespace Components
MapRotation::MapRotation() MapRotation::MapRotation()
{ {
AddMapRotationCommands();
Utils::Hook::Set<void(*)()>(0x4152E8, SV_MapRotate_f); Utils::Hook::Set<void(*)()>(0x4152E8, SV_MapRotate_f);
SVRandomMapRotation = Dvar::Register<bool>("sv_randomMapRotation", false, SVRandomMapRotation = Dvar::Register<bool>("sv_randomMapRotation", false,
@ -196,4 +249,38 @@ namespace Components
SVDontRotate = Dvar::Register<bool>("sv_dontRotate", false, SVDontRotate = Dvar::Register<bool>("sv_dontRotate", false,
Game::dvar_flag::DVAR_NONE, "Do not perform map rotation"); Game::dvar_flag::DVAR_NONE, "Do not perform map rotation");
} }
bool MapRotation::unitTest()
{
RotationData rotation;
Logger::DebugInfo("Testing map rotation parsing...");
const auto* normal = "map mp_highrise map mp_terminal map mp_firingrange map mp_trailerpark gametype dm map mp_shipment_long";
try
{
DedicatedRotation.parse(normal);
}
catch (const std::exception& ex)
{
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: parsing of 'normal' failed", ex.what());
return false;
}
const auto* mistake = "spdevmap mp_dome";
auto success = false;
try
{
DedicatedRotation.parse(mistake);
}
catch (const std::exception& ex)
{
Logger::Debug("{}: parsing of 'normal' failed as expected", ex.what());
success = true;
}
return success;
}
} }

View File

@ -7,6 +7,8 @@ namespace Components
public: public:
MapRotation(); MapRotation();
bool unitTest() override;
private: private:
struct ParseRotationError : public std::exception struct ParseRotationError : public std::exception
{ {
@ -31,6 +33,9 @@ namespace Components
void parse(const std::string& data); void parse(const std::string& data);
// Json11 Implicit constructor
[[nodiscard]] json11::Json to_json() const;
private: private:
std::vector<rotationEntry> rotationEntries_; std::vector<rotationEntry> rotationEntries_;
@ -49,6 +54,9 @@ namespace Components
static void LoadRotation(const std::string& data); static void LoadRotation(const std::string& data);
// Use these commands before SV_MapRotate_f is called
static void AddMapRotationCommands();
static bool ShouldRotate(); static bool ShouldRotate();
static void RestartCurrentMap(); static void RestartCurrentMap();
static void ApplyMapRotation(); static void ApplyMapRotation();

View File

@ -731,9 +731,9 @@ namespace Components
bool QuickPatch::unitTest() bool QuickPatch::unitTest()
{ {
uint32_t randIntCount = 4'000'000; uint32_t randIntCount = 4'000'000;
printf("Generating %d random integers...", randIntCount); Logger::Debug("Generating %d random integers...", randIntCount);
auto startTime = std::chrono::high_resolution_clock::now(); const auto startTime = std::chrono::high_resolution_clock::now();
for (uint32_t i = 0; i < randIntCount; ++i) for (uint32_t i = 0; i < randIntCount; ++i)
{ {
@ -741,9 +741,9 @@ namespace Components
} }
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count();
Logger::Print("took {}ms\n", duration); Logger::Debug("took {}ms\n", duration);
printf("Testing ZLib compression..."); Logger::Debug("Testing ZLib compression...");
std::string test = Utils::String::VA("%c", Utils::Cryptography::Rand::GenerateInt()); std::string test = Utils::String::VA("%c", Utils::Cryptography::Rand::GenerateInt());
@ -754,21 +754,20 @@ namespace Components
if (test != decompressed) if (test != decompressed)
{ {
printf("Error\n"); Logger::PrintError(Game::CON_CHANNEL_ERROR, "Compressing {} bytes and decompressing failed!\n", test.size());
printf("Compressing %d bytes and decompressing failed!\n", test.size());
return false; return false;
} }
auto size = test.size(); const auto size = test.size();
for (unsigned int j = 0; j < size; ++j) for (unsigned int j = 0; j < size; ++j)
{ {
test.append(Utils::String::VA("%c", Utils::Cryptography::Rand::GenerateInt())); test.append(Utils::String::VA("%c", Utils::Cryptography::Rand::GenerateInt()));
} }
} }
printf("Success\n"); Logger::Debug("Success");
printf("Testing trimming..."); Logger::Debug("Testing trimming...");
std::string trim1 = " 1 "; std::string trim1 = " 1 ";
std::string trim2 = " 1"; std::string trim2 = " 1";
std::string trim3 = "1 "; std::string trim3 = "1 ";
@ -781,7 +780,7 @@ namespace Components
if (trim2 != "1") return false; if (trim2 != "1") return false;
if (trim3 != "1") return false; if (trim3 != "1") return false;
printf("Success\n"); Logger::Debug("Success");
return true; return true;
} }
} }