Skip to content

Server Browser Revamp #3933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Client/core/CClientVariables.cpp
Original file line number Diff line number Diff line change
@@ -277,7 +277,6 @@ void CClientVariables::LoadDefaults()
DEFAULT("debugfile", _S("")); // debug filename
DEFAULT("console_pos", CVector2D(0, 0)); // console position
DEFAULT("console_size", CVector2D(200, 200)); // console size
DEFAULT("serverbrowser_size", CVector2D(720.0f, 495.0f)); // serverbrowser size
DEFAULT("fps_limit", 100); // frame limiter
DEFAULT("chat_font", 2); // chatbox font type
DEFAULT("chat_lines", 10); // chatbox lines
29 changes: 29 additions & 0 deletions Client/core/CQueryReceiver.cpp
Original file line number Diff line number Diff line change
@@ -192,6 +192,35 @@ SQueryInfo CQueryReceiver::GetServerResponse()
if (!strHttpPort.empty())
info.httpPort = atoi(strHttpPort);

// Check if this reply includes rules
if (strncmp(szBuffer + i, "RULES", 5) == 0)
{
g_pCore->GetConsole()->Printf("Parsing rules for server: %s", info.serverName.c_str());

i += 5;
while (i < len)
{
// Check if it's the end of rules
if ((unsigned char)szBuffer[i] == 1)
{
i++;
break;
}

SString key, value;
if (!ReadString(key, szBuffer, i, len))
return info;
if (!ReadString(value, szBuffer, i, len))
return info;

info.rules[key] = value;

g_pCore->GetConsole()->Printf(" Rule: %s = %s", key.c_str(), value.c_str());
}

g_pCore->GetConsole()->Printf("Finished parsing rules");
}

// Get player nicks
while (i < len)
{
1 change: 1 addition & 0 deletions Client/core/CQueryReceiver.h
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ struct SQueryInfo
ushort pingTime;

std::vector<SString> playersPool;
std::unordered_map<SString, SString> rules;
};

class CQueryReceiver
2 changes: 2 additions & 0 deletions Client/core/ServerBrowser/CServerList.cpp
Original file line number Diff line number Diff line change
@@ -511,6 +511,8 @@ bool CServerListItem::ParseQuery()
m_iBuildNumber = info.buildNum;
m_usHttpPort = info.httpPort;

rules = info.rules;

if ((uiMasterServerSaysRestrictions & RESTRICTION_PLAYER_LIST) == false)
vecPlayers = info.playersPool;

1 change: 1 addition & 0 deletions Client/core/ServerBrowser/CServerList.h
Original file line number Diff line number Diff line change
@@ -222,6 +222,7 @@ class CServerListItem
CQueryReceiver queryReceiver;

std::vector<SString> vecPlayers;
std::unordered_map<SString, SString> rules;

void Query();

131 changes: 129 additions & 2 deletions Server/mods/deathmatch/logic/ASE.cpp
Original file line number Diff line number Diff line change
@@ -187,8 +187,8 @@ void ASE::DoPulse()
break;
}
case 'r':
{ // Our own lighter query for ingame browser - Release version only
strReply = QueryLightCached();
{ // New query for ingame server browser
strReply = QueryNewBrowserCached();
break;
}
case 'x':
@@ -395,6 +395,133 @@ const std::string* ASE::QueryLightCached()
return &m_strLightCached;
}

// Protect against a flood of server queries.
// Send cached version unless player count has changed, or last re-cache is older than m_lNewMinInterval
const std::string* ASE::QueryNewBrowserCached()
{
if (m_uiCurrentPlayerCount != m_uiNewLastPlayerCount || m_llCurrentTime - m_llNewLastTime > m_lNewMinInterval || m_strNewCached == "")
{
m_strNewCached = QueryNewBrowser();
m_llNewLastTime = m_llCurrentTime;
m_uiNewLastPlayerCount = m_uiCurrentPlayerCount;
}
return &m_strNewCached;
}

