diff --git a/src/engine/server.h b/src/engine/server.h index af9c395bc..99e034b2e 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -356,6 +356,9 @@ public: virtual void TeehistorianRecordPlayerJoin(int ClientId, bool Sixup) = 0; virtual void TeehistorianRecordPlayerDrop(int ClientId, const char *pReason) = 0; virtual void TeehistorianRecordPlayerRejoin(int ClientId) = 0; + virtual void TeehistorianRecordPlayerName(int ClientId, const char *pName) = 0; + virtual void TeehistorianRecordPlayerFinish(int ClientId, int TimeTicks) = 0; + virtual void TeehistorianRecordTeamFinish(int TeamId, int TimeTicks) = 0; virtual void FillAntibot(CAntibotRoundData *pData) = 0; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index f258dea50..403d6cf8c 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -354,10 +354,11 @@ bool CServer::SetClientNameImpl(int ClientId, const char *pNameRequest, bool Set bool Changed = str_comp(m_aClients[ClientId].m_aName, aNameTry) != 0; - if(Set) + if(Set && Changed) { // set the client name str_copy(m_aClients[ClientId].m_aName, aNameTry); + GameServer()->TeehistorianRecordPlayerName(ClientId, m_aClients[ClientId].m_aName); } return Changed; diff --git a/src/engine/shared/teehistorian_ex_chunks.h b/src/engine/shared/teehistorian_ex_chunks.h index b1f07fefd..8546e521c 100644 --- a/src/engine/shared/teehistorian_ex_chunks.h +++ b/src/engine/shared/teehistorian_ex_chunks.h @@ -18,3 +18,6 @@ UUID(TEEHISTORIAN_TEAM_PRACTICE, "teehistorian-team-practice@ddnet.tw") UUID(TEEHISTORIAN_PLAYER_READY, "teehistorian-player-ready@ddnet.tw") UUID(TEEHISTORIAN_PLAYER_REJOIN, "teehistorian-rejoinver6@ddnet.org") UUID(TEEHISTORIAN_ANTIBOT, "teehistorian-antibot@ddnet.org") +UUID(TEEHISTORIAN_PLAYER_NAME, "teehistorian-player-name@ddnet.org") +UUID(TEEHISTORIAN_PLAYER_FINISH, "teehistorian-player-finish@ddnet.org") +UUID(TEEHISTORIAN_TEAM_FINISH, "teehistorian-team-finish@ddnet.org") diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 9e806cd33..7c0f93693 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1767,6 +1767,30 @@ void CGameContext::TeehistorianRecordPlayerRejoin(int ClientId) } } +void CGameContext::TeehistorianRecordPlayerName(int ClientId, const char *pName) +{ + if(m_TeeHistorianActive) + { + m_TeeHistorian.RecordPlayerName(ClientId, pName); + } +} + +void CGameContext::TeehistorianRecordPlayerFinish(int ClientId, int TimeTicks) +{ + if(m_TeeHistorianActive) + { + m_TeeHistorian.RecordPlayerFinish(ClientId, TimeTicks); + } +} + +void CGameContext::TeehistorianRecordTeamFinish(int TeamId, int TimeTicks) +{ + if(m_TeeHistorianActive) + { + m_TeeHistorian.RecordTeamFinish(TeamId, TimeTicks); + } +} + bool CGameContext::OnClientDDNetVersionKnown(int ClientId) { IServer::CClientInfo Info; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index a47effe24..a011e39bf 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -328,6 +328,9 @@ public: void TeehistorianRecordPlayerJoin(int ClientId, bool Sixup) override; void TeehistorianRecordPlayerDrop(int ClientId, const char *pReason) override; void TeehistorianRecordPlayerRejoin(int ClientId) override; + void TeehistorianRecordPlayerName(int ClientId, const char *pName) override; + void TeehistorianRecordPlayerFinish(int ClientId, int TimeTicks) override; + void TeehistorianRecordTeamFinish(int TeamId, int TimeTicks) override; bool IsClientReady(int ClientId) const override; bool IsClientPlayer(int ClientId) const override; diff --git a/src/game/server/score.cpp b/src/game/server/score.cpp index dedb1eb1a..3b73fb863 100644 --- a/src/game/server/score.cpp +++ b/src/game/server/score.cpp @@ -145,12 +145,14 @@ void CScore::MapInfo(int ClientId, const char *pMapName) ExecPlayerThread(CScoreWorker::MapInfo, "map info", ClientId, pMapName, 0); } -void CScore::SaveScore(int ClientId, float Time, const char *pTimestamp, const float aTimeCp[NUM_CHECKPOINTS], bool NotEligible) +void CScore::SaveScore(int ClientId, int TimeTicks, const char *pTimestamp, const float aTimeCp[NUM_CHECKPOINTS], bool NotEligible) { CConsole *pCon = (CConsole *)GameServer()->Console(); if(pCon->Cheated() || NotEligible) return; + GameServer()->TeehistorianRecordPlayerFinish(ClientId, TimeTicks); + CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientId]; if(pCurPlayer->m_ScoreFinishResult != nullptr) dbg_msg("sql", "WARNING: previous save score result didn't complete, overwriting it now"); @@ -160,7 +162,7 @@ void CScore::SaveScore(int ClientId, float Time, const char *pTimestamp, const f FormatUuid(GameServer()->GameUuid(), Tmp->m_aGameUuid, sizeof(Tmp->m_aGameUuid)); Tmp->m_ClientId = ClientId; str_copy(Tmp->m_aName, Server()->ClientName(ClientId), sizeof(Tmp->m_aName)); - Tmp->m_Time = Time; + Tmp->m_Time = (float)(TimeTicks) / (float)Server()->TickSpeed(); str_copy(Tmp->m_aTimestamp, pTimestamp, sizeof(Tmp->m_aTimestamp)); for(int i = 0; i < NUM_CHECKPOINTS; i++) Tmp->m_aCurrentTimeCp[i] = aTimeCp[i]; @@ -168,7 +170,7 @@ void CScore::SaveScore(int ClientId, float Time, const char *pTimestamp, const f m_pPool->ExecuteWrite(CScoreWorker::SaveScore, std::move(Tmp), "save score"); } -void CScore::SaveTeamScore(int *pClientIds, unsigned int Size, float Time, const char *pTimestamp) +void CScore::SaveTeamScore(int Team, int *pClientIds, unsigned int Size, int TimeTicks, const char *pTimestamp) { CConsole *pCon = (CConsole *)GameServer()->Console(); if(pCon->Cheated()) @@ -178,11 +180,14 @@ void CScore::SaveTeamScore(int *pClientIds, unsigned int Size, float Time, const if(GameServer()->m_apPlayers[pClientIds[i]]->m_NotEligibleForFinish) return; } + + GameServer()->TeehistorianRecordTeamFinish(Team, TimeTicks); + auto Tmp = std::make_unique(); for(unsigned int i = 0; i < Size; i++) str_copy(Tmp->m_aaNames[i], Server()->ClientName(pClientIds[i]), sizeof(Tmp->m_aaNames[i])); Tmp->m_Size = Size; - Tmp->m_Time = Time; + Tmp->m_Time = (float)TimeTicks / (float)Server()->TickSpeed(); str_copy(Tmp->m_aTimestamp, pTimestamp, sizeof(Tmp->m_aTimestamp)); FormatUuid(GameServer()->GameUuid(), Tmp->m_aGameUuid, sizeof(Tmp->m_aGameUuid)); str_copy(Tmp->m_aMap, g_Config.m_SvMap, sizeof(Tmp->m_aMap)); diff --git a/src/game/server/score.h b/src/game/server/score.h index 7ed6abbb2..d3d4c7dc4 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -49,9 +49,9 @@ public: void MapVote(int ClientId, const char *pMapName); void LoadPlayerData(int ClientId, const char *pName = ""); void LoadPlayerTimeCp(int ClientId, const char *pName = ""); - void SaveScore(int ClientId, float Time, const char *pTimestamp, const float aTimeCp[NUM_CHECKPOINTS], bool NotEligible); + void SaveScore(int ClientId, int TimeTicks, const char *pTimestamp, const float aTimeCp[NUM_CHECKPOINTS], bool NotEligible); - void SaveTeamScore(int *pClientIds, unsigned int Size, float Time, const char *pTimestamp); + void SaveTeamScore(int Team, int *pClientIds, unsigned int Size, int TimeTicks, const char *pTimestamp); void ShowTop(int ClientId, int Offset = 1); void ShowRank(int ClientId, const char *pName); diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index 6f15fa78b..2274c9ffc 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -335,8 +335,9 @@ void CGameTeams::CheckTeamFinished(int Team) if(PlayersCount > 0) { - float Time = (float)(Server()->Tick() - GetStartTime(apTeamPlayers[0])) / ((float)Server()->TickSpeed()); - if(Time < 0.000001f) + int TimeTicks = Server()->Tick() - GetStartTime(apTeamPlayers[0]); + float Time = (float)TimeTicks / (float)Server()->TickSpeed(); + if(TimeTicks <= 0) { return; } @@ -348,7 +349,7 @@ void CGameTeams::CheckTeamFinished(int Team) char aBuf[256]; str_format(aBuf, sizeof(aBuf), "Your team would've finished in: %d minute(s) %5.2f second(s). Since you had practice mode enabled your rank doesn't count.", - (int)Time / 60, Time - ((int)Time / 60 * 60)); + (int)Time / 50 / 60, Time - ((int)Time / 60 * 60)); GameServer()->SendChatTeam(Team, aBuf); for(unsigned int i = 0; i < PlayersCount; ++i) @@ -365,7 +366,7 @@ void CGameTeams::CheckTeamFinished(int Team) for(unsigned int i = 0; i < PlayersCount; ++i) OnFinish(apTeamPlayers[i], Time, aTimestamp); ChangeTeamState(Team, TEAMSTATE_FINISHED); // TODO: Make it better - OnTeamFinish(apTeamPlayers, PlayersCount, Time, aTimestamp); + OnTeamFinish(Team, apTeamPlayers, PlayersCount, TimeTicks, aTimestamp); } } } @@ -666,7 +667,7 @@ float *CGameTeams::GetCurrentTimeCp(CPlayer *Player) return NULL; } -void CGameTeams::OnTeamFinish(CPlayer **Players, unsigned int Size, float Time, const char *pTimestamp) +void CGameTeams::OnTeamFinish(int Team, CPlayer **Players, unsigned int Size, int TimeTicks, const char *pTimestamp) { int aPlayerCids[MAX_CLIENTS]; @@ -685,13 +686,16 @@ void CGameTeams::OnTeamFinish(CPlayer **Players, unsigned int Size, float Time, } if(Size >= (unsigned int)g_Config.m_SvMinTeamSize) - GameServer()->Score()->SaveTeamScore(aPlayerCids, Size, Time, pTimestamp); + GameServer()->Score()->SaveTeamScore(Team, aPlayerCids, Size, TimeTicks, pTimestamp); } -void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp) +void CGameTeams::OnFinish(CPlayer *Player, int TimeTicks, const char *pTimestamp) { if(!Player || !Player->IsPlaying()) return; + + float Time = TimeTicks / (float)Server()->TickSpeed(); + // TODO:DDRace:btd: this ugly const int ClientId = Player->GetCid(); CPlayerData *pData = GameServer()->Score()->PlayerData(ClientId); @@ -804,7 +808,7 @@ void CGameTeams::OnFinish(CPlayer *Player, float Time, const char *pTimestamp) if(CallSaveScore) if(g_Config.m_SvNamelessScore || !str_startswith(Server()->ClientName(ClientId), "nameless tee")) - GameServer()->Score()->SaveScore(ClientId, Time, pTimestamp, + GameServer()->Score()->SaveScore(ClientId, TimeTicks, pTimestamp, GetCurrentTimeCp(Player), Player->m_NotEligibleForFinish); bool NeedToSendNewServerRecord = false; diff --git a/src/game/server/teams.h b/src/game/server/teams.h index b703ed078..b6a3bcdc5 100644 --- a/src/game/server/teams.h +++ b/src/game/server/teams.h @@ -47,8 +47,8 @@ class CGameTeams */ void KillTeam(int Team, int NewStrongId, int ExceptId = -1); bool TeamFinished(int Team); - void OnTeamFinish(CPlayer **Players, unsigned int Size, float Time, const char *pTimestamp); - void OnFinish(CPlayer *Player, float Time, const char *pTimestamp); + void OnTeamFinish(int Team, CPlayer **Players, unsigned int Size, int TimeTicks, const char *pTimestamp); + void OnFinish(CPlayer *Player, int TimeTicks, const char *pTimestamp); public: enum diff --git a/src/game/server/teehistorian.cpp b/src/game/server/teehistorian.cpp index 022c42819..e566c845e 100644 --- a/src/game/server/teehistorian.cpp +++ b/src/game/server/teehistorian.cpp @@ -573,6 +573,23 @@ void CTeeHistorian::RecordPlayerDrop(int ClientId, const char *pReason) Write(Buffer.Data(), Buffer.Size()); } +void CTeeHistorian::RecordPlayerName(int ClientId, const char *pName) +{ + EnsureTickWritten(); + + CPacker Buffer; + Buffer.Reset(); + Buffer.AddInt(ClientId); + Buffer.AddString(pName, 0); + + if(m_Debug) + { + dbg_msg("teehistorian", "drop cid=%d reason='%s'", ClientId, pName); + } + + WriteExtra(UUID_TEEHISTORIAN_PLAYER_NAME, Buffer.Data(), Buffer.Size()); +} + void CTeeHistorian::RecordConsoleCommand(int ClientId, int FlagMask, const char *pCmd, IConsole::IResult *pResult) { EnsureTickWritten(); @@ -794,6 +811,34 @@ void CTeeHistorian::RecordAntibot(const void *pData, int DataSize) WriteExtra(UUID_TEEHISTORIAN_ANTIBOT, pData, DataSize); } +void CTeeHistorian::RecordPlayerFinish(int ClientId, int TimeTicks) +{ + CPacker Buffer; + Buffer.Reset(); + Buffer.AddInt(ClientId); + Buffer.AddInt(TimeTicks); + if(m_Debug) + { + dbg_msg("teehistorian", "player_finish cid=%d Time=%d", ClientId, TimeTicks); + } + + WriteExtra(UUID_TEEHISTORIAN_PLAYER_FINISH, Buffer.Data(), Buffer.Size()); +} + +void CTeeHistorian::RecordTeamFinish(int TeamId, int TimeTicks) +{ + CPacker Buffer; + Buffer.Reset(); + Buffer.AddInt(TeamId); + Buffer.AddInt(TimeTicks); + if(m_Debug) + { + dbg_msg("teehistorian", "player_finish cid=%d Time=%d", TeamId, TimeTicks); + } + + WriteExtra(UUID_TEEHISTORIAN_TEAM_FINISH, Buffer.Data(), Buffer.Size()); +} + void CTeeHistorian::Finish() { dbg_assert(m_State == STATE_START || m_State == STATE_INPUTS || m_State == STATE_BEFORE_ENDTICK || m_State == STATE_BEFORE_TICK, "invalid teehistorian state"); diff --git a/src/game/server/teehistorian.h b/src/game/server/teehistorian.h index e9a8eb343..46e97862f 100644 --- a/src/game/server/teehistorian.h +++ b/src/game/server/teehistorian.h @@ -70,6 +70,7 @@ public: void RecordPlayerRejoin(int ClientId); void RecordPlayerReady(int ClientId); void RecordPlayerDrop(int ClientId, const char *pReason); + void RecordPlayerName(int ClientId, const char *pName); void RecordConsoleCommand(int ClientId, int FlagMask, const char *pCmd, IConsole::IResult *pResult); void RecordTestExtra(); void RecordPlayerSwap(int ClientId1, int ClientId2); @@ -90,6 +91,9 @@ public: void RecordAntibot(const void *pData, int DataSize); + void RecordPlayerFinish(int ClientId, int TimeTicks); + void RecordTeamFinish(int TeamId, int TimeTicks); + int m_Debug; // Possible values: 0, 1, 2. private: diff --git a/src/test/teehistorian.cpp b/src/test/teehistorian.cpp index 60a912f92..1a95ee714 100644 --- a/src/test/teehistorian.cpp +++ b/src/test/teehistorian.cpp @@ -838,6 +838,53 @@ TEST_F(TeeHistorian, AntibotEmptyMessage) Expect(EXPECTED, sizeof(EXPECTED)); } +TEST_F(TeeHistorian, PlayerName) +{ + const unsigned char EXPECTED[] = { + // EX uuid=d016f9b9-4151-3b87-87e5-3a6087eb5f26 datalen=4 + 0x4a, + 0xd0, 0x16, 0xf9, 0xb9, 0x41, 0x51, 0x3b, 0x87, + 0x87, 0xe5, 0x3a, 0x60, 0x87, 0xeb, 0x5f, 0x26, + 0x0e, + // (PLAYER_NAME) id=21 name="nameless tee" + 0x15, + 0x6e, 0x61, 0x6d, 0x65, 0x6c, 0x65, 0x73, 0x73, + 0x20, 0x74, 0x65, 0x65, 0x00}; + + m_TH.RecordPlayerName(21, "nameless tee"); + Expect(EXPECTED, sizeof(EXPECTED)); +} + +TEST_F(TeeHistorian, PlayerFinish) +{ + const unsigned char EXPECTED[] = { + // EX uuid=68943c01-2348-3e01-9490-3f27f8269d94 datalen=4 + 0x4a, + 0x68, 0x94, 0x3c, 0x01, 0x23, 0x48, 0x3e, 0x01, + 0x94, 0x90, 0x3f, 0x27, 0xf8, 0x26, 0x9d, 0x94, + 0x04, + // (PLAYER_FINISH) id=1 time=1000000 + 0x01, 0x80, 0x89, 0x7a}; + + m_TH.RecordPlayerFinish(1, 1000000); + Expect(EXPECTED, sizeof(EXPECTED)); +} + +TEST_F(TeeHistorian, TeamFinish) +{ + const unsigned char EXPECTED[] = { + // EX uuid=9588b9af-3fdc-3760-8043-82deeee317a5 datalen=4 + 0x4a, + 0x95, 0x88, 0xb9, 0xaf, 0x3f, 0xdc, 0x37, 0x60, + 0x80, 0x43, 0x82, 0xde, 0xee, 0xe3, 0x17, 0xa5, + 0x03, + // (TEAM_FINISH) team=63 Time=5000 + 0x3f, 0xa8, 0x0f}; + + m_TH.RecordTeamFinish(63, 1000); + Expect(EXPECTED, sizeof(EXPECTED)); +} + TEST_F(TeeHistorian, PrevGameUuid) { m_GameInfo.m_HavePrevGameUuid = true;