[MapRotation]: Refactor (#916)

This commit is contained in:
Edo 2023-04-09 18:26:25 +02:00 committed by GitHub
parent 1f94353e1a
commit 9b1f49252e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 96 additions and 35 deletions

View File

@ -121,7 +121,7 @@ namespace Components
return; return;
} }
command.append("\n"); // Make sure it's terminated command.push_back('\n'); // Make sure it's terminated
assert(command.size() < Game::MAX_CMD_LINE); assert(command.size() < Game::MAX_CMD_LINE);

View File

@ -1,5 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "MapRotation.hpp" #include "MapRotation.hpp"
#include "Party.hpp"
namespace Components namespace Components
{ {
@ -25,7 +26,7 @@ namespace Components
void MapRotation::RotationData::addEntry(const std::string& key, const std::string& value) void MapRotation::RotationData::addEntry(const std::string& key, const std::string& value)
{ {
this->rotationEntries_.emplace_back(std::make_pair(key, value)); this->rotationEntries_.emplace_back(key, value);
} }
std::size_t MapRotation::RotationData::getEntriesSize() const noexcept std::size_t MapRotation::RotationData::getEntriesSize() const noexcept
@ -36,7 +37,7 @@ namespace Components
MapRotation::RotationData::rotationEntry& MapRotation::RotationData::getNextEntry() MapRotation::RotationData::rotationEntry& MapRotation::RotationData::getNextEntry()
{ {
const auto index = this->index_; const auto index = this->index_;
++this->index_ %= this->rotationEntries_.size(); // Point index_ to the next entry ++this->index_ %= this->rotationEntries_.size();
return this->rotationEntries_.at(index); return this->rotationEntries_.at(index);
} }
@ -45,6 +46,19 @@ namespace Components
return this->rotationEntries_.at(this->index_); return this->rotationEntries_.at(this->index_);
} }
void MapRotation::RotationData::setHandler(const std::string& key, const rotationCallback& callback)
{
this->rotationHandlers_[key] = callback;
}
void MapRotation::RotationData::callHandler(const rotationEntry& entry) const
{
if (const auto itr = this->rotationHandlers_.find(entry.first); itr != this->rotationHandlers_.end())
{
itr->second(entry.second);
}
}
void MapRotation::RotationData::parse(const std::string& data) void MapRotation::RotationData::parse(const std::string& data)
{ {
const auto tokens = Utils::String::Split(data, ' '); const auto tokens = Utils::String::Split(data, ' ');
@ -54,15 +68,13 @@ namespace Components
const auto& key = tokens[i]; const auto& key = tokens[i];
const auto& value = tokens[i + 1]; const auto& value = tokens[i + 1];
if (key == "map"s || key == "gametype"s) if (!this->containsHandler(key))
{ {
throw MapRotationParseError(std::format("Invalid key {}", key));
}
this->addEntry(key, value); this->addEntry(key, value);
} }
else
{
throw MapRotationParseError();
}
}
} }
bool MapRotation::RotationData::empty() const noexcept bool MapRotation::RotationData::empty() const noexcept
@ -78,6 +90,16 @@ namespace Components
}); });
} }
bool MapRotation::RotationData::containsHandler(const std::string& key) const
{
return this->rotationHandlers_.contains(key);
}
void MapRotation::RotationData::clear() noexcept
{
this->rotationEntries_.clear();
}
nlohmann::json MapRotation::RotationData::to_json() const nlohmann::json MapRotation::RotationData::to_json() const
{ {
std::vector<std::string> mapVector; std::vector<std::string> mapVector;
@ -97,8 +119,8 @@ namespace Components
auto mapRotationJson = nlohmann::json auto mapRotationJson = nlohmann::json
{ {
{"maps", mapVector}, { "maps", mapVector },
{"gametypes", gametypeVector}, { "gametypes", gametypeVector },
}; };
return mapRotationJson; return mapRotationJson;
@ -183,7 +205,7 @@ namespace Components
return false; return false;
} }
if (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>()) if (Party::IsEnabled() && Dvar::Var("party_host").get<bool>())
{ {
Logger::Warning(Game::CON_CHANNEL_SERVER, "Not performing map rotation as we are hosting a party!\n"); Logger::Warning(Game::CON_CHANNEL_SERVER, "Not performing map rotation as we are hosting a party!\n");
return false; return false;
@ -212,6 +234,13 @@ namespace Components
Game::Dvar_SetStringByName("g_gametype", gametype.data()); Game::Dvar_SetStringByName("g_gametype", gametype.data());
} }
void MapRotation::ApplyExec(const std::string& name)
{
assert(!name.empty());
Command::Execute(std::format("exec {}", name), false);
Command::Execute(std::format("exec game_settings/{}", name), false);
}
void MapRotation::RestartCurrentMap() void MapRotation::RestartCurrentMap()
{ {
std::string svMapname = (*Game::sv_mapname)->current.string; std::string svMapname = (*Game::sv_mapname)->current.string;
@ -234,21 +263,15 @@ namespace Components
while (i < rotation.getEntriesSize()) while (i < rotation.getEntriesSize())
{ {
const auto& entry = rotation.getNextEntry(); const auto& entry = rotation.getNextEntry();
rotation.callHandler(entry);
Logger::Print("MapRotation: applying key '{}' with value '{}'\n", entry.first, entry.second);
if (entry.first == "map"s) if (entry.first == "map"s)
{ {
Logger::Print("Loading new map: '{}'", entry.second);
ApplyMap(entry.second);
// Map was found so we exit the loop // Map was found so we exit the loop
break; break;
} }
if (entry.first == "gametype"s)
{
Logger::Print("Applying new gametype: '{}'", entry.second);
ApplyGametype(entry.second);
}
++i; ++i;
} }
@ -374,13 +397,15 @@ namespace Components
AddMapRotationCommands(); AddMapRotationCommands();
Utils::Hook::Set<void(*)()>(0x4152E8, SV_MapRotate_f); Utils::Hook::Set<void(*)()>(0x4152E8, SV_MapRotate_f);
DedicatedRotation.setHandler("map", ApplyMap);
DedicatedRotation.setHandler("gametype", ApplyGametype);
DedicatedRotation.setHandler("exec", ApplyExec);
Events::OnDvarInit(RegisterMapRotationDvars); Events::OnDvarInit(RegisterMapRotationDvars);
} }
bool MapRotation::unitTest() bool MapRotation::unitTest()
{ {
RotationData rotation;
Logger::Debug("Testing map rotation parsing..."); Logger::Debug("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"; const auto* normal = "map mp_highrise map mp_terminal map mp_firingrange map mp_trailerpark gametype dm map mp_shipment_long";
@ -395,6 +420,8 @@ namespace Components
return false; return false;
} }
DedicatedRotation.clear();
const auto* mistake = "spdevmap mp_dome"; const auto* mistake = "spdevmap mp_dome";
auto success = false; auto success = false;

View File

@ -14,9 +14,27 @@ namespace Components
bool unitTest() override; bool unitTest() override;
private: private:
struct MapRotationParseError : public std::exception class MapRotationParseError : public std::runtime_error
{ {
[[nodiscard]] const char* what() const noexcept override { return "Map Rotation Parse Error"; } private:
static std::string fmt(const std::string& message)
{
std::string error = "Map Rotation Parse Error";
if (!message.empty())
{
error.append(": ");
error.append(message);
}
return error;
}
public:
MapRotationParseError(const std::string& message)
: std::runtime_error(fmt(message))
{
}
}; };
class RotationData class RotationData
@ -24,6 +42,8 @@ namespace Components
public: public:
using rotationEntry = std::pair<std::string, std::string>; using rotationEntry = std::pair<std::string, std::string>;
using rotationCallback = std::function<void(const std::string&)>;
RotationData(); RotationData();
void randomize(); void randomize();
@ -36,15 +56,22 @@ namespace Components
rotationEntry& getNextEntry(); rotationEntry& getNextEntry();
rotationEntry& peekNextEntry(); rotationEntry& peekNextEntry();
void setHandler(const std::string& key, const rotationCallback& callback);
void callHandler(const rotationEntry& entry) const;
void parse(const std::string& data); void parse(const std::string& data);
[[nodiscard]] bool empty() const noexcept; [[nodiscard]] bool empty() const noexcept;
[[nodiscard]] bool contains(const std::string& key, const std::string& value) const; [[nodiscard]] bool contains(const std::string& key, const std::string& value) const;
[[nodiscard]] bool containsHandler(const std::string& key) const;
void clear() noexcept;
[[nodiscard]] nlohmann::json to_json() const; [[nodiscard]] nlohmann::json to_json() const;
private: private:
std::vector<rotationEntry> rotationEntries_; std::vector<rotationEntry> rotationEntries_;
std::unordered_map<std::string, rotationCallback> rotationHandlers_;
std::size_t index_; std::size_t index_;
}; };
@ -67,6 +94,7 @@ namespace Components
static bool ShouldRotate(); static bool ShouldRotate();
static void ApplyMap(const std::string& map); static void ApplyMap(const std::string& map);
static void ApplyGametype(const std::string& gametype); static void ApplyGametype(const std::string& gametype);
static void ApplyExec(const std::string& name);
static void RestartCurrentMap(); static void RestartCurrentMap();
static void ApplyRotation(RotationData& rotation); static void ApplyRotation(RotationData& rotation);
static void ApplyMapRotationCurrent(const std::string& data); static void ApplyMapRotationCurrent(const std::string& data);

