6585: Add client_score_kind field to serverinfo (followup) r=Robyt3 a=edg-l

followup on #5960 (fixes conflicts)

It specifies whether the scores are to be interpreted as times (string value starting with "time") or as points (string value starting with "points").

## Checklist

- [x] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: heinrich5991 <heinrich5991@gmail.com>
Co-authored-by: Edgar Luque <git@edgarluque.com>
This commit is contained in:
bors[bot] 2023-05-14 12:17:55 +00:00 committed by GitHub
commit 03d17a98ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 84 additions and 37 deletions

View file

@ -299,7 +299,7 @@ void CServer::CClient::Reset()
m_LastAckedSnapshot = -1;
m_LastInputTick = -1;
m_SnapRate = CClient::SNAPRATE_INIT;
m_Score = 0;
m_Score = -1;
m_NextMapChunk = 0;
m_Flags = 0;
}
@ -2017,7 +2017,7 @@ void CServer::CacheServerInfo(CCache *pCache, int Type, bool SendClients)
q.AddString(ClientClan(i), MAX_CLAN_LENGTH); // client clan
ADD_INT(q, m_aClients[i].m_Country); // client country
ADD_INT(q, m_aClients[i].m_Score); // client score
ADD_INT(q, m_aClients[i].m_Score == -1 ? -9999 : m_aClients[i].m_Score == 9999 ? -10000 : -m_aClients[i].m_Score); // client score
ADD_INT(q, GameServer()->IsClientPlayer(i) ? 1 : 0); // is player?
if(Type == SERVERINFO_EXTENDED)
q.AddString("", 0); // extra info, reserved
@ -2099,7 +2099,7 @@ void CServer::CacheServerInfoSixup(CCache *pCache, bool SendClients)
Packer.AddString(ClientName(i), MAX_NAME_LENGTH); // client name
Packer.AddString(ClientClan(i), MAX_CLAN_LENGTH); // client clan
Packer.AddInt(m_aClients[i].m_Country); // client country
Packer.AddInt(m_aClients[i].m_Score == -9999 ? -1 : -m_aClients[i].m_Score); // client score
Packer.AddInt(m_aClients[i].m_Score); // client score
Packer.AddInt(GameServer()->IsClientPlayer(i) ? 0 : 1); // flag spectator=1, bot=2 (player=0)
}
}
@ -2230,6 +2230,7 @@ void CServer::UpdateRegisterServerInfo()
"\"size\":%d"
"},"
"\"version\":\"%s\","
"\"client_score_kind\":\"time\","
"\"clients\":[",
MaxClients,
MaxPlayers,
@ -2266,7 +2267,7 @@ void CServer::UpdateRegisterServerInfo()
EscapeJson(aCName, sizeof(aCName), ClientName(i)),
EscapeJson(aCClan, sizeof(aCClan), ClientClan(i)),
m_aClients[i].m_Country,
m_aClients[i].m_Score,
m_aClients[i].m_Score == -1 ? -9999 : m_aClients[i].m_Score,
JsonBool(GameServer()->IsClientPlayer(i)),
aExtraPlayerInfo);
str_append(aInfo, aClientInfo, sizeof(aInfo));

View file

@ -33,6 +33,14 @@ public:
NUM_LOCS,
};
enum
{
CLIENT_SCORE_KIND_UNSPECIFIED,
CLIENT_SCORE_KIND_POINTS,
CLIENT_SCORE_KIND_TIME,
CLIENT_SCORE_KIND_TIME_BACKCOMPAT,
};
class CClient
{
public:
@ -68,6 +76,7 @@ public:
int m_MaxPlayers;
int m_NumPlayers;
int m_Flags;
int m_ClientScoreKind;
TRISTATE m_Favorite;
TRISTATE m_FavoriteAllowPing;
bool m_Official;

View file

@ -64,6 +64,7 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson)
const json_value &ServerInfo = *pJson;
const json_value &MaxClients = ServerInfo["max_clients"];
const json_value &MaxPlayers = ServerInfo["max_players"];
const json_value &ClientScoreKind = ServerInfo["client_score_kind"];
const json_value &Passworded = ServerInfo["passworded"];
const json_value &GameType = ServerInfo["game_type"];
const json_value &Name = ServerInfo["name"];
@ -74,6 +75,7 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson)
Error = Error || MaxClients.type != json_integer;
Error = Error || MaxPlayers.type != json_integer;
Error = Error || Passworded.type != json_boolean;
Error = Error || (ClientScoreKind.type != json_none && ClientScoreKind.type != json_string);
Error = Error || GameType.type != json_string || str_has_cc(GameType);
Error = Error || Name.type != json_string || str_has_cc(Name);
Error = Error || MapName.type != json_string || str_has_cc(MapName);
@ -85,6 +87,15 @@ bool CServerInfo2::FromJsonRaw(CServerInfo2 *pOut, const json_value *pJson)
}
pOut->m_MaxClients = json_int_get(&MaxClients);
pOut->m_MaxPlayers = json_int_get(&MaxPlayers);
pOut->m_ClientScoreKind = CServerInfo::CLIENT_SCORE_KIND_UNSPECIFIED;
if(ClientScoreKind.type == json_string && str_startswith(ClientScoreKind, "points"))
{
pOut->m_ClientScoreKind = CServerInfo::CLIENT_SCORE_KIND_POINTS;
}
else if(ClientScoreKind.type == json_string && str_startswith(ClientScoreKind, "time"))
{
pOut->m_ClientScoreKind = CServerInfo::CLIENT_SCORE_KIND_TIME;
}
pOut->m_Passworded = Passworded;
str_copy(pOut->m_aGameType, GameType);
str_copy(pOut->m_aName, Name);
@ -174,6 +185,7 @@ bool CServerInfo2::operator==(const CServerInfo2 &Other) const
Unequal = Unequal || m_NumClients != Other.m_NumClients;
Unequal = Unequal || m_MaxPlayers != Other.m_MaxPlayers;
Unequal = Unequal || m_NumPlayers != Other.m_NumPlayers;
Unequal = Unequal || m_ClientScoreKind != Other.m_ClientScoreKind;
Unequal = Unequal || m_Passworded != Other.m_Passworded;
Unequal = Unequal || str_comp(m_aGameType, Other.m_aGameType) != 0;
Unequal = Unequal || str_comp(m_aName, Other.m_aName) != 0;
@ -206,6 +218,7 @@ CServerInfo2::operator CServerInfo() const
Result.m_NumClients = m_NumClients;
Result.m_MaxPlayers = m_MaxPlayers;
Result.m_NumPlayers = m_NumPlayers;
Result.m_ClientScoreKind = m_ClientScoreKind;
Result.m_Flags = m_Passworded ? SERVER_FLAG_PASSWORD : 0;
str_copy(Result.m_aGameType, m_aGameType);
str_copy(Result.m_aName, m_aName);

View file

@ -29,6 +29,7 @@ public:
int m_NumClients; // Indirectly serialized.
int m_MaxPlayers;
int m_NumPlayers; // Not serialized.
int m_ClientScoreKind;
bool m_Passworded;
char m_aGameType[16];
char m_aName[64];

View file

