[ServerList]: Check for duplicates (#820)

This commit is contained in:
Edo 2023-03-09 18:39:10 +00:00 committed by GitHub
parent a1112ed292
commit c71eddc5a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 160 additions and 138 deletions

View File

@ -471,7 +471,7 @@ namespace Components
}
else
{
const auto level = static_cast<uint32_t>(atoi(params->get(1)));
const auto level = std::strtoul(params->get(1), nullptr, 10);
Auth::IncreaseSecurityLevel(level);
}
});

View File

@ -167,8 +167,10 @@ namespace Components
Scheduler::Once([]
{
auto* ent = Game::SV_AddTestClient();
if (ent == nullptr)
if (!ent)
{
return;
}
Scheduler::Once([ent]
{
@ -232,7 +234,7 @@ namespace Components
const auto* weapon = Game::Scr_GetString(0);
if (weapon == nullptr || weapon[0] == '\0')
if (!weapon || !*weapon)
{
g_botai[entref.entnum].weapon = 1;
return;
@ -255,7 +257,7 @@ namespace Components
const auto* action = Game::Scr_GetString(0);
if (action == nullptr)
if (!action)
{
Game::Scr_ParamError(0, "^1BotAction: Illegal parameter!");
return;
@ -305,8 +307,10 @@ namespace Components
void Bots::BotAiAction(Game::client_t* cl)
{
if (cl->gentity == nullptr)
if (!cl->gentity)
{
return;
}
const auto entnum = cl->gentity->s.number;

View File

@ -205,7 +205,7 @@ namespace Components::GSC
{
assert(ent);
if (ent->client == nullptr)
if (!ent->client)
{
Game::Scr_ObjectError(Utils::String::VA("Entity %i is not a player", ent->s.number));
return nullptr;

View File

@ -28,7 +28,7 @@ namespace Components::GSC
assert(entref.entnum < Game::MAX_GENTITIES);
auto* ent = &Game::g_entities[entref.entnum];
if (ent->client == nullptr)
if (!ent->client)
{
Game::Scr_ObjectError(Utils::String::VA("entity %i is not a player", entref.entnum));
return nullptr;

View File

@ -34,7 +34,7 @@ namespace Components::GSC
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
if (table == nullptr)
if (!table)
{
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist", fileName));
return;

View File

@ -14,7 +14,7 @@ namespace Components::GSC
const auto* key = Game::Scr_GetString(0);
const auto* value = Game::Scr_GetString(1);
if (key == nullptr || value == nullptr)
if (!key || !value)
{
Game::Scr_Error("^1StorageSet: Illegal parameters!");
return;
@ -27,7 +27,7 @@ namespace Components::GSC
{
const auto* key = Game::Scr_GetString(0);
if (key == nullptr)
if (!key)
{
Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!");
return;
@ -46,7 +46,7 @@ namespace Components::GSC
{
const auto* key = Game::Scr_GetString(0);
if (key == nullptr)
if (!key)
{
Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!");
return;
@ -65,7 +65,7 @@ namespace Components::GSC
{
const auto* key = Game::Scr_GetString(0);
if (key == nullptr)
if (!key)
{
Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!");
return;

View File

@ -52,7 +52,7 @@ namespace Components::GSC
const auto* ent = Game::GetPlayerEntity(entref);
const auto* name = Game::Scr_GetString(0);
if (name == nullptr)
if (!name)
{
Game::Scr_ParamError(0, "^1SetName: Illegal parameter!");
return;
@ -77,7 +77,7 @@ namespace Components::GSC
const auto* ent = Game::GetPlayerEntity(entref);
const auto* clanName = Game::Scr_GetString(0);
if (clanName == nullptr)
if (!clanName)
{
Game::Scr_ParamError(0, "^1SetClanTag: Illegal parameter!");
return;

View File

@ -96,7 +96,7 @@ namespace Components
++(*Game::com_errorPrintsCount);
MessagePrint(channel, msg);
if (Game::cls->uiStarted != 0 && (*Game::com_fixedConsolePosition == 0))
if (Game::cls->uiStarted && (*Game::com_fixedConsolePosition == 0))
{
Game::CL_ConsoleFixPosition();
}

View File

@ -55,7 +55,7 @@ namespace Components
}
const auto* dvar = Game::Dvar_FindVar(name);
if (dvar == nullptr)
if (!dvar)
{
// If it's not a dvar let it continue
Game::CL_SelectStringTableEntryInDvar_f();

View File

@ -194,7 +194,7 @@ namespace Components
auto* list = GetList();
if (!list) return;
std::vector tempList(*list);
const std::vector tempList(*list);
if (tempList.empty())
{
@ -209,7 +209,7 @@ namespace Components
RefreshContainer.sendCount = 0;
RefreshContainer.sentCount = 0;
for (auto& server : tempList)
for (const auto& server : tempList)
{
InsertRequest(server.addr);
}
@ -274,13 +274,7 @@ namespace Components
void ServerList::Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
{
Dvar::Var("ui_serverSelected").set(false);
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
#if 0
OnlineList.clear();
OfflineList.clear();
FavouriteList.clear();
#endif
auto* list = GetList();
if (list) list->clear();
@ -320,7 +314,7 @@ namespace Components
RefreshContainer.awaitTime = Game::Sys_Milliseconds();
RefreshContainer.host = Network::Address(std::format("{}:{}", masterServerName, masterPort));
Logger::Print("Sending serverlist request to master\n");
Logger::Print("Sending server list request to master\n");
Network::SendCommand(RefreshContainer.host, "getservers", std::format("IW4 {} full empty", PROTOCOL));
}
else if (IsFavouriteList())
@ -417,8 +411,6 @@ namespace Components
if (list) list->clear();
RefreshVisibleListInternal(UIScript::Token(), nullptr);
Game::ShowMessageBox("Server removed from favourites.", "Success");
}
void ServerList::LoadFavourties()
@ -471,7 +463,7 @@ namespace Components
container.sent = false;
container.target = address;
bool alreadyInserted = false;
auto alreadyInserted = false;
for (auto &server : RefreshContainer.servers)
{
if (server.target == container.target)
@ -510,99 +502,104 @@ namespace Components
for (auto i = RefreshContainer.servers.begin(); i != RefreshContainer.servers.end();)
{
// Our desired server
if ((i->target == address) && i->sent)
if ((i->target != address) || !i->sent)
{
// Challenge did not match
if (i->challenge != info.get("challenge"))
++i;
continue;
}
// Challenge did not match
if (i->challenge != info.get("challenge"))
{
// Shall we remove the server from the queue?
// Better not, it might send a second response with the correct challenge.
// This might happen when users refresh twice (or more often) in a short period of time
break;
}
ServerInfo server;
server.hostname = info.get("hostname");
server.mapname = info.get("mapname");
server.gametype = info.get("gametype");
server.shortversion = info.get("shortversion");
server.mod = info.get("fs_game");
server.matchType = std::strtol(info.get("matchtype").data(), nullptr, 10);
server.clients = std::strtol(info.get("clients").data(), nullptr, 10);
server.bots = std::strtol(info.get("bots").data(), nullptr, 10);
server.securityLevel = std::strtol(info.get("securityLevel").data(), nullptr, 10);
server.maxClients = std::strtol(info.get("sv_maxclients").data(), nullptr, 10);
server.password = info.get("isPrivate") == "1"s;
server.aimassist = info.get("aimAssist") == "1";
server.voice = info.get("voiceChat") == "1"s;
server.hardcore = info.get("hc") == "1"s;
server.svRunning = info.get("sv_running") == "1"s;
server.ping = (Game::Sys_Milliseconds() - i->sendTime);
server.addr = address;
std::hash<ServerInfo> hashFn;
server.hash = hashFn(server);
server.hostname = TextRenderer::StripMaterialTextIcons(server.hostname);
server.mapname = TextRenderer::StripMaterialTextIcons(server.mapname);
server.gametype = TextRenderer::StripMaterialTextIcons(server.gametype);
server.mod = TextRenderer::StripMaterialTextIcons(server.mod);
// Remove server from queue
i = RefreshContainer.servers.erase(i);
// Servers with more than 18 players or less than 0 players are faking for sure
// So lets ignore those
if (static_cast<std::size_t>(server.clients) > Game::MAX_CLIENTS || static_cast<std::size_t>(server.maxClients) > Game::MAX_CLIENTS)
{
return;
}
// Check if already inserted and remove
auto* list = GetList();
if (!list) return;
std::size_t k = 0;
for (auto j = list->begin(); j != list->end(); ++k)
{
if (j->addr == address)
{
// Shall we remove the server from the queue?
// Better not, it might send a second response with the correct challenge.
// This might happen when users refresh twice (or more often) in a short period of time
break;
j = list->erase(j);
}
ServerInfo server;
server.hostname = info.get("hostname");
server.mapname = info.get("mapname");
server.gametype = info.get("gametype");
server.shortversion = info.get("shortversion");
server.mod = info.get("fs_game");
server.matchType = std::strtol(info.get("matchtype").data(), nullptr, 10);
server.clients = std::strtol(info.get("clients").data(), nullptr, 10);
server.bots = std::strtol(info.get("bots").data(), nullptr, 10);
server.securityLevel = std::strtol(info.get("securityLevel").data(), nullptr, 10);
server.maxClients = std::strtol(info.get("sv_maxclients").data(), nullptr, 10);
server.password = info.get("isPrivate") == "1"s;
server.aimassist = info.get("aimAssist") == "1";
server.voice = info.get("voiceChat") == "1"s;
server.hardcore = info.get("hc") == "1"s;
server.svRunning = info.get("sv_running") == "1"s;
server.ping = (Game::Sys_Milliseconds() - i->sendTime);
server.addr = address;
server.hostname = TextRenderer::StripMaterialTextIcons(server.hostname);
server.mapname = TextRenderer::StripMaterialTextIcons(server.mapname);
server.gametype = TextRenderer::StripMaterialTextIcons(server.gametype);
server.mod = TextRenderer::StripMaterialTextIcons(server.mod);
// Remove server from queue
i = RefreshContainer.servers.erase(i);
// Servers with more than 18 players or less than 0 players are faking for sure
// So lets ignore those
if (server.clients > 18 || server.maxClients > 18 || server.clients < 0 || server.maxClients < 0)
else
{
return;
++j;
}
}
// Check if already inserted and remove
auto* list = GetList();
if (!list) return;
std::size_t k = 0;
for (auto j = list->begin(); j != list->end(); ++k)
// Also remove from visible list
for (auto j = VisibleList.begin(); j != VisibleList.end();)
{
if (*j == k)
{
if (j->addr == address)
{
j = list->erase(j);
}
else
{
++j;
}
j = VisibleList.erase(j);
}
// Also remove from visible list
for (auto j = VisibleList.begin(); j != VisibleList.end();)
else
{
if (*j == k)
{
j = VisibleList.erase(j);
}
else
{
++j;
}
++j;
}
}
if (info.get("gamename") == "IW4"s && server.matchType
if (info.get("gamename") == "IW4"s && server.matchType
#if !defined(DEBUG) && defined(VERSION_FILTER)
&& CompareVersion(server.shortversion, SHORTVERSION)
&& CompareVersion(server.shortversion, SHORTVERSION)
#endif
)
)
{
auto* lList = GetList();
if (lList)
{
auto* lList = GetList();
if (lList)
if (!IsServerDuplicate(lList, server))
{
lList->push_back(server);
RefreshVisibleListInternal(UIScript::Token(), nullptr);
}
}
}
else
{
++i;
}
}
}
@ -626,7 +623,7 @@ namespace Components
}
catch (const std::exception& ex)
{
Logger::Warning(Game::CON_CHANNEL_CONSOLEONLY, "{} while performing numeric comparison between {} and {}\n", ex.what(), subVersions1[i], subVersions2[i]);
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{} while performing numeric comparison between {} and {}\n", ex.what(), subVersions1[i], subVersions2[i]);
return false;
}
}
@ -634,6 +631,19 @@ namespace Components
return true;
}
bool ServerList::IsServerDuplicate(const std::vector<ServerInfo>* list, const ServerInfo& server)
{
for (auto l = list->begin(); l != list->end(); ++l)
{
if (l->hash == server.hash)
{
return true;
}
}
return false;
}
ServerList::ServerInfo* ServerList::GetCurrentServer()
{
return GetServer(CurrentServer);
@ -730,6 +740,7 @@ namespace Components
}
}
const auto challenge = Utils::Cryptography::Rand::GenerateChallenge();
auto requestLimit = NETServerQueryLimit.get<int>();
for (std::size_t i = 0; i < RefreshContainer.servers.size() && requestLimit > 0; ++i)
{
@ -741,14 +752,11 @@ namespace Components
requestLimit--;
server->sendTime = Game::Sys_Milliseconds();
server->challenge = Utils::Cryptography::Rand::GenerateChallenge();
server->challenge = challenge;
++RefreshContainer.sentCount;
Network::SendCommand(server->target, "getinfo", server->challenge);
// Display in the menu, like in CoD4 - Disabled to avoid spamming?
//Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Sent requests: %d/%d", ServerList::RefreshContainer.sentCount, ServerList::RefreshContainer.sendCount));
}
UpdateVisibleInfo();
@ -784,17 +792,17 @@ namespace Components
void ServerList::UpdateVisibleInfo()
{
static int servers = 0;
static int players = 0;
static int bots = 0;
static auto servers = 0;
static auto players = 0;
static auto bots = 0;
auto list = GetList();
auto* list = GetList();
if (list)
{
int newSevers = list->size();
int newPlayers = 0;
int newBots = 0;
auto newSevers = static_cast<int>(list->size());
auto newPlayers = 0;
auto newBots = 0;
for (std::size_t i = 0; i < list->size(); ++i)
{
@ -808,7 +816,7 @@ namespace Components
players = newPlayers;
bots = newBots;
Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Servers: %i\nPlayers: %i (%i)", servers, players, bots));
Localization::Set("MPUI_SERVERQUERIED", std::format("Servers: {}\nPlayers: {} ({})", servers, players, bots));
}
}
}
@ -960,18 +968,6 @@ namespace Components
}
});
#ifdef _DEBUG
Command::Add("playerCount", [](Command::Params*)
{
auto count = 0;
for (const auto& server : OnlineList)
{
count += server.clients;
}
Logger::Debug("There are {} players playing", count);
});
#endif
// Add required ownerDraws
UIScript::AddOwnerDraw(220, UpdateSource);
UIScript::AddOwnerDraw(253, UpdateGameType);
@ -980,7 +976,7 @@ namespace Components
Scheduler::Loop(Frame, Scheduler::Pipeline::CLIENT);
}
ServerList::~ServerList()
void ServerList::preDestroy()
{
std::lock_guard _(RefreshContainer.mutex);
RefreshContainer.awatingList = false;

View File

@ -10,15 +10,15 @@ namespace Components
public:
typedef int(SortCallback)(const void*, const void*);
class ServerInfo
struct ServerInfo
{
public:
Network::Address addr;
std::string hostname;
std::string mapname;
std::string gametype;
std::string mod;
std::string shortversion;
std::size_t hash;
int clients;
int bots;
int maxClients;
@ -33,7 +33,8 @@ namespace Components
};
ServerList();
~ServerList();
void preDestroy() override;
static void Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
static void RefreshVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info);
@ -138,6 +139,7 @@ namespace Components
static ServerInfo* GetServer(unsigned int index);
static bool CompareVersion(const std::string& version1, const std::string& version2);
static bool IsServerDuplicate(const std::vector<ServerInfo>* list, const ServerInfo& server);
static int SortKey;
static bool SortAsc;
@ -159,3 +161,20 @@ namespace Components
static bool IsServerListOpen();
};
}
template <>
struct std::hash<Components::ServerList::ServerInfo>
{
std::size_t operator()(const Components::ServerList::ServerInfo& x) const noexcept
{
std::size_t hash = 0;
hash ^= std::hash<std::string>()(x.hostname);
hash ^= std::hash<std::string>()(x.mapname);
hash ^= std::hash<std::string>()(x.mod);
hash ^= std::hash<std::uint32_t>()(*reinterpret_cast<const std::uint32_t*>(&x.addr.getIP().bytes[0]));;
hash ^= x.clients;
return hash;
}
};

View File

@ -56,7 +56,7 @@ BOOL APIENTRY DllMain(HINSTANCE /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpvReser
#ifndef DEBUG_BINARY_CHECK
const auto* binary = reinterpret_cast<const char*>(0x6F9358);
if (binary == nullptr || std::memcmp(binary, BASEGAME_NAME, 14) != 0)
if (!binary || std::memcmp(binary, BASEGAME_NAME, 14) != 0)
#endif
{
MessageBoxA(nullptr,

View File

@ -29,6 +29,7 @@
#include <cctype>
#include <chrono>
#include <cmath>
#include <cstring>
#include <filesystem>
#include <format>
#include <fstream>

View File

@ -22,11 +22,11 @@ namespace Steam
if (!idBits)
{
if (Components::Dedicated::IsEnabled() || Components::ZoneBuilder::IsEnabled()) // Dedi guid
if (Components::ZoneBuilder::IsEnabled())
{
idBits = *reinterpret_cast<unsigned __int64*>(const_cast<char*>("DEDICATE"));
}
else if (Components::Singleton::IsFirstInstance()) // ECDSA guid
else if (Components::Singleton::IsFirstInstance() && !Components::Dedicated::IsEnabled()) // ECDSA guid
{
idBits = Components::Auth::GetKeyHash();
}

View File

@ -18,12 +18,14 @@ namespace Utils
std::string Rand::GenerateChallenge()
{
std::string challenge;
challenge.append(String::VA("%X", GenerateInt()));
challenge.append(String::VA("%X", ~timeGetTime() ^ GenerateInt()));
challenge.append(String::VA("%X", GenerateInt()));
char buffer[512]{};
int buffer_pos = 0;
return challenge;
buffer_pos += sprintf_s(&buffer[buffer_pos], sizeof(buffer) - buffer_pos, "%X", GenerateInt());
buffer_pos += sprintf_s(&buffer[buffer_pos], sizeof(buffer) - buffer_pos, "%X", ~timeGetTime() ^ GenerateInt());
buffer_pos += sprintf_s(&buffer[buffer_pos], sizeof(buffer) - buffer_pos, "%X", GenerateInt());
return std::string(buffer, static_cast<std::size_t>(buffer_pos));
}
std::uint32_t Rand::GenerateInt()