diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index d8fd9eee4..58bb09b75 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -234,7 +234,6 @@ MACRO_CONFIG_INT(SvFastDownload, sv_fast_download, 1, 0, 1, CFGFLAG_SERVER, "Ena MACRO_CONFIG_INT(SvShotgunBulletSound, sv_shotgun_bullet_sound, 0, 0, 1, CFGFLAG_SERVER, "Crazy shotgun bullet sound on/off") -MACRO_CONFIG_INT(SvCheckpointSave, sv_checkpoint_save, 1, 0, 1, CFGFLAG_SERVER, "Whether to save checkpoint times to the score file") MACRO_CONFIG_STR(SvScoreFolder, sv_score_folder, 32, "records", CFGFLAG_SERVER, "Folder to save score files to") MACRO_CONFIG_STR(SvRegionName, sv_region_name, 5, "UNK", CFGFLAG_SERVER, "Server region. Used for regional bans") diff --git a/src/game/ddracechat.h b/src/game/ddracechat.h index cb9743e7d..619567b91 100644 --- a/src/game/ddracechat.h +++ b/src/game/ddracechat.h @@ -43,6 +43,7 @@ CHAT_COMMAND("top5", "?i[rank to start with]", CFGFLAG_CHAT | CFGFLAG_SERVER, Co CHAT_COMMAND("times", "?s[player name] ?i[number of times to skip]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTimes, this, "/times ?s?i shows last 5 times of the server or of a player beginning with name s starting with time i (i = 1 by default, -1 for first)") CHAT_COMMAND("points", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConPoints, this, "Shows the global points of a player beginning with name r (your rank by default)") CHAT_COMMAND("top5points", "?i[number]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTopPoints, this, "Shows five points of the global point ladder beginning with rank i (1 by default)") +CHAT_COMMAND("timecp", "?r[player name]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConTimeCP, this, "Set your checkpoints based on another player") CHAT_COMMAND("team", "?i[id]", CFGFLAG_CHAT | CFGFLAG_SERVER, ConJoinTeam, this, "Lets you join team i (shows your team if left blank)") CHAT_COMMAND("lock", "?i['0'|'1']", CFGFLAG_CHAT | CFGFLAG_SERVER, ConLockTeam, this, "Toggle team lock so no one else can join and so the team restarts when a player dies. /lock 0 to unlock, /lock 1 to lock.") diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 9ea6c0610..1c59b47fb 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -1576,3 +1576,24 @@ void CGameContext::ConTopPoints(IConsole::IResult *pResult, void *pUserData) else pSelf->Score()->ShowTopPoints(pResult->m_ClientID); } + +void CGameContext::ConTimeCP(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(!CheckClientID(pResult->m_ClientID)) + return; + + if(g_Config.m_SvHideScore) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "chatresp", + "Showing the checkpoint times is not allowed on this server."); + return; + } + + CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientID]; + if(!pPlayer) + return; + + const char *pName = pResult->GetString(0); + pSelf->Score()->LoadPlayerData(pResult->m_ClientID, pName); +} diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 1dd9f29cf..8ec778878 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -362,6 +362,7 @@ private: static void ConTimes(IConsole::IResult *pResult, void *pUserData); static void ConPoints(IConsole::IResult *pResult, void *pUserData); static void ConTopPoints(IConsole::IResult *pResult, void *pUserData); + static void ConTimeCP(IConsole::IResult *pResult, void *pUserData); static void ConUTF8(IConsole::IResult *pResult, void *pUserData); static void ConDND(IConsole::IResult *pResult, void *pUserData); diff --git a/src/game/server/score.cpp b/src/game/server/score.cpp index 75b89796c..7d9d402b4 100644 --- a/src/game/server/score.cpp +++ b/src/game/server/score.cpp @@ -113,9 +113,9 @@ CScore::CScore(CGameContext *pGameServer, CDbConnectionPool *pPool) : m_pPool->Execute(CScoreWorker::Init, std::move(Tmp), "load best time"); } -void CScore::LoadPlayerData(int ClientID) +void CScore::LoadPlayerData(int ClientID, const char *pName) { - ExecPlayerThread(CScoreWorker::LoadPlayerData, "load player data", ClientID, "", 0); + ExecPlayerThread(CScoreWorker::LoadPlayerData, "load player data", ClientID, pName, 0); } void CScore::MapVote(int ClientID, const char *MapName) diff --git a/src/game/server/score.h b/src/game/server/score.h index 7908b2160..2f5e692e4 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -44,7 +44,7 @@ public: void MapInfo(int ClientID, const char *pMapName); void MapVote(int ClientID, const char *pMapName); - void LoadPlayerData(int ClientID); + void LoadPlayerData(int ClientID, const char *pName = ""); void SaveScore(int ClientID, float Time, const char *pTimestamp, float aCpTime[NUM_CHECKPOINTS], bool NotEligible); void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp); diff --git a/src/game/server/scoreworker.cpp b/src/game/server/scoreworker.cpp index 43404f8b2..59b17e0f4 100644 --- a/src/game/server/scoreworker.cpp +++ b/src/game/server/scoreworker.cpp @@ -131,20 +131,27 @@ bool CScoreWorker::LoadPlayerData(IDbConnection *pSqlServer, const ISqlData *pGa char aBuf[512]; // get best race time str_format(aBuf, sizeof(aBuf), - "SELECT Time, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8, cp9, cp10, " - " cp11, cp12, cp13, cp14, cp15, cp16, cp17, cp18, cp19, cp20, " - " cp21, cp22, cp23, cp24, cp25 " + "SELECT" + " (SELECT Time FROM %s_race WHERE Map = ? AND Name = ? ORDER BY Time ASC LIMIT 1) AS Time, " + " cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8, cp9, cp10, cp11, cp12, cp13, cp14, " + " cp15, cp16, cp17, cp18, cp19, cp20, cp21, cp22, cp23, cp24, cp25, " + " (cp1 + cp2 + cp3 + cp4 + cp5 + cp6 + cp7 + cp8 + cp9 + cp10 + cp11 + cp12 + cp13 + cp14 + " + " cp15 + cp16 + cp17 + cp18 + cp19 + cp20 + cp21 + cp22 + cp23 + cp24 + cp25 > 0) AS hasCP " "FROM %s_race " "WHERE Map = ? AND Name = ? " - "ORDER BY Time ASC " + "ORDER BY hasCP DESC, Time ASC " "LIMIT 1", - pSqlServer->GetPrefix()); + pSqlServer->GetPrefix(), pSqlServer->GetPrefix()); if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize)) { return true; } + + const char *pPlayer = pData->m_aName[0] != '\0' ? pData->m_aName : pData->m_aRequestingPlayer; pSqlServer->BindString(1, pData->m_aMap); pSqlServer->BindString(2, pData->m_aRequestingPlayer); + pSqlServer->BindString(3, pData->m_aMap); + pSqlServer->BindString(4, pPlayer); bool End; if(pSqlServer->Step(&End, pError, ErrorSize)) @@ -159,12 +166,9 @@ bool CScoreWorker::LoadPlayerData(IDbConnection *pSqlServer, const ISqlData *pGa pResult->m_Data.m_Info.m_Score = -Time; pResult->m_Data.m_Info.m_HasFinishScore = true; - if(g_Config.m_SvCheckpointSave) + for(int i = 0; i < NUM_CHECKPOINTS; i++) { - for(int i = 0; i < NUM_CHECKPOINTS; i++) - { - pResult->m_Data.m_Info.m_CpTime[i] = pSqlServer->GetFloat(i + 2); - } + pResult->m_Data.m_Info.m_CpTime[i] = pSqlServer->GetFloat(i + 2); } } diff --git a/src/test/score.cpp b/src/test/score.cpp index 27f9f35a6..76cc0b916 100644 --- a/src/test/score.cpp +++ b/src/test/score.cpp @@ -90,7 +90,7 @@ struct Score : public testing::TestWithParam ASSERT_EQ(NumInserted, 1); } - void InsertRank() + void InsertRank(float Time = 100.0, bool WithTimeCheckPoints = false) { str_copy(g_Config.m_SvSqlServerName, "USA", sizeof(g_Config.m_SvSqlServerName)); CSqlScoreData ScoreData(std::make_shared()); @@ -98,10 +98,10 @@ struct Score : public testing::TestWithParam str_copy(ScoreData.m_aGameUuid, "8d300ecf-5873-4297-bee5-95668fdff320", sizeof(ScoreData.m_aGameUuid)); str_copy(ScoreData.m_aName, "nameless tee", sizeof(ScoreData.m_aName)); ScoreData.m_ClientID = 0; - ScoreData.m_Time = 100.0; + ScoreData.m_Time = Time; str_copy(ScoreData.m_aTimestamp, "2021-11-24 19:24:08", sizeof(ScoreData.m_aTimestamp)); for(int i = 0; i < NUM_CHECKPOINTS; i++) - ScoreData.m_aCpCurrent[i] = i; + ScoreData.m_aCpCurrent[i] = WithTimeCheckPoints ? i : 0; str_copy(ScoreData.m_aRequestingPlayer, "deen", sizeof(ScoreData.m_aRequestingPlayer)); ASSERT_FALSE(CScoreWorker::SaveScore(m_pConn, &ScoreData, false, m_aError, sizeof(m_aError))) << m_aError; } @@ -175,6 +175,31 @@ TEST_P(SingleScore, RankServer) ExpectLines(m_pPlayerResult, {"nameless tee - 01:40.00 - better than 100% - requested by brainless tee", "Global rank 1 - USA rank 1"}, true); } +TEST_P(SingleScore, LoadPlayerData) +{ + InsertRank(120.0, true); + str_copy(m_PlayerRequest.m_aName, "", sizeof(m_PlayerRequest.m_aRequestingPlayer)); + 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); + for(int i = 0; i < NUM_CHECKPOINTS; i++) + { + ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_CpTime[i], 0); + } + + str_copy(m_PlayerRequest.m_aRequestingPlayer, "nameless tee", sizeof(m_PlayerRequest.m_aRequestingPlayer)); + str_copy(m_PlayerRequest.m_aName, "", sizeof(m_PlayerRequest.m_aRequestingPlayer)); + 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, 100.0); + for(int i = 0; i < NUM_CHECKPOINTS; i++) + { + ASSERT_EQ(m_pPlayerResult->m_Data.m_Info.m_CpTime[i], i); + } +} + TEST_P(SingleScore, TimesExists) { ASSERT_FALSE(CScoreWorker::ShowTimes(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError;