From 528ca377ed2c2fb4f7dad28bd2c446490519af15 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 10 Jun 2020 11:26:40 +0200 Subject: [PATCH] Thread safe saving of team score --- src/engine/server/sql_string_helpers.h | 5 + src/game/server/score/sql_score.cpp | 137 ++++++++++--------------- src/game/server/score/sql_score.h | 9 +- 3 files changed, 63 insertions(+), 88 deletions(-) diff --git a/src/engine/server/sql_string_helpers.h b/src/engine/server/sql_string_helpers.h index 120a221af..a5cad6871 100644 --- a/src/engine/server/sql_string_helpers.h +++ b/src/engine/server/sql_string_helpers.h @@ -40,6 +40,11 @@ public: return *this; } + bool operator < (const CSqlString& other) const + { + return strcmp(m_aString, other.m_aString) < 0; + } + private: char m_aString[size]; char m_aClearString[size * 2 - 1]; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 327e2c6a3..1bafc8aa4 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "../entities/character.h" #include "../gamemodes/DDRace.h" @@ -630,34 +631,35 @@ bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlDataConsole(); if(pCon->m_Cheated) return; - CSqlTeamScoreData *Tmp = new CSqlTeamScoreData(); - Tmp->m_NotEligible = false; for(unsigned int i = 0; i < Size; i++) { - Tmp->m_aClientIDs[i] = aClientIDs[i]; - Tmp->m_aNames[i] = Server()->ClientName(aClientIDs[i]); - Tmp->m_NotEligible = Tmp->m_NotEligible || GameServer()->m_apPlayers[aClientIDs[i]]->m_NotEligibleForFinish; + if(GameServer()->m_apPlayers[aClientIDs[i]]->m_NotEligibleForFinish) + return; } + CSqlTeamScoreData *Tmp = new CSqlTeamScoreData(nullptr); + for(unsigned int i = 0; i < Size; i++) + Tmp->m_aNames[i] = Server()->ClientName(aClientIDs[i]); Tmp->m_Size = Size; Tmp->m_Time = Time; str_copy(Tmp->m_aTimestamp, pTimestamp, sizeof(Tmp->m_aTimestamp)); + FormatUuid(GameServer()->GameUuid(), Tmp->m_GameUuid, sizeof(Tmp->m_GameUuid)); + Tmp->m_Map = g_Config.m_SvMap; - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(SaveTeamScoreThread, Tmp, false), "save team score"); - */ + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(SaveTeamScoreThread, Tmp), + "save team score"); } -bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* const CSqlTeamScoreData *pData = dynamic_cast(pGameData); - if (HandleFailure) + if(HandleFailure) { - if (!g_Config.m_SvSqlFailureFile[0]) + if(!g_Config.m_SvSqlFailureFile[0]) return true; dbg_msg("sql", "ERROR: Could not save TeamScore, writing insert to a file now..."); @@ -673,7 +675,7 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlDatam_Size; i++) { - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %%s_teamrace(Map, Name, Timestamp, Time, ID, GameID, DDNet7) VALUES ('%s', '%s', '%s', '%.2f', @id, '%s', false);", pData->m_Map.ClrStr(), pData->m_aNames[i].ClrStr(), pData->m_aTimestamp, pData->m_Time, pData->m_GameUuid.ClrStr()); + str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %%s_teamrace(Map, Name, Timestamp, Time, ID, GameID, DDNet7) VALUES ('%s', '%s', '%s', '%.2f', @id, '%s', false);", pData->m_Map.ClrStr(), pData->m_aNames[i].ClrStr(), pData->m_aTimestamp, pData->m_Time, pData->m_GameUuid); io_write(File, aBuf, str_length(aBuf)); io_write_newline(File); } @@ -685,99 +687,68 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlDatam_NotEligible) - { - return false; - } - try { char aBuf[2300]; - char aUpdateID[17]; - aUpdateID[0] = 0; - str_format(aBuf, sizeof(aBuf), "SELECT Name, l.ID, Time FROM ((SELECT ID FROM %s_teamrace WHERE Map = '%s' AND Name = '%s' and DDNet7 = false) as l) LEFT JOIN %s_teamrace as r ON l.ID = r.ID ORDER BY ID;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_aNames[0].ClrStr(), pSqlServer->GetPrefix()); + // get the names sorted in a tab separated string + const sqlstr::CSqlString *apNames[MAX_CLIENTS]; + for(unsigned int i = 0; i < pData->m_Size; i++) + apNames[i] = &pData->m_aNames[i]; + std::sort(apNames, apNames+pData->m_Size); + char aSortedNames[2048] = {0}; + for(unsigned int i = 0; i < pData->m_Size; i++) + { + if(i != 0) + str_append(aSortedNames, "\t", sizeof(aSortedNames)); + str_append(aSortedNames, apNames[i]->ClrStr(), sizeof(aSortedNames)); + } + str_format(aBuf, sizeof(aBuf), + "SELECT l.ID, Time " + "FROM ((" // preselect teams with first name in team + "SELECT ID " + "FROM %s_teamrace " + "WHERE Map = '%s' AND Name = '%s' AND DDNet7 = false" + ") as l" + ") INNER JOIN %s_teamrace AS r ON l.ID = r.ID " + "GROUP BY ID " + "HAVING GROUP_CONCAT(Name ORDER BY Name SEPARATOR '\t') = '%s'", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_aNames[0].ClrStr(), + pSqlServer->GetPrefix(), aSortedNames); pSqlServer->executeSqlQuery(aBuf); if (pSqlServer->GetResults()->rowsCount() > 0) { - char aID[17]; - char aID2[17]; - char aName[64]; - unsigned int Count = 0; - bool ValidNames = true; - pSqlServer->GetResults()->first(); float Time = (float)pSqlServer->GetResults()->getDouble("Time"); - strcpy(aID, pSqlServer->GetResults()->getString("ID").c_str()); - - do + auto ID = pSqlServer->GetResults()->getString("ID"); + dbg_msg("sql", "found team rank from same team (old time: %f, new time: %f)", Time, pData->m_Time); + if(pData->m_Time < Time) { - strcpy(aID2, pSqlServer->GetResults()->getString("ID").c_str()); - strcpy(aName, pSqlServer->GetResults()->getString("Name").c_str()); - sqlstr::ClearString(aName); - if (str_comp(aID, aID2) != 0) - { - if (ValidNames && Count == pData->m_Size) - { - if (pData->m_Time < Time) - strcpy(aUpdateID, aID); - else - goto end; - break; - } - - Time = (float)pSqlServer->GetResults()->getDouble("Time"); - ValidNames = true; - Count = 0; - strcpy(aID, aID2); - } - - if (!ValidNames) - continue; - - ValidNames = false; - - for(unsigned int i = 0; i < pData->m_Size; i++) - { - if (str_comp(aName, pData->m_aNames[i].ClrStr()) == 0) - { - ValidNames = true; - Count++; - break; - } - } - } while (pSqlServer->GetResults()->next()); - - if (ValidNames && Count == pData->m_Size) - { - if (pData->m_Time < Time) - strcpy(aUpdateID, aID); - else - goto end; + str_format(aBuf, sizeof(aBuf), + "UPDATE %s_teamrace SET Time='%.2f', Timestamp='%s', DDNet7=false WHERE ID = '%s';", + pSqlServer->GetPrefix(), pData->m_Time, pData->m_aTimestamp, ID.c_str()); + dbg_msg("sql", "%s", aBuf); + pSqlServer->executeSql(aBuf); } } - - if (aUpdateID[0]) - { - str_format(aBuf, sizeof(aBuf), "UPDATE %s_teamrace SET Time='%.2f', Timestamp='%s', DDNet7=false WHERE ID = '%s';", pSqlServer->GetPrefix(), pData->m_Time, pData->m_aTimestamp, aUpdateID); - dbg_msg("sql", "%s", aBuf); - pSqlServer->executeSql(aBuf); - } else { pSqlServer->executeSql("SET @id = UUID();"); for(unsigned int i = 0; i < pData->m_Size; i++) { - // if no entry found... create a new one - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_teamrace(Map, Name, Timestamp, Time, ID, GameID, DDNet7) VALUES ('%s', '%s', '%s', '%.2f', @id, '%s', false);", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_aNames[i].ClrStr(), pData->m_aTimestamp, pData->m_Time, pData->m_GameUuid.ClrStr()); + // if no entry found... create a new one + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %s_teamrace(Map, Name, Timestamp, Time, ID, GameID, DDNet7) " + "VALUES ('%s', '%s', '%s', '%.2f', @id, '%s', false);", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_aNames[i].ClrStr(), + pData->m_aTimestamp, pData->m_Time, pData->m_GameUuid); dbg_msg("sql", "%s", aBuf); pSqlServer->executeSql(aBuf); } } - end: dbg_msg("sql", "Updating team time done"); return true; } @@ -787,8 +758,6 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData char m_aRequestingPlayer[MAX_NAME_LENGTH]; }; -struct CSqlTeamScoreData : CSqlData +struct CSqlTeamScoreData : CSqlData { - bool m_NotEligible; + using CSqlData::CSqlData; + char m_GameUuid[UUID_MAXSTRSIZE]; + sqlstr::CSqlString m_Map; float m_Time; char m_aTimestamp[TIMESTAMP_STR_LENGTH]; unsigned int m_Size; - int m_aClientIDs[MAX_CLIENTS]; sqlstr::CSqlString m_aNames[MAX_CLIENTS]; }; @@ -209,7 +210,7 @@ class CSqlScore: public IScore static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); CGameContext *GameServer() { return m_pGameServer; } IServer *Server() { return m_pServer; }