Skip to content
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

Server Browser Revamp #3933

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions Client/core/CQueryReceiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Fernando-A-Rocha marked this conversation as resolved.
Show resolved Hide resolved
}

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

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

class CQueryReceiver
Expand Down
2 changes: 2 additions & 0 deletions Client/core/ServerBrowser/CServerList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

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

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

void Query();

Expand Down
131 changes: 129 additions & 2 deletions Server/mods/deathmatch/logic/ASE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions Server/mods/deathmatch/logic/ASE.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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;
Expand Down
Loading