From 7272ea416498f42fc586ef19dcc6379aa009ddee Mon Sep 17 00:00:00 2001 From: Force67 Date: Sat, 16 Apr 2022 16:31:57 +0200 Subject: [PATCH] fix(ModPolicy): Make sure there are no workarounds possible. --- .../Services/Generic/TransportService.cpp | 21 ++++--- Code/server/GameServer.cpp | 58 ++++++++++++++----- Code/server/main.cpp | 2 + 3 files changed, 61 insertions(+), 20 deletions(-) diff --git a/Code/client/Services/Generic/TransportService.cpp b/Code/client/Services/Generic/TransportService.cpp index 523c22329..632f01658 100644 --- a/Code/client/Services/Generic/TransportService.cpp +++ b/Code/client/Services/Generic/TransportService.cpp @@ -279,18 +279,25 @@ void TransportService::HandleAuthenticationResponse(const AuthenticationResponse case AuthenticationResponse::ResponseType::kAccepted: { m_connected = true; - - // Dispatch the mods to anyone who needs it m_dispatcher.trigger(acMessage.UserMods); - - // Notify we are ready for action m_dispatcher.trigger(ConnectedEvent()); break; } + // TODO(Anyone): Handle this within the ui + case AuthenticationResponse::ResponseType::kWrongVersion: + spdlog::error("This server expects version {} but you are on version {}", acMessage.Version, BUILD_COMMIT); + break; + case AuthenticationResponse::ResponseType::kMissingMods: { + spdlog::error("This server has ModPolicy enabled. You were kicked because you have the following mods installed:"); + for (const auto& m : acMessage.UserMods.ModList) + { + spdlog::error("{}:{}", m.Filename.c_str(), m.Id); + } + spdlog::error("Please remove them to join"); + break; + } default: - case AuthenticationResponse::ResponseType::kMissingMods: - // TODO: this message should be handeled elsewhere... - // (OR dispatched). + spdlog::error("The server refused connection without reason."); break; } } diff --git a/Code/server/GameServer.cpp b/Code/server/GameServer.cpp index f230c0529..1bf1a854e 100644 --- a/Code/server/GameServer.cpp +++ b/Code/server/GameServer.cpp @@ -37,16 +37,17 @@ Console::StringSetting sServerIconURL{"GameServer:sIconUrl", "URL to the image t Console::StringSetting sTagList{"GameServer:sTagList", "List of tags, separated by a comma (,)", ""}; Console::StringSetting sAdminPassword{"GameServer:sAdminPassword", "Admin authentication password", ""}; Console::StringSetting sToken{"GameServer:sToken", "Admin token", ""}; - +Console::Setting bBypassMoPo{"ModPolicy:bBypass", "Bypass the mod policy restrictions.", false, + Console::SettingsFlags::kHidden | Console::SettingsFlags::kLocked}; // -- Commands -- Console::Command TogglePremium("TogglePremium", "Toggle the premium mode", [](Console::ArgStack& aStack) { bPremiumTickrate = aStack.Pop(); }); Console::Command<> ShowVersion("version", "Show the version the server was compiled with", [](Console::ArgStack&) { spdlog::get("ConOut")->info("Server " BUILD_COMMIT); }); - -Console::Setting bBypassMoPo{"ModPolicy:bBypass", "Bypass the mod policy restrictions.", false, - Console::SettingsFlags::kHidden | Console::SettingsFlags::kLocked}; +Console::Command<> ShowMoPoStatus("isMoPoActive", "Shows if the ModPolicy is active", [](Console::ArgStack&) { + spdlog::get("ConOut")->info("ModPolicy status: {}", bBypassMoPo ? "not active" : "active"); +}); // -- Constants -- constexpr char kBypassMoPoWarning[]{ @@ -66,6 +67,11 @@ static uint16_t GetUserTickRate() return bPremiumTickrate ? 60 : 30; } +static bool IsMoPoActive() +{ + return !bBypassMoPo; +} + GameServer* GameServer::s_pInstance = nullptr; GameServer::GameServer(Console::ConsoleRegistry& aConsole) noexcept @@ -469,26 +475,52 @@ void GameServer::HandleAuthenticationRequest(const ConnectionId_t aConnectionId, Mods& responseList = serverResponse.UserMods; auto& modsComponent = m_pWorld->ctx(); - if (!bBypassMoPo) + if (IsMoPoActive()) { - Mods missingMods; - for (const Mods::Entry& mod : acRequest->UserMods.ModList) + // mods that exist on the client, but not on the server + // modscomponent contains a list filled in by the recordcollection + Mods modsToRemove; + + const auto& userMods = acRequest->UserMods.ModList; + for (const Mods::Entry& mod : userMods) { - // modscomponent contains a list filled in by the recordcollection + // if the client has more mods than the server.. if (!modsComponent.IsInstalled(mod.Filename)) { - missingMods.ModList.push_back(mod); + modsToRemove.ModList.push_back(mod); + } + } + + // TODO(Vince): if you have a better to do this than two for loops + // let me know! + // Also, for the future, lets think about a mode that allows more than the server installed mods + // but requires essential mods? + + // mods that may exist on the server, but not on the client + for (const auto& entry : modsComponent.GetServerMods()) + { + const auto it = std::find_if(userMods.begin(), userMods.end(), + [&](const Mods::Entry& it) { return it.Filename == entry.first; }); + + if (it == userMods.end()) + { + Mods::Entry removeEntry; + removeEntry.Filename = entry.first; + removeEntry.Id = 0; + modsToRemove.ModList.push_back(removeEntry); } } - if (missingMods.ModList.size() > 0) + if (modsToRemove.ModList.size() > 0) { - String text = PrettyPrintModList(missingMods.ModList); - spdlog::info("Modpolicy: refusing connection {:x} because essential mods are missing: {}", + String text = PrettyPrintModList(modsToRemove.ModList); + // "ModPolicy: refusing connection {:x} because essential mods are missing: {}" + // for future reference ^ + spdlog::info("ModPolicy: refusing connection {:x} because the following mods are installed on the client: {}", aConnectionId, text.c_str()); serverResponse.Type = AuthenticationResponse::ResponseType::kMissingMods; - serverResponse.UserMods.ModList = std::move(missingMods.ModList); + serverResponse.UserMods.ModList = std::move(modsToRemove.ModList); Send(aConnectionId, serverResponse); // This is a lingering kick, so sending the response should still succeed. Kick(aConnectionId); diff --git a/Code/server/main.cpp b/Code/server/main.cpp index 8fbfb79e7..8b4d76898 100644 --- a/Code/server/main.cpp +++ b/Code/server/main.cpp @@ -174,6 +174,8 @@ void DediRunner::StartTerminalIO() while (m_gameServer.IsRunning()) { + // should have a isDirty flag and flush if, every x seconds. + std::string s; std::getline(std::cin, s); if (!m_console.TryExecuteCommand(s))