diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 8f3159143..b5cc636cf 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -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)); diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h index ebdc1eba7..3ac6d39b3 100644 --- a/src/engine/serverbrowser.h +++ b/src/engine/serverbrowser.h @@ -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; diff --git a/src/engine/shared/serverinfo.cpp b/src/engine/shared/serverinfo.cpp index 2e5236603..976aa8ce2 100644 --- a/src/engine/shared/serverinfo.cpp +++ b/src/engine/shared/serverinfo.cpp @@ -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); diff --git a/src/engine/shared/serverinfo.h b/src/engine/shared/serverinfo.h index f32c97fa9..97ff6b9c1 100644 --- a/src/engine/shared/serverinfo.h +++ b/src/engine/shared/serverinfo.h @@ -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]; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index effd2c5f1..acfc73746 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -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) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 2c27bc0a4..4b51f537b 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -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) diff --git a/src/game/server/player.h b/src/game/server/player.h index 0934a938f..7cd894dac 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -182,7 +182,6 @@ public: vec2 m_ShowDistance; bool m_SpecTeam; bool m_NinjaJetpack; - bool m_HasFinishScore; int m_ChatScore; diff --git a/src/game/server/scoreworker.cpp b/src/game/server/scoreworker.cpp index 0b8ad7d2d..aa21b0be8 100644 --- a/src/game/server/scoreworker.cpp +++ b/src/game/server/scoreworker.cpp @@ -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++) diff --git a/src/game/server/scoreworker.h b/src/game/server/scoreworker.h index 16d523f90..16fc23522 100644 --- a/src/game/server/scoreworker.h +++ b/src/game/server/scoreworker.h @@ -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 diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index 0cb15f182..09e656698 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -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; } } diff --git a/src/test/score.cpp b/src/test/score.cpp index 723ffa7f4..742a49cd5 100644 --- a/src/test/score.cpp +++ b/src/test/score.cpp @@ -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);