Basic serverlist.
This commit is contained in:
parent
2473ce8119
commit
601d4d063e
@ -276,8 +276,6 @@ namespace Components
|
||||
|
||||
// some thing overwriting feeder 2's data
|
||||
Utils::Hook::Set<BYTE>(0x4A06A9, 0xEB);
|
||||
|
||||
Feeder::Add(2.0f, [] () { return 10; }, [] (int index, int column) { return Utils::VA("%d : %d", index, column); }, [] (int index){});
|
||||
}
|
||||
|
||||
Feeder::~Feeder()
|
||||
|
@ -26,6 +26,7 @@ namespace Components
|
||||
Loader::Register(new Singleton());
|
||||
Loader::Register(new FileSystem());
|
||||
Loader::Register(new QuickPatch());
|
||||
Loader::Register(new ServerList());
|
||||
Loader::Register(new AssetHandler());
|
||||
Loader::Register(new Localization());
|
||||
Loader::Register(new MusicalTalent());
|
||||
|
@ -39,6 +39,7 @@ namespace Components
|
||||
#include "Singleton.hpp"
|
||||
#include "FileSystem.hpp"
|
||||
#include "QuickPatch.hpp"
|
||||
#include "ServerList.hpp"
|
||||
#include "AssetHandler.hpp"
|
||||
#include "Localization.hpp"
|
||||
#include "MusicalTalent.hpp"
|
||||
|
@ -21,6 +21,14 @@ namespace Components
|
||||
{
|
||||
return this->address.port;
|
||||
}
|
||||
void Network::Address::SetIP(DWORD ip)
|
||||
{
|
||||
*(DWORD*)this->address.ip = ip;
|
||||
}
|
||||
DWORD Network::Address::GetIP()
|
||||
{
|
||||
return *(DWORD*)this->address.ip;
|
||||
}
|
||||
Game::netadr_t* Network::Address::Get()
|
||||
{
|
||||
return &this->address;
|
||||
@ -40,6 +48,11 @@ namespace Components
|
||||
Game::OOBPrintT(type, *target.Get(), data.data());
|
||||
}
|
||||
|
||||
void Network::Send(Address target, std::string data)
|
||||
{
|
||||
Network::Send(Game::netsrc_t::NS_CLIENT, target, data);
|
||||
}
|
||||
|
||||
int Network::PacketInterceptionHandler(const char* packet)
|
||||
{
|
||||
// Check if custom handler exists
|
||||
|
@ -16,6 +16,10 @@ namespace Components
|
||||
|
||||
void SetPort(unsigned short port);
|
||||
unsigned short GetPort();
|
||||
|
||||
void SetIP(DWORD ip);
|
||||
DWORD GetIP();
|
||||
|
||||
Game::netadr_t* Get();
|
||||
const char* GetString();
|
||||
|
||||
@ -31,6 +35,7 @@ namespace Components
|
||||
const char* GetName() { return "Network"; };
|
||||
|
||||
static void Handle(std::string packet, Callback callback);
|
||||
static void Send(Address target, std::string data);
|
||||
static void Send(Game::netsrc_t type, Address target, std::string data);
|
||||
|
||||
private:
|
||||
|
@ -24,7 +24,7 @@ namespace Components
|
||||
Party::Container.Target = target;
|
||||
Party::Container.Challenge = Utils::VA("%X", Party::Container.JoinTime);
|
||||
|
||||
Network::Send(Game::NS_CLIENT, Party::Container.Target, Utils::VA("getinfo %s\n", Party::Container.Challenge.data()));
|
||||
Network::Send(Party::Container.Target, Utils::VA("getinfo %s\n", Party::Container.Challenge.data()));
|
||||
|
||||
Command::Execute("openmenu popup_reconnectingtoparty");
|
||||
}
|
||||
@ -177,11 +177,14 @@ namespace Components
|
||||
info.Set("matchtype", "0");
|
||||
}
|
||||
|
||||
Network::Send(Game::NS_CLIENT, address, Utils::VA("infoResponse\n%s\n", info.Build().data()));
|
||||
Network::Send(address, Utils::VA("infoResponse\n%s\n", info.Build().data()));
|
||||
});
|
||||
|
||||
Network::Handle("infoResponse", [] (Network::Address address, std::string data)
|
||||
{
|
||||
OutputDebugStringA(data.data());
|
||||
Utils::InfoString info(data);
|
||||
|
||||
// Handle connection
|
||||
if (Party::Container.Valid)
|
||||
{
|
||||
@ -190,8 +193,6 @@ namespace Components
|
||||
// Invalidate handler for future packets
|
||||
Party::Container.Valid = false;
|
||||
|
||||
Utils::InfoString info(data);
|
||||
|
||||
int matchType = atoi(info.Get("matchtype").data());
|
||||
|
||||
if (info.Get("challenge") != Party::Container.Challenge)
|
||||
@ -246,6 +247,8 @@ namespace Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerList::Insert(address, info);
|
||||
});
|
||||
}
|
||||
|
||||
|
195
iw4/Components/ServerList.cpp
Normal file
195
iw4/Components/ServerList.cpp
Normal file
@ -0,0 +1,195 @@
|
||||
#include "..\STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
int ServerList::CurrentServer = 0;
|
||||
ServerList::Container ServerList::RefreshContainer;
|
||||
std::vector<ServerList::ServerInfo> ServerList::OnlineList;
|
||||
|
||||
int ServerList::GetServerCount()
|
||||
{
|
||||
return ServerList::OnlineList.size();
|
||||
}
|
||||
|
||||
const char* ServerList::GetServerText(int index, int column)
|
||||
{
|
||||
if (index >= (int)ServerList::OnlineList.size()) return "";
|
||||
|
||||
ServerList::ServerInfo* Server = &ServerList::OnlineList[index];
|
||||
|
||||
switch (column)
|
||||
{
|
||||
case Column::Password:
|
||||
{
|
||||
return (Server->Password ? "X" : "");
|
||||
}
|
||||
|
||||
case Column::Hostname:
|
||||
{
|
||||
return Server->Hostname.data();
|
||||
}
|
||||
|
||||
case Column::Mapname:
|
||||
{
|
||||
return Game::UI_LocalizeMapName(Server->Mapname.data());
|
||||
}
|
||||
|
||||
case Column::Players:
|
||||
{
|
||||
return Utils::VA("%i (%i)", Server->Clients, Server->MaxClients);
|
||||
}
|
||||
|
||||
case Column::Gametype:
|
||||
{
|
||||
if (Server->Mod != "")
|
||||
{
|
||||
return (Server->Mod.data() + 5);
|
||||
}
|
||||
|
||||
return Game::UI_LocalizeGameType(Server->Gametype.data());
|
||||
}
|
||||
|
||||
case Column::Ping:
|
||||
{
|
||||
return Utils::VA("%i", Server->Ping);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void ServerList::SelectServer(int index)
|
||||
{
|
||||
ServerList::CurrentServer = index;
|
||||
}
|
||||
|
||||
void ServerList::Refresh()
|
||||
{
|
||||
ServerList::OnlineList.clear();
|
||||
|
||||
ServerList::RefreshContainer.Mutex.lock();
|
||||
ServerList::RefreshContainer.Servers.clear();
|
||||
ServerList::RefreshContainer.Mutex.unlock();
|
||||
|
||||
int masterPort = Dvar::Var("masterPort").Get<int>();
|
||||
const char* masterServerName = Dvar::Var("masterServerName").Get<const char*>();
|
||||
|
||||
ServerList::RefreshContainer.Host = Network::Address(Utils::VA("%s:%u", masterServerName, masterPort));
|
||||
|
||||
ServerList::RefreshContainer.AwaitingList = true;
|
||||
|
||||
Network::Send(ServerList::RefreshContainer.Host, "getservers IW4 145 full empty");
|
||||
}
|
||||
|
||||
void ServerList::Insert(Network::Address address, Utils::InfoString info)
|
||||
{
|
||||
// Do not enter any new servers, if we are awaiting a new list from the master
|
||||
if (ServerList::RefreshContainer.AwaitingList) return;
|
||||
|
||||
ServerList::RefreshContainer.Mutex.lock();
|
||||
|
||||
for (auto i = ServerList::RefreshContainer.Servers.begin(); i != ServerList::RefreshContainer.Servers.end(); i++)
|
||||
{
|
||||
// Our desired server
|
||||
if (i->Target == address)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// TODO: Implement deeper check like version and game
|
||||
ServerInfo server;
|
||||
server.Hostname = info.Get("hostname");
|
||||
server.Mapname = info.Get("mapname");
|
||||
server.Gametype = info.Get("gametype");
|
||||
server.Mod = info.Get("fs_game");
|
||||
server.MatchType = atoi(info.Get("matchtype").data());
|
||||
server.Clients = atoi(info.Get("clients").data());
|
||||
server.MaxClients = atoi(info.Get("sv_maxclients").data());
|
||||
server.Password = 0; // No info yet
|
||||
server.Ping = (Game::Com_Milliseconds() - i->SendTime);
|
||||
|
||||
ServerList::OnlineList.push_back(server);
|
||||
|
||||
ServerList::RefreshContainer.Servers.erase(i);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ServerList::RefreshContainer.Mutex.unlock();
|
||||
}
|
||||
|
||||
ServerList::ServerList()
|
||||
{
|
||||
ServerList::OnlineList.clear();
|
||||
|
||||
// Add server list feeder
|
||||
Feeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer);
|
||||
|
||||
Network::Handle("getServersResponse", [] (Network::Address address, std::string data)
|
||||
{
|
||||
if (!ServerList::RefreshContainer.AwaitingList) return; // Only parse if we are awaiting a list
|
||||
if (ServerList::RefreshContainer.Host != address) return; // Only parse from host we sent to
|
||||
|
||||
ServerList::RefreshContainer.AwaitingList = false;
|
||||
|
||||
ServerList::RefreshContainer.Mutex.lock();
|
||||
|
||||
// Is this safe? If server ip/port is encoded as '\', it might fail
|
||||
// TODO: Rather parse six bytes and skip the seventh
|
||||
auto servers = Utils::Explode(data, '\\');
|
||||
|
||||
if (servers.size())
|
||||
{
|
||||
if (servers[servers.size() - 1] == "EOT")
|
||||
{
|
||||
Logger::Print("We got an invalid response from the master. Trying to parse it anyways...\n");
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < servers.size() - 1; i++)
|
||||
{
|
||||
const char* server = servers[i].data();
|
||||
|
||||
DWORD ip = *(DWORD*)server;
|
||||
uint16_t port = *(uint16_t*)(server + 4);
|
||||
|
||||
Network::Address serverAddr = address;
|
||||
serverAddr.SetIP(ip);
|
||||
serverAddr.SetPort(port);
|
||||
serverAddr.Get()->type = Game::NA_IP;
|
||||
|
||||
ServerList::Container::ServerContainer container;
|
||||
container.SendTime = Game::Com_Milliseconds();
|
||||
container.Challenge = Utils::VA("%d", container.SendTime);
|
||||
container.Sent = true;
|
||||
container.Target = serverAddr;
|
||||
|
||||
ServerList::RefreshContainer.Servers.push_back(container);
|
||||
|
||||
Network::Send(container.Target, Utils::VA("getinfo %s\n", container.Challenge.data()));
|
||||
}
|
||||
}
|
||||
|
||||
ServerList::RefreshContainer.Mutex.unlock();
|
||||
});
|
||||
|
||||
Command::Add("refreshList", [] (Command::Params params)
|
||||
{
|
||||
ServerList::Refresh();
|
||||
});
|
||||
|
||||
// Temporary overwriting
|
||||
strcpy((char*)0x6D9CBC, "localhost");
|
||||
}
|
||||
|
||||
ServerList::~ServerList()
|
||||
{
|
||||
ServerList::OnlineList.clear();
|
||||
}
|
||||
}
|
64
iw4/Components/ServerList.hpp
Normal file
64
iw4/Components/ServerList.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
namespace Components
|
||||
{
|
||||
class ServerList : public Component
|
||||
{
|
||||
public:
|
||||
struct ServerInfo
|
||||
{
|
||||
Network::Address Addr;
|
||||
bool Visible;
|
||||
std::string Hostname;
|
||||
std::string Mapname;
|
||||
std::string Gametype;
|
||||
std::string Mod;
|
||||
int Clients;
|
||||
int MaxClients;
|
||||
bool Password;
|
||||
int Ping;
|
||||
int MatchType;
|
||||
bool Hardcore;
|
||||
};
|
||||
|
||||
ServerList();
|
||||
~ServerList();
|
||||
const char* GetName() { return "ServerList"; };
|
||||
|
||||
static void Refresh();
|
||||
static void Insert(Network::Address address, Utils::InfoString info);
|
||||
|
||||
private:
|
||||
enum Column
|
||||
{
|
||||
Password,
|
||||
Hostname,
|
||||
Mapname,
|
||||
Players,
|
||||
Gametype,
|
||||
Ping,
|
||||
};
|
||||
|
||||
struct Container
|
||||
{
|
||||
struct ServerContainer
|
||||
{
|
||||
bool Sent;
|
||||
int SendTime;
|
||||
std::string Challenge;
|
||||
Network::Address Target;
|
||||
};
|
||||
|
||||
bool AwaitingList;
|
||||
Network::Address Host;
|
||||
std::vector<ServerContainer> Servers;
|
||||
std::mutex Mutex;
|
||||
};
|
||||
|
||||
static int GetServerCount();
|
||||
static const char* GetServerText(int index, int column);
|
||||
static void SelectServer(int index);
|
||||
|
||||
static int CurrentServer;
|
||||
static Container RefreshContainer;
|
||||
static std::vector<ServerInfo> OnlineList;
|
||||
};
|
||||
}
|
@ -66,6 +66,9 @@ namespace Game
|
||||
LoadInitialFF_t LoadInitialFF = (LoadInitialFF_t)0x506AC0;
|
||||
LoadModdableRawfile_t LoadModdableRawfile = (LoadModdableRawfile_t)0x61ABC0;
|
||||
|
||||
LocalizeString_t LocalizeString = (LocalizeString_t)0x4FB010;
|
||||
LocalizeMapString_t LocalizeMapString = (LocalizeMapString_t)0x44BB30;
|
||||
|
||||
sendOOB_t OOBPrint = (sendOOB_t)0x4AEF00;
|
||||
|
||||
PC_ReadToken_t PC_ReadToken = (PC_ReadToken_t)0x4ACCD0;
|
||||
@ -101,6 +104,12 @@ namespace Game
|
||||
|
||||
UiContext *uiContext = (UiContext *)0x62E2858;
|
||||
|
||||
int* arenaCount = (int*)0x62E6930;
|
||||
mapArena_t* arenas = (mapArena_t*)0x62E6934;
|
||||
|
||||
int* gameTypeCount = (int*)0x62E50A0;
|
||||
gameTypeName_t* gameTypes = (gameTypeName_t*)0x62E50A4;
|
||||
|
||||
void* ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
||||
{
|
||||
int elSize = DB_GetXAssetSizeHandlers[type]();
|
||||
@ -126,4 +135,43 @@ namespace Game
|
||||
|
||||
OOBPrint(type, *adr, *(adr + 1), *(adr + 2), 0xFFFFFFFF, *(adr + 4), message);
|
||||
}
|
||||
|
||||
const char* UI_LocalizeMapName(const char* mapName)
|
||||
{
|
||||
for (int i = 0; i < *arenaCount; i++)
|
||||
{
|
||||
if (!_stricmp(arenas[i].mapName, mapName))
|
||||
{
|
||||
char* uiName = &arenas[i].uiName[0];
|
||||
if ((uiName[0] == 'M' && uiName[1] == 'P') || (uiName[0] == 'P' && uiName[1] == 'A')) // MPUI/PATCH
|
||||
{
|
||||
char* name = LocalizeMapString(uiName);
|
||||
return name;
|
||||
}
|
||||
|
||||
return uiName;
|
||||
}
|
||||
}
|
||||
|
||||
return mapName;
|
||||
}
|
||||
|
||||
const char* UI_LocalizeGameType(const char* gameType)
|
||||
{
|
||||
if (gameType == 0 || *gameType == '\0')
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
for (int i = 0; i < *gameTypeCount; i++)
|
||||
{
|
||||
if (!_stricmp(gameTypes[i].gameType, gameType))
|
||||
{
|
||||
char* name = LocalizeMapString(gameTypes[i].uiName);
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
return gameType;
|
||||
}
|
||||
}
|
@ -148,6 +148,12 @@ namespace Game
|
||||
typedef void* (__cdecl * LoadModdableRawfile_t)(int a1, const char* filename);
|
||||
extern LoadModdableRawfile_t LoadModdableRawfile;
|
||||
|
||||
typedef char* (__cdecl * LocalizeString_t)(char*, char*);
|
||||
extern LocalizeString_t LocalizeString;
|
||||
|
||||
typedef char* (__cdecl * LocalizeMapString_t)(char*);
|
||||
extern LocalizeMapString_t LocalizeMapString;
|
||||
|
||||
typedef void(__cdecl* sendOOB_t)(int, int, int, int, int, int, const char*);
|
||||
extern sendOOB_t OOBPrint;
|
||||
|
||||
@ -199,7 +205,15 @@ namespace Game
|
||||
|
||||
extern UiContext *uiContext;
|
||||
|
||||
extern int* arenaCount;
|
||||
extern mapArena_t* arenas;
|
||||
|
||||
extern int* gameTypeCount;
|
||||
extern gameTypeName_t* gameTypes;
|
||||
|
||||
void* ReallocateAssetPool(XAssetType type, unsigned int newSize);
|
||||
void Menu_FreeItemMemory(Game::itemDef_t* item);
|
||||
void OOBPrintT(int type, netadr_t netadr, const char* message);
|
||||
const char* UI_LocalizeMapName(const char* mapName);
|
||||
const char* UI_LocalizeGameType(const char* gameType);
|
||||
}
|
||||
|
@ -922,4 +922,17 @@ namespace Game
|
||||
XNADDR hostAddress;
|
||||
XNKEY keyExchangeKey;
|
||||
};
|
||||
|
||||
struct mapArena_t
|
||||
{
|
||||
char uiName[32];
|
||||
char mapName[16];
|
||||
char pad[2768];
|
||||
};
|
||||
|
||||
struct gameTypeName_t
|
||||
{
|
||||
char gameType[12];
|
||||
char uiName[32];
|
||||
};
|
||||
}
|
||||
|
@ -23,8 +23,6 @@ namespace Steam
|
||||
|
||||
void Callbacks::RegisterCallResult(uint64_t call, Callbacks::Base* result)
|
||||
{
|
||||
OutputDebugStringA(::Utils::VA("Registering result: %d", result->GetICallback()));
|
||||
|
||||
Callbacks::ResultHandlers[call] = result;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,15 @@ namespace Utils
|
||||
|
||||
for (std::string token; std::getline(iss, token, delim);)
|
||||
{
|
||||
result.push_back(std::move(token));
|
||||
std::string _entry = std::move(token);
|
||||
|
||||
// Remove trailing 0x0 bytes
|
||||
while (_entry.size() && !_entry[_entry.size() - 1])
|
||||
{
|
||||
_entry = _entry.substr(0, _entry.size() - 1);
|
||||
}
|
||||
|
||||
result.push_back(_entry);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -114,6 +122,11 @@ namespace Utils
|
||||
|
||||
void InfoString::Parse(std::string buffer)
|
||||
{
|
||||
if (buffer[0] == '\\')
|
||||
{
|
||||
buffer = buffer.substr(1);
|
||||
}
|
||||
|
||||
std::vector<std::string> KeyValues = Utils::Explode(buffer, '\\');
|
||||
|
||||
for (unsigned int i = 0; i < (KeyValues.size() - 1); i+=2)
|
||||
|
@ -12,6 +12,7 @@ namespace Utils
|
||||
public:
|
||||
InfoString() {};
|
||||
InfoString(std::string buffer) :InfoString() { this->Parse(buffer); };
|
||||
InfoString(const InfoString &obj) { this->KeyValuePairs = obj.KeyValuePairs; };
|
||||
|
||||
void Set(std::string key, std::string value);
|
||||
std::string Get(std::string key);
|
||||
|
@ -28,7 +28,7 @@
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Normal|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<TargetName>iw4m</TargetName>
|
||||
<TargetName>iw4x</TargetName>
|
||||
<OutDir>$(SolutionDir)$(Configuration)\Bin\</OutDir>
|
||||
<IntDir>$(SolutionDir)$(Configuration)\Obj\</IntDir>
|
||||
</PropertyGroup>
|
||||
@ -75,6 +75,7 @@
|
||||
<ClInclude Include="Components\QuickPatch.hpp" />
|
||||
<ClInclude Include="Components\RawFiles.hpp" />
|
||||
<ClInclude Include="Components\Renderer.hpp" />
|
||||
<ClInclude Include="Components\ServerList.hpp" />
|
||||
<ClInclude Include="Components\Singleton.hpp" />
|
||||
<ClInclude Include="Components\Window.hpp" />
|
||||
<ClInclude Include="Game\Functions.hpp" />
|
||||
@ -114,6 +115,7 @@
|
||||
<ClCompile Include="Components\QuickPatch.cpp" />
|
||||
<ClCompile Include="Components\RawFiles.cpp" />
|
||||
<ClCompile Include="Components\Renderer.cpp" />
|
||||
<ClCompile Include="Components\ServerList.cpp" />
|
||||
<ClCompile Include="Components\Singleton.cpp" />
|
||||
<ClCompile Include="Components\Window.cpp" />
|
||||
<ClCompile Include="Game\Functions.cpp" />
|
||||
|
@ -137,6 +137,9 @@
|
||||
<ClCompile Include="Components\Feeder.cpp">
|
||||
<Filter>Source\Components\Modules</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Components\ServerList.cpp">
|
||||
<Filter>Source\Components\Modules</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Steam\Interfaces\SteamUser.hpp">
|
||||
@ -250,5 +253,8 @@
|
||||
<ClInclude Include="Components\Feeder.hpp">
|
||||
<Filter>Source\Components\Modules</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Components\ServerList.hpp">
|
||||
<Filter>Source\Components\Modules</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user