std::string ASE::QueryNewBrowser()
{
std::stringstream reply;

int iJoinedPlayers = m_pPlayerManager->CountJoined();
int iMaxPlayers = m_pMainConfig->GetMaxPlayers();
SString strPlayerCount = SString("%d/%d", iJoinedPlayers, iMaxPlayers);
SString strBuildType = SString("%d", MTASA_VERSION_TYPE);
SString strBuildNumber = SString("%d", MTASA_VERSION_BUILD);
SFixedString<32> strPingStatusFixed;
SFixedString<32> strNetRouteFixed;
g_pNetServer->GetPingStatus(&strPingStatusFixed);
g_pNetServer->GetNetRoute(&strNetRouteFixed);
SString strPingStatus = (const char*)strPingStatusFixed;
SString strNetRoute = (const char*)strNetRouteFixed;
SString strUpTime("%d", (uint)(time(NULL) - m_tStartTime));
SString strHttpPort("%d", m_pMainConfig->GetHTTPPort());

uint uiExtraDataLength = (strPlayerCount.length() + 1 + strBuildType.length() + 1 + strBuildNumber.length() + 1 + strPingStatus.length() + 1 +
strNetRoute.length() + 1 + strUpTime.length() + 1 + strHttpPort.length() + 1);
uint uiMaxMapNameLength = 250 - uiExtraDataLength;
m_strMapName = m_strMapName.Left(uiMaxMapNameLength);

reply << "EYE2";
// game
reply << (unsigned char)4;
reply << "mta";
// port
reply << (unsigned char)(m_strPort.length() + 1);
reply << m_strPort;
// server name
reply << (unsigned char)(m_pMainConfig->GetServerName().length() + 1);
reply << m_pMainConfig->GetServerName();
// game type
reply << (unsigned char)(m_strGameType.length() + 1);
reply << m_strGameType;
// map name with backwardly compatible large player count, build type and build number
reply << (unsigned char)(m_strMapName.length() + 1 + uiExtraDataLength);
reply << m_strMapName;
reply << (unsigned char)0;
reply << strPlayerCount;
reply << (unsigned char)0;
reply << strBuildType;
reply << (unsigned char)0;
reply << strBuildNumber;
reply << (unsigned char)0;
reply << strPingStatus;
reply << (unsigned char)0;
reply << strNetRoute;
reply << (unsigned char)0;
reply << strUpTime;
reply << (unsigned char)0;
reply << strHttpPort;
// version
std::string temp = MTA_DM_ASE_VERSION;
reply << (unsigned char)(temp.length() + 1);
reply << temp;
// passworded
reply << (unsigned char)((m_pMainConfig->HasPassword()) ? 1 : 0);
// serial verification?
reply << (unsigned char)((m_pMainConfig->GetSerialVerificationEnabled()) ? 1 : 0);
// players count
reply << (unsigned char)std::min(iJoinedPlayers, 255);
// players max
reply << (unsigned char)std::min(iMaxPlayers, 255);

// rules - this informs this response contains them
// previous version of this query did not have rules
reply << "RULES";

// send a max of 20 rules
list<CASERule*>::iterator rIter = IterBegin();
int rulesCount = 0;
for (; rIter != IterEnd() && rulesCount < 20; rIter++)
{
reply << (unsigned char)(strlen((*rIter)->GetKey()) + 1);
reply << (*rIter)->GetKey();
reply << (unsigned char)(strlen((*rIter)->GetValue()) + 1);
reply << (*rIter)->GetValue();
rulesCount++;
}
reply << (unsigned char)1;

// players
CPlayer* pPlayer = NULL;

// Keep the packet under 1350 bytes to try to avoid fragmentation
int iBytesLeft = 1340 - (int)reply.tellp();
int iPlayersLeft = iJoinedPlayers;

list<CPlayer*>::const_iterator pIter = m_pPlayerManager->IterBegin();
for (; pIter != m_pPlayerManager->IterEnd(); pIter++)
{
pPlayer = *pIter;
if (pPlayer->IsJoined())
{
// nick
std::string strPlayerName = RemoveColorCodes(pPlayer->GetNick());
if (strPlayerName.length() == 0)
strPlayerName = pPlayer->GetNick();

// Check if we can fit more names
iBytesLeft -= strPlayerName.length() + 1;
if (iBytesLeft < iPlayersLeft--)
strPlayerName = "";

reply << (unsigned char)(strPlayerName.length() + 1);
reply << strPlayerName.c_str();
}
}

return reply.str();
}

std::string ASE::QueryLight()
{
std::stringstream reply;
8 changes: 8 additions & 0 deletions Server/mods/deathmatch/logic/ASE.h
Original file line number Diff line number Diff line change
@@ -84,6 +84,8 @@ class ASE
const std::string* QueryFullCached();
std::string QueryFull();
const std::string* QueryLightCached();
std::string QueryNewBrowser();
const std::string* QueryNewBrowserCached();
const std::string* QueryXfireLightCached();
std::string QueryXfireLight();

@@ -120,6 +122,12 @@ class ASE
long m_lLightMinInterval;
std::string m_strLightCached;

// New query cache
unsigned int m_uiNewLastPlayerCount;
long long m_llNewLastTime;
long m_lNewMinInterval;
std::string m_strNewCached;

// XFire Light query cache
unsigned int m_uiXfireLightLastPlayerCount;
long long m_llXfireLightLastTime;
Loading