From 323ab2bf41f0822bd72b41a4393fbbb995f0cf0e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Fri, 8 Jan 2016 02:20:55 +0100 Subject: [PATCH] Serverinfo stuff. --- src/Components/Loader.cpp | 1 + src/Components/Loader.hpp | 1 + src/Components/Modules/ServerInfo.cpp | 251 ++++++++++++++++++++++++++ src/Components/Modules/ServerInfo.hpp | 33 ++++ src/Components/Modules/ServerList.cpp | 5 + src/Components/Modules/ServerList.hpp | 2 + src/Game/Functions.cpp | 4 + src/Game/Functions.hpp | 9 + 8 files changed, 306 insertions(+) create mode 100644 src/Components/Modules/ServerInfo.cpp create mode 100644 src/Components/Modules/ServerInfo.hpp diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index bc72e1b2..347efb70 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -32,6 +32,7 @@ namespace Components Loader::Register(new Materials()); Loader::Register(new FileSystem()); Loader::Register(new QuickPatch()); + Loader::Register(new ServerInfo()); Loader::Register(new ServerList()); Loader::Register(new ZoneBuilder()); Loader::Register(new AssetHandler()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 5522fa6f..7629f388 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -45,6 +45,7 @@ namespace Components #include "Modules\Singleton.hpp" #include "Modules\FileSystem.hpp" #include "Modules\QuickPatch.hpp" +#include "Modules\ServerInfo.hpp" #include "Modules\ServerList.hpp" #include "Modules\ZoneBuilder.hpp" #include "Modules\AssetHandler.hpp" diff --git a/src/Components/Modules/ServerInfo.cpp b/src/Components/Modules/ServerInfo.cpp new file mode 100644 index 00000000..e68b83f4 --- /dev/null +++ b/src/Components/Modules/ServerInfo.cpp @@ -0,0 +1,251 @@ +#include "STDInclude.hpp" +#include "..\..\Utils\Versioning.hpp" + +namespace Components +{ + ServerInfo::Container ServerInfo::PlayerContainer; + + int ServerInfo::GetPlayerCount() + { + return ServerInfo::PlayerContainer.PlayerList.size(); + } + + const char* ServerInfo::GetPlayerText(int index, int column) + { + if ((unsigned int)index < ServerInfo::PlayerContainer.PlayerList.size()) + { + switch (column) + { + case 0: + return Utils::VA("%d", index); + + case 1: + return ServerInfo::PlayerContainer.PlayerList[index].Name.data(); + + case 2: + return Utils::VA("%d", ServerInfo::PlayerContainer.PlayerList[index].Score); + + case 3: + return Utils::VA("%d", ServerInfo::PlayerContainer.PlayerList[index].Ping); + } + } + + return ""; + } + + void ServerInfo::SelectPlayer(int index) + { + ServerInfo::PlayerContainer.CurrentPlayer = index; + } + + void ServerInfo::ServerStatus() + { + ServerInfo::PlayerContainer.CurrentPlayer = 0; + ServerInfo::PlayerContainer.PlayerList.clear(); + + ServerList::ServerInfo* info = ServerList::GetCurrentServer(); + + if(info) + { + Dvar::Var("uiSi_ServerName").Set(info->Hostname); + Dvar::Var("uiSi_MaxClients").Set(info->Clients); + Dvar::Var("uiSi_Version").Set(info->Shortversion); + Dvar::Var("uiSi_isPrivate").Set(info->Password ? "@MENU_YES" : "@MENU_NO"); + Dvar::Var("uiSi_Hardcore").Set(info->Hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED"); + Dvar::Var("uiSi_KillCam").Set("@MENU_NO"); + Dvar::Var("uiSi_ffType").Set("@MENU_DISABLED"); + Dvar::Var("uiSi_MapName").Set(info->Mapname); + Dvar::Var("uiSi_MapNameLoc").Set(Game::UI_LocalizeMapName(info->Mapname.data())); + Dvar::Var("uiSi_GameType").Set(Game::UI_LocalizeGameType(info->Gametype.data())); + Dvar::Var("uiSi_ModName").Set(""); + + if (info->Mod.size() > 5) + { + Dvar::Var("uiSi_ModName").Set(info->Mod.data() + 5); + } + + ServerInfo::PlayerContainer.Target = info->Addr; + Network::Send(ServerInfo::PlayerContainer.Target, "getstatus\n"); + } + } + + ServerInfo::ServerInfo() + { + ServerInfo::PlayerContainer.CurrentPlayer = 0; + ServerInfo::PlayerContainer.PlayerList.clear(); + + // Ignore native getStatus implementation + Utils::Hook::Nop(0x62654E, 6); + + // Add uiscript + UIScript::Add("ServerStatus", ServerInfo::ServerStatus); + + // Add uifeeder + UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer); + + Network::Handle("getStatus", [] (Network::Address address, std::string data) + { + bool isInLobby = false; + int maxclientCount = *Game::svs_numclients; + + if(!maxclientCount) + { + isInLobby = true; + + //maxclientCount = Dvar::Var("sv_maxclients").Get(); + maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); + } + + Utils::InfoString info(Game::Dvar_InfoString_Big(1024)); + info.Set("challenge", Utils::ParseChallenge(data)); + info.Set("gamename", "IW4"); + info.Set("sv_maxclients", Utils::VA("%i", maxclientCount)); + info.Set("protocol", Utils::VA("%i", PROTOCOL)); + info.Set("shortversion", VERSION_STR); + info.Set("checksum", Utils::VA("%d", Game::Com_Milliseconds())); + info.Set("mapname", Dvar::Var("mapname").Get()); + info.Set("isPrivate", (Dvar::Var("g_password").Get().size() ? "1" : "0")); + + // Ensure mapname is set + if (!info.Get("mapname").size()) + { + info.Set("mapname", Dvar::Var("ui_mapname").Get()); + } + + // Set matchtype + // 0 - No match, connecting not possible + // 1 - Party, use Steam_JoinLobby to connect + // 2 - Match, use CL_ConnectFromParty to connect + + if (Dvar::Var("party_enable").Get() && Dvar::Var("party_host").Get()) // Party hosting + { + info.Set("matchtype", "1"); + } + else if (Dvar::Var("sv_running").Get()) // Match hosting + { + info.Set("matchtype", "2"); + } + else + { + info.Set("matchtype", "0"); + } + + std::string playerList; + + for (int i = 0; i < maxclientCount; i++) // Maybe choose 18 here? + { + int score = 0; + int ping = 0; + std::string name; + + if (!isInLobby) + { + if (Game::svs_clients[i].state < 3) continue; + + score = Game::SV_GameClientNum_Score(i); + ping = Game::svs_clients[i].ping; + name = Game::svs_clients[i].name; + } + else + { + // Score and ping are irrelevant + const char* namePtr = Game::PartyHost_GetMemberName((Game::PartyData_s*)0x1081C00, i); + if (!namePtr || !namePtr[0]) continue; + + name = namePtr; + } + + playerList.append(Utils::VA("%i %i \"%s\"\n", score, ping, name.data())); + } + + Network::Send(address, Utils::VA("statusResponse\n\\%s\n%s\n", info.Build().data(), playerList.data())); + }); + + Network::Handle("statusResponse", [] (Network::Address address, std::string data) + { + if (ServerInfo::PlayerContainer.Target == address) + { + Utils::InfoString info(data.substr(0, data.find_first_of("\n"))); + + Dvar::Var("uiSi_ServerName").Set(info.Get("sv_hostname")); + Dvar::Var("uiSi_MaxClients").Set(info.Get("sv_maxclients")); + Dvar::Var("uiSi_Version").Set(info.Get("shortversion")); + Dvar::Var("uiSi_isPrivate").Set(info.Get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES"); + Dvar::Var("uiSi_Hardcore").Set(info.Get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED"); + Dvar::Var("uiSi_KillCam").Set(info.Get("scr_game_allowkillcam") == "0" ? "@MENU_NO" : "@MENU_YES"); + Dvar::Var("uiSi_MapName").Set(info.Get("mapname")); + Dvar::Var("uiSi_MapNameLoc").Set(Game::UI_LocalizeMapName(info.Get("mapname").data())); + Dvar::Var("uiSi_GameType").Set(Game::UI_LocalizeGameType(info.Get("g_gametype").data())); + Dvar::Var("uiSi_ModName").Set(""); + + switch (atoi(info.Get("scr_team_fftype").data())) + { + default: + Dvar::Var("uiSi_ffType").Set("@MENU_DISABLED"); + break; + + case 1: + Dvar::Var("uiSi_ffType").Set("@MENU_ENABLED"); + break; + + case 2: + Dvar::Var("uiSi_ffType").Set("@MPUI_RULES_REFLECT"); + break; + + case 3: + Dvar::Var("uiSi_ffType").Set("@MPUI_RULES_SHARED"); + break; + } + + if (info.Get("fs_game").size() > 5) + { + Dvar::Var("uiSi_ModName").Set(info.Get("fs_game").data() + 5); + } + + auto lines = Utils::Explode(data, '\n'); + + if (lines.size() <= 1) return; + + for (unsigned int i = 1; i < lines.size(); i++) + { + ServerInfo::Container::Player player; + + std::string currentData = lines[i]; + + if (currentData.size() < 3) continue; + + // Insert score + player.Score = atoi(currentData.substr(0, currentData.find_first_of(" ")).data()); + + // Remove score + currentData = currentData.substr(currentData.find_first_of(" ") + 1); + + // Insert ping + player.Ping = atoi(currentData.substr(0, currentData.find_first_of(" ")).data()); + + // Remove ping + currentData = currentData.substr(currentData.find_first_of(" ") + 1); + + if (currentData[0] == '\"') + { + currentData = currentData.substr(1); + } + + if (currentData[currentData.size() - 1] == '\"') + { + currentData.pop_back(); + } + + player.Name = currentData; + + ServerInfo::PlayerContainer.PlayerList.push_back(player); + } + } + }); + } + + ServerInfo::~ServerInfo() + { + ServerInfo::PlayerContainer.PlayerList.clear(); + } +} diff --git a/src/Components/Modules/ServerInfo.hpp b/src/Components/Modules/ServerInfo.hpp new file mode 100644 index 00000000..8aa13828 --- /dev/null +++ b/src/Components/Modules/ServerInfo.hpp @@ -0,0 +1,33 @@ +namespace Components +{ + class ServerInfo : public Component + { + public: + ServerInfo(); + ~ServerInfo(); + const char* GetName() { return "ServerInfo"; }; + + private: + struct Container + { + struct Player + { + int Ping; + int Score; + std::string Name; + }; + + int CurrentPlayer; + std::vector PlayerList; + Network::Address Target; + }; + + static Container PlayerContainer; + + static void ServerStatus(); + + static int GetPlayerCount(); + static const char* GetPlayerText(int index, int column); + static void SelectPlayer(int index); + }; +} diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index e8cb8517..a359cabb 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -303,6 +303,11 @@ namespace Components ServerList::RefreshContainer.Mutex.unlock(); } + ServerList::ServerInfo* ServerList::GetCurrentServer() + { + return ServerList::GetServer(ServerList::CurrentServer); + } + void ServerList::SortList() { qsort(ServerList::VisibleList.data(), ServerList::VisibleList.size(), sizeof(int), [] (const void* first, const void* second) diff --git a/src/Components/Modules/ServerList.hpp b/src/Components/Modules/ServerList.hpp index 81ce5259..cb079dc2 100644 --- a/src/Components/Modules/ServerList.hpp +++ b/src/Components/Modules/ServerList.hpp @@ -30,6 +30,8 @@ namespace Components static void InsertRequest(Network::Address address, bool acquireMutex = true); static void Insert(Network::Address address, Utils::InfoString info); + static ServerInfo* GetCurrentServer(); + static bool IsFavouriteList(); static bool IsOfflineList(); static bool IsOnlineList(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index c927d725..1c67a121 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -34,6 +34,7 @@ namespace Game Dvar_RegisterColor_t Dvar_RegisterColor = (Dvar_RegisterColor_t)0x471500; Dvar_FindVar_t Dvar_FindVar = (Dvar_FindVar_t)0x4D5390; + Dvar_InfoString_Big_t Dvar_InfoString_Big = (Dvar_InfoString_Big_t)0x4D98A0; Dvar_SetCommand_t Dvar_SetCommand = (Dvar_SetCommand_t)0x4EE430; Field_Clear_t Field_Clear = (Field_Clear_t)0x437EB0; @@ -87,6 +88,7 @@ namespace Game Party_GetMaxPlayers_t Party_GetMaxPlayers = (Party_GetMaxPlayers_t)0x4F5D60; PartyHost_CountMembers_t PartyHost_CountMembers = (PartyHost_CountMembers_t)0x497330; PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot = (PartyHost_GetMemberAddressBySlot_t)0x44E100; + PartyHost_GetMemberName_t PartyHost_GetMemberName = (PartyHost_GetMemberName_t)0x44BE90; Script_Alloc_t Script_Alloc = (Script_Alloc_t)0x422E70; Script_SetupTokens_t Script_SetupTokens = (Script_SetupTokens_t)0x4E6950; @@ -98,6 +100,8 @@ namespace Game Steam_JoinLobby_t Steam_JoinLobby = (Steam_JoinLobby_t)0x49CF70; + SV_GameClientNum_Score_t SV_GameClientNum_Score = (SV_GameClientNum_Score_t)0x469AC0; + Sys_IsMainThread_t Sys_IsMainThread = (Sys_IsMainThread_t)0x4C37D0; UI_AddMenuList_t UI_AddMenuList = (UI_AddMenuList_t)0x4533C0; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 6e156280..2d73e303 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -78,6 +78,9 @@ namespace Game typedef dvar_t* (__cdecl * Dvar_FindVar_t)(const char *dvarName); extern Dvar_FindVar_t Dvar_FindVar; + typedef char* (__cdecl* Dvar_InfoString_Big_t)(int typeMask); + extern Dvar_InfoString_Big_t Dvar_InfoString_Big; + typedef dvar_t* (__cdecl * Dvar_SetCommand_t)(const char* name, const char* value); extern Dvar_SetCommand_t Dvar_SetCommand; @@ -199,6 +202,9 @@ namespace Game typedef netadr_t *(__cdecl * PartyHost_GetMemberAddressBySlot_t)(int unk, void *party, const int slot); extern PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot; + typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData_s* party, const int clientNum); + extern PartyHost_GetMemberName_t PartyHost_GetMemberName; + typedef script_t* (__cdecl * Script_Alloc_t)(int length); extern Script_Alloc_t Script_Alloc; @@ -217,6 +223,9 @@ namespace Game typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char); extern Steam_JoinLobby_t Steam_JoinLobby; + typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID); + extern SV_GameClientNum_Score_t SV_GameClientNum_Score; + typedef bool(__cdecl * Sys_IsMainThread_t)(); extern Sys_IsMainThread_t Sys_IsMainThread;