View File

@ -316,11 +316,17 @@ namespace Components
void Maps::GetBSPName(char* buffer, size_t size, const char* format, const char* mapname) void Maps::GetBSPName(char* buffer, size_t size, const char* format, const char* mapname)
{ {
if (!Utils::String::StartsWith(mapname, "mp_") && !Utils::String::StartsWith(mapname, "zm_")) if (!Utils::String::StartsWith(mapname, "mp_"))
{ {
format = "maps/%s.d3dbsp"; format = "maps/%s.d3dbsp";
} }
// TODO: Remove this hack by using CoD4 version of the map
if (std::strcmp(mapname, "mp_shipment") == 0)
{
mapname = "mp_shipment_long";
}
_snprintf_s(buffer, size, _TRUNCATE, format, mapname); _snprintf_s(buffer, size, _TRUNCATE, format, mapname);
} }

View File

@ -4,7 +4,7 @@ namespace Components
{ {
Utils::Signal<Network::CallbackRaw> Network::StartupSignal; Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
// Packet interception // Packet interception
std::unordered_map<std::string, Network::NetworkCallback> Network::CL_Callbacks; std::unordered_map<std::string, Network::networkCallback> Network::CL_Callbacks;
Network::Address::Address() Network::Address::Address()
{ {
@ -283,7 +283,7 @@ namespace Components
Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg); Utils::Hook::Call<void(Game::client_t*, Game::msg_t*)>(0x414D40)(client, msg);
} }
void Network::OnClientPacket(const std::string& command, const NetworkCallback& callback) void Network::OnClientPacket(const std::string& command, const networkCallback& callback)
{ {
CL_Callbacks[Utils::String::ToLower(command)] = callback; CL_Callbacks[Utils::String::ToLower(command)] = callback;
} }

View File

@ -48,7 +48,7 @@ namespace Components
typedef void(CallbackRaw)(); typedef void(CallbackRaw)();
using NetworkCallback = std::function<void(Address&, const std::string&)>; using networkCallback = std::function<void(Address&, const std::string&)>;
Network(); Network();
@ -72,11 +72,11 @@ namespace Components
static void BroadcastRange(unsigned int min, unsigned int max, const std::string& data); static void BroadcastRange(unsigned int min, unsigned int max, const std::string& data);
static void BroadcastAll(const std::string& data); static void BroadcastAll(const std::string& data);
static void OnClientPacket(const std::string& command, const NetworkCallback& callback); static void OnClientPacket(const std::string& command, const networkCallback& callback);
private: private:
static Utils::Signal<CallbackRaw> StartupSignal; static Utils::Signal<CallbackRaw> StartupSignal;
static std::unordered_map<std::string, NetworkCallback> CL_Callbacks; static std::unordered_map<std::string, networkCallback> CL_Callbacks;
static void NetworkStart(); static void NetworkStart();
static void NetworkStartStub(); static void NetworkStartStub();

View File

@ -14,7 +14,7 @@ namespace Components
Utils::Cryptography::ECC::Key Session::SignatureKey; Utils::Cryptography::ECC::Key Session::SignatureKey;
std::unordered_map<std::string, Network::NetworkCallback> Session::PacketHandlers; std::unordered_map<std::string, Network::networkCallback> Session::PacketHandlers;
std::queue<std::pair<Network::Address, std::string>> Session::SignatureQueue; std::queue<std::pair<Network::Address, std::string>> Session::SignatureQueue;
@ -61,7 +61,7 @@ namespace Components
#endif #endif
} }
void Session::Handle(const std::string& packet, const Network::NetworkCallback& callback) void Session::Handle(const std::string& packet, const Network::networkCallback& callback)
{ {
#ifdef DISABLE_SESSION #ifdef DISABLE_SESSION
Network::OnClientPacket(packet, callback); Network::OnClientPacket(packet, callback);

View File

@ -35,7 +35,7 @@ namespace Components
void preDestroy() override; void preDestroy() override;
static void Send(const Network::Address& target, const std::string& command, const std::string& data = ""); static void Send(const Network::Address& target, const std::string& command, const std::string& data = "");
static void Handle(const std::string& packet, const Network::NetworkCallback& callback); static void Handle(const std::string& packet, const Network::networkCallback& callback);
private: private:
static volatile bool Terminate; static volatile bool Terminate;
@ -46,7 +46,7 @@ namespace Components
static Utils::Cryptography::ECC::Key SignatureKey; static Utils::Cryptography::ECC::Key SignatureKey;
static std::unordered_map<std::string, Network::NetworkCallback> PacketHandlers; static std::unordered_map<std::string, Network::networkCallback> PacketHandlers;
static std::queue<std::pair<Network::Address, std::string>> SignatureQueue; static std::queue<std::pair<Network::Address, std::string>> SignatureQueue;