5372: Get best Time Checkpoints back if map has one, add /timecp, remove sv_checkpoint_save r=heinrich5991 a=def-

Even if you made a better finish without time cps before. Can happen now
that we add Time CPs to existing maps:
https://docs.google.com/spreadsheets/d/1yMMBLSbS2cOSYsbIMkDZpMLaV6k930mXj5yVZF0Uuaw/edit#gid=0

As suggested by snail

<!-- What is the motivation for the changes of this pull request -->

## Checklist

- [ ] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [x] Written a unit test if it works standalone, system.c especially
- [ ] 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: Dennis Felsing <dennis@felsin9.de>
This commit is contained in:
bors[bot] 2022-06-09 08:34:20 +00:00 committed by GitHub
commit d9a6175e30
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 68 additions and 17 deletions

View file

@ -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")

View file

@ -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.")

View file

@ -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);
}

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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,14 +166,11 @@ 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++)
{
pResult->m_Data.m_Info.m_CpTime[i] = pSqlServer->GetFloat(i + 2);
}
}
}
// birthday check
str_format(aBuf, sizeof(aBuf),

View file

@ -90,7 +90,7 @@ struct Score : public testing::TestWithParam<IDbConnection *>
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<CScorePlayerResult>());
@ -98,10 +98,10 @@ struct Score : public testing::TestWithParam<IDbConnection *>
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;