@ -1091,6 +1091,19 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
s_ListBox.DoAutoSpacing(1.0f);
s_ListBox.DoStart(25.0f, pSelectedServer->m_NumReceivedClients, 1, 3, -1, &ServerScoreBoard);
int ClientScoreKind = pSelectedServer->m_ClientScoreKind;
if(ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_UNSPECIFIED)
{
if((str_find_nocase(pSelectedServer->m_aGameType, "race") || str_find_nocase(pSelectedServer->m_aGameType, "fastcap")) && g_Config.m_ClDDRaceScoreBoard)
{
ClientScoreKind = CServerInfo::CLIENT_SCORE_KIND_TIME_BACKCOMPAT;
}
else
{
ClientScoreKind = CServerInfo::CLIENT_SCORE_KIND_POINTS;
}
}
for(int i = 0; i < pSelectedServer->m_NumReceivedClients; i++)
{
const CServerInfo::CClient &CurrentClient = pSelectedServer->m_aClients[i];
@ -1119,16 +1132,38 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
char aTemp[16];
if(!CurrentClient.m_Player)
str_copy(aTemp, "SPEC");
else if((str_find_nocase(pSelectedServer->m_aGameType, "race") || str_find_nocase(pSelectedServer->m_aGameType, "fastcap")) && g_Config.m_ClDDRaceScoreBoard)
{
if(CurrentClient.m_Score == -9999 || CurrentClient.m_Score == 0)
aTemp[0] = 0;
else
str_time((int64_t)absolute(CurrentClient.m_Score) * 100, TIME_HOURS, aTemp, sizeof(aTemp));
str_copy(aTemp, "SPEC");
}
else if(ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_POINTS)
{
str_format(aTemp, sizeof(aTemp), "%d", CurrentClient.m_Score);
}
else
str_format(aTemp, sizeof(aTemp), "%d", CurrentClient.m_Score);
{
bool Empty = false;
int Time;
if(ClientScoreKind == CServerInfo::CLIENT_SCORE_KIND_TIME_BACKCOMPAT)
{
Time = abs(CurrentClient.m_Score);
Empty = Time == 0 || Time == 9999;
}
else
{
Time = CurrentClient.m_Score;
Empty = Time < 0;
}
if(!Empty)
{
str_time((int64_t)Time * 100, TIME_HOURS, aTemp, sizeof(aTemp));
}
else
{
aTemp[0] = 0;
}
}
float ScoreFontSize = 12.0f;
while(ScoreFontSize >= 4.0f && TextRender()->TextWidth(ScoreFontSize, aTemp, -1, -1.0f) > Score.w)

View file

@ -119,8 +119,7 @@ void CPlayer::Reset()
m_DND = false;
m_LastPause = 0;
m_Score = -9999;
m_HasFinishScore = false;
m_Score = -1;
// Variable initialized:
m_Last_Team = 0;
@ -337,11 +336,11 @@ void CPlayer::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
int Latency = SnappingClient == SERVER_DEMO_CLIENT ? m_Latency.m_Min : GameServer()->m_apPlayers[SnappingClient]->m_aCurLatency[m_ClientID];
int Score = absolute(m_Score) * -1;
int Score = m_Score;
// send 0 if times of others are not shown
if(SnappingClient != m_ClientID && g_Config.m_SvHideScore)
Score = -9999;
Score = -1;
if(!Server()->IsSixup(SnappingClient))
{
@ -350,7 +349,10 @@ void CPlayer::Snap(int SnappingClient)
return;
pPlayerInfo->m_Latency = Latency;
pPlayerInfo->m_Score = Score;
// -9999 stands for no time and isn't displayed in scoreboard, so
// shift the time by a second if the player actually took 9999
// seconds to finish the map.
pPlayerInfo->m_Score = Score == -1 ? -9999 : Score == 9999 ? -10000 : -Score;
pPlayerInfo->m_Local = (int)(m_ClientID == SnappingClient && (m_Paused != PAUSE_PAUSED || SnappingClientVersion >= VERSION_DDNET_OLD));
pPlayerInfo->m_ClientID = id;
pPlayerInfo->m_Team = m_Team;
@ -373,7 +375,7 @@ void CPlayer::Snap(int SnappingClient)
pPlayerInfo->m_PlayerFlags |= protocol7::PLAYERFLAG_ADMIN;
// Times are in milliseconds for 0.7
pPlayerInfo->m_Score = Score == -9999 ? -1 : -Score * 1000;
pPlayerInfo->m_Score = Score == -1 ? -1 : Score * 1000;
pPlayerInfo->m_Latency = Latency;
}
@ -886,13 +888,7 @@ void CPlayer::ProcessScoreResult(CScorePlayerResult &Result)
break;
case CScorePlayerResult::PLAYER_INFO:
GameServer()->Score()->PlayerData(m_ClientID)->Set(Result.m_Data.m_Info.m_Time, Result.m_Data.m_Info.m_aTimeCp);
m_Score = Result.m_Data.m_Info.m_Score;
m_HasFinishScore = Result.m_Data.m_Info.m_HasFinishScore;
// -9999 stands for no time and isn't displayed in scoreboard, so
// shift the time by a second if the player actually took 9999
// seconds to finish the map.
if(m_HasFinishScore && m_Score == -9999)
m_Score = -10000;
m_Score = Result.m_Data.m_Info.m_Time;
Server()->ExpireServerInfo();
int Birthday = Result.m_Data.m_Info.m_Birthday;
if(Birthday != 0 && !m_BirthdayAnnounced)

View file

@ -182,7 +182,6 @@ public:
vec2 m_ShowDistance;
bool m_SpecTeam;
bool m_NinjaJetpack;
bool m_HasFinishScore;
int m_ChatScore;

View file

@ -39,10 +39,8 @@ void CScorePlayerResult::SetVariant(Variant v)
m_Data.m_MapVote.m_aServer[0] = '\0';
break;
case PLAYER_INFO:
m_Data.m_Info.m_Score = -9999;
m_Data.m_Info.m_Birthday = 0;
m_Data.m_Info.m_HasFinishScore = false;
m_Data.m_Info.m_Time = 0;
m_Data.m_Info.m_Time = -1;
for(float &TimeCp : m_Data.m_Info.m_aTimeCp)
TimeCp = 0;
}
@ -167,8 +165,6 @@ bool CScoreWorker::LoadPlayerData(IDbConnection *pSqlServer, const ISqlData *pGa
// get the best time
float Time = pSqlServer->GetFloat(1);
pResult->m_Data.m_Info.m_Time = Time;
pResult->m_Data.m_Info.m_Score = -Time;
pResult->m_Data.m_Info.m_HasFinishScore = true;
}
for(int i = 0; i < NUM_CHECKPOINTS; i++)

View file

@ -45,10 +45,8 @@ struct CScorePlayerResult : ISqlResult
char m_aBroadcast[1024];
struct
{
float m_Time;
float m_Time; // -1 for no time.
float m_aTimeCp[NUM_CHECKPOINTS];
int m_Score;
int m_HasFinishScore;
int m_Birthday; // 0 indicates no birthday
} m_Info;
struct

View file

@ -818,10 +818,9 @@ void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp)
}
int TTime = 0 - (int)Time;
if(Player->m_Score < TTime || !Player->m_HasFinishScore)
if(Player->m_Score == -1 || Player->m_Score < TTime)
{
Player->m_Score = TTime;
Player->m_HasFinishScore = true;
}
}

View file

@ -183,7 +183,7 @@ TEST_P(SingleScore, LoadPlayerData)
ASSERT_FALSE(CScoreWorker::LoadPlayerData(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError;
EXPECT_EQ(m_pPlayerResult->m_MessageKind, CScorePlayerResult::PLAYER_INFO);
ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_Time, 0.0);
ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_Time, -1.0);
for(auto &Time : m_pPlayerResult->m_Data.m_Info.m_aTimeCp)
{
ASSERT_EQ(Time, 0);
@ -205,7 +205,7 @@ TEST_P(SingleScore, LoadPlayerData)
ASSERT_FALSE(CScoreWorker::LoadPlayerData(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError;
EXPECT_EQ(m_pPlayerResult->m_MessageKind, CScorePlayerResult::PLAYER_INFO);
ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_Time, 0.0);
ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_Time, -1.0);
for(int i = 0; i < NUM_CHECKPOINTS; i++)
{
ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_aTimeCp[i], i);