From 2d7886bf335cac10b2aef74b3ad1a47b62f198fe Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 27 May 2020 17:49:07 +0200 Subject: [PATCH 01/44] Small cleanup --- src/game/server/gamecontext.cpp | 2 ++ src/game/server/player.cpp | 14 +++++++------- src/game/server/score/sql_score.cpp | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index f78447fee..4e2b29110 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -901,6 +901,7 @@ void CGameContext::OnTick() } if(Collision()->m_NumSwitchers > 0) + { for (int i = 0; i < Collision()->m_NumSwitchers+1; ++i) { for (int j = 0; j < MAX_CLIENTS; ++j) @@ -919,6 +920,7 @@ void CGameContext::OnTick() } } } + } if(m_pRandomMapResult && m_pRandomMapResult->m_Done) { diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 47fa83646..433a82646 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -127,7 +127,7 @@ void CPlayer::Reset() // non-empty, allow them to vote immediately. This allows players to // vote after map changes or when they join an empty server. // - // Otherwise, block voting in the begnning after joining. + // Otherwise, block voting in the beginning after joining. if(Now > GameServer()->m_NonEmptySince + 10 * TickSpeed) m_FirstVoteTick = Now + g_Config.m_SvJoinVoteDelay * TickSpeed; else @@ -233,7 +233,7 @@ void CPlayer::Tick() int CurrentIndex = GameServer()->Collision()->GetMapIndex(m_ViewPos); m_TuneZone = GameServer()->Collision()->IsTune(CurrentIndex); - if (m_TuneZone != m_TuneZoneOld) // don't send tunigs all the time + if (m_TuneZone != m_TuneZoneOld) // don't send tunings all the time { GameServer()->SendTuningParams(m_ClientID, m_TuneZone); } @@ -258,11 +258,11 @@ void CPlayer::PostTick() void CPlayer::PostPostTick() { - #ifdef CONF_DEBUG - if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies) - #endif - if(!Server()->ClientIngame(m_ClientID)) - return; +#ifdef CONF_DEBUG + if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies) +#endif + if(!Server()->ClientIngame(m_ClientID)) + return; if(!GameServer()->m_World.m_Paused && !m_pCharacter && m_Spawning && m_WeakHookSpawn) TryRespawn(); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 6d425d691..6c3daa483 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -96,7 +96,7 @@ void CSqlScore::ExecSqlFunc(void *pUser) bool Success = false; try { - // try to connect to a working databaseserver + // try to connect to a working database server while (!Success && !connector.MaxTriesReached(pData->m_ReadOnly) && connector.ConnectSqlServer(pData->m_ReadOnly)) { try { @@ -106,7 +106,7 @@ void CSqlScore::ExecSqlFunc(void *pUser) dbg_msg("sql", "Unexpected exception caught"); } - // disconnect from databaseserver + // disconnect from database server connector.SqlServer()->Disconnect(); } From 7146eb572b8ba78e2cbc013442d8ac7cfaa26815 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 27 May 2020 20:09:12 +0200 Subject: [PATCH 02/44] Save error handling clean up --- src/game/server/save.cpp | 10 ++++++---- src/game/server/save.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index fec42a0c6..bd9993250 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -398,6 +398,8 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon { switch(Result) { + case 0: + return false; case 1: pGameContext->SendChatTarget(ClientID, "You have to be in a team (from 1-63)"); break; @@ -408,12 +410,12 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon pGameContext->SendChatTarget(ClientID, "Unable to find all Characters"); break; case 4: - pGameContext->SendChatTarget(ClientID, "Your team is not started yet"); + pGameContext->SendChatTarget(ClientID, "Your team has not started yet"); + break; + default: // this state should never be reached + pGameContext->SendChatTarget(ClientID, "Unknown error while saving"); break; - default: - return false; } - return true; } diff --git a/src/game/server/save.h b/src/game/server/save.h index 293b4d2a1..d6cab6816 100644 --- a/src/game/server/save.h +++ b/src/game/server/save.h @@ -100,6 +100,7 @@ public: int load(int Team); CSaveTee* m_pSavedTees; + // returns true if an error occured static bool HandleSaveError(int Result, int ClientID, CGameContext *pGameContext); private: int MatchPlayer(char name[16]); From add4fab6459e93d86a912132819a377a3ebb7552 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 27 May 2020 20:09:45 +0200 Subject: [PATCH 03/44] Disallow team join while save command is active --- src/game/server/teams.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index d31edc2ec..c5a0217cf 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -250,6 +250,10 @@ bool CGameTeams::SetCharacterTeam(int ClientID, int Team) if (m_Practice[m_Core.Team(ClientID)]) return false; + //you can not join a team which is currently in the process of saving, + //because the save-process can fail and then the team is reset into the game + if(Team != TEAM_SUPER && m_IsSaving[Team]) + return false; SetForceCharacterTeam(ClientID, Team); //GameServer()->CreatePlayerSpawn(Character(id)->m_Core.m_Pos, TeamMask()); From 8a22cce0313f690754594e5d4b0c892130b0f9e7 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Tue, 26 May 2020 23:22:16 +0200 Subject: [PATCH 04/44] Start of sql refractor * Use atomic integer in thread count for thread safety --- src/game/server/gamecontext.cpp | 4 +- src/game/server/player.cpp | 41 ++++ src/game/server/player.h | 8 + src/game/server/score.h | 27 +-- src/game/server/score/file_score.cpp | 9 +- src/game/server/score/file_score.h | 8 +- src/game/server/score/sql_score.cpp | 303 ++++++++++++++++----------- src/game/server/score/sql_score.h | 180 ++++++++-------- 8 files changed, 346 insertions(+), 234 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 4e2b29110..b02d8ff5c 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -922,6 +922,7 @@ void CGameContext::OnTick() } } + /* if(m_pRandomMapResult && m_pRandomMapResult->m_Done) { str_copy(g_Config.m_SvMap, m_pRandomMapResult->m_aMap, sizeof(g_Config.m_SvMap)); @@ -944,6 +945,7 @@ void CGameContext::OnTick() m_pMapVoteResult = NULL; } + */ #ifdef CONF_DEBUG if(g_Config.m_DbgDummies) @@ -2243,7 +2245,7 @@ void CGameContext::ConRandomMap(IConsole::IResult *pResult, void *pUserData) int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; - pSelf->m_pScore->RandomMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, Stars); + pSelf->m_pScore->RandomMap(pSelf->m_VoteCreator, Stars); } void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUserData) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 433a82646..8b6119f75 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -12,6 +12,10 @@ #include "gamemodes/DDRace.h" #include +#if defined(CONF_SQL) +#include "score/sql_score.h" +#endif + MACRO_ALLOC_POOL_ID_IMPL(CPlayer, MAX_CLIENTS) IServer *CPlayer::Server() const { return m_pGameServer->Server(); } @@ -119,6 +123,7 @@ void CPlayer::Reset() m_Last_Team = 0; #if defined(CONF_SQL) m_LastSQLQuery = 0; + m_SqlQueryResult = nullptr; #endif int64 Now = Server()->Tick(); @@ -782,3 +787,39 @@ void CPlayer::SpectatePlayerName(const char *pName) } } } + +#if defined(CONF_SQL) +void CPlayer::ProcessSqlResult() +{ + if(m_SqlQueryResult == nullptr || !m_SqlQueryResult->m_Done) + return; + if(m_SqlQueryResult->m_Message[0] != 0) + { + switch(m_SqlQueryResult->m_MessageTarget) + { + case CSqlResult::DIRECT: + GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_Message); + break; + case CSqlResult::TEAM: + if(m_SqlQueryResult->m_TeamMessageTo == -1) + break; + GameServer()->SendChatTeam(m_SqlQueryResult->m_TeamMessageTo, m_SqlQueryResult->m_Message); + break; + case CSqlResult::ALL: + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_Message); + break; + } + } + switch(m_SqlQueryResult->m_Tag) + { + case CSqlResult::NONE: + break; + case CSqlResult::LOAD: + break; + case CSqlResult::RANDOM_MAP: + break; + case CSqlResult::MAP_VOTE: + break; + } +} +#endif diff --git a/src/game/server/player.h b/src/game/server/player.h index ada68a22a..4e41b1274 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -5,7 +5,13 @@ // this include should perhaps be removed #include "entities/character.h" +#include "score.h" #include "gamecontext.h" +#include + +#if defined(CONF_SQL) +class CSqlResult; +#endif // player object class CPlayer @@ -194,7 +200,9 @@ public: bool m_Halloween; bool m_FirstPacket; #if defined(CONF_SQL) + void ProcessSqlResult(); int64 m_LastSQLQuery; + std::shared_ptr m_SqlQueryResult; #endif bool m_NotEligibleForFinish; int64 m_EligibleForFinishCheck; diff --git a/src/game/server/score.h b/src/game/server/score.h index 8943ffb17..a04b4a6a6 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -2,9 +2,9 @@ #define GAME_SERVER_SCORE_H #include +#include -#include "entities/character.h" -#include "gamecontext.h" +#include "save.h" enum { @@ -40,26 +40,15 @@ public: float m_aBestCpTime[NUM_CHECKPOINTS]; }; -// Watch this: TODO(2019-05-20): Temporary fix for the random maps race -// condition. See you in ten years. -class CRandomMapResult +struct CRandomMapResult { -public: - bool m_Done; char m_aMap[64]; - - CRandomMapResult() : m_Done(false) {} }; -class CMapVoteResult +struct CMapVoteResult { -public: - bool m_Done; char m_aMap[64]; char m_aServer[32]; - int m_ClientID; - - CMapVoteResult() : m_Done(false) {} }; class IScore @@ -79,16 +68,16 @@ public: virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0; - virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowRank(int ClientID, const char *pName, bool Search=false) = 0; - virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowTeamRank(int ClientID, const char *pName, bool Search=false) = 0; - virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowPoints(int ClientID, const char *pName, bool Search=false) = 0; - virtual void RandomMap(std::shared_ptr *ppResult, int ClientID, int Stars) = 0; + virtual void RandomMap(int ClientID, int Stars) = 0; virtual void RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int Stars) = 0; virtual void SaveTeam(int Team, const char *pCode, int ClientID, const char *pServer) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 798dc2473..fa1d5c543 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -241,7 +241,7 @@ void CFileScore::SaveScore(int ClientID, float Time, const char *pTimestamp, UpdatePlayer(ClientID, Time, CpTime); } -void CFileScore::ShowTop5(IConsole::IResult *pResult, int ClientID, +void CFileScore::ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut) { CGameContext *pSelf = (CGameContext *) pUserData; @@ -300,7 +300,7 @@ void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +void CFileScore::ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); @@ -314,7 +314,7 @@ void CFileScore::ShowTeamRank(int ClientID, const char* pName, bool Search) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +void CFileScore::ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); @@ -328,12 +328,11 @@ void CFileScore::ShowPoints(int ClientID, const char* pName, bool Search) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::RandomMap(std::shared_ptr *ppResult, int ClientID, int stars) +void CFileScore::RandomMap(int ClientID, int stars) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Random map not supported in file based servers"); GameServer()->SendChatTarget(ClientID, aBuf); - *ppResult = NULL; } void CFileScore::RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int stars) diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index ceeecb88c..b45c9d37e 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -71,17 +71,17 @@ public: float CpTime[NUM_CHECKPOINTS], bool NotEligible); virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp); - virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, + virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void ShowRank(int ClientID, const char* pName, bool Search = false); - virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, + virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); - virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut); + virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut); virtual void ShowPoints(int ClientID, const char* pName, bool Search); - virtual void RandomMap(std::shared_ptr *ppResult, int ClientID, int stars); + virtual void RandomMap(int ClientID, int stars); virtual void RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int stars); virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 6c3daa483..d9fb362a9 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -2,6 +2,8 @@ /* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */ /* CSqlScore class by Sushi */ #if defined(CONF_SQL) +#include "sql_score.h" + #include #include @@ -9,66 +11,51 @@ #include #include -#include "sql_score.h" - #include "../entities/character.h" #include "../gamemodes/DDRace.h" #include "../save.h" -CGameContext* CSqlData::ms_pGameServer = 0; -IServer* CSqlData::ms_pServer = 0; -CPlayerData* CSqlData::ms_pPlayerData = 0; -const char* CSqlData::ms_pMap = 0; -const char* CSqlData::ms_pGameUuid = 0; +std::atomic_int CSqlExecData::ms_InstanceCount(0); -bool CSqlData::ms_GameContextAvailable = false; -int CSqlData::ms_Instance = 0; - -volatile int CSqlExecData::ms_InstanceCount = 0; +CSqlResult::CSqlResult() : + m_Done(false), + m_MessageTarget(DIRECT), + m_TeamMessageTo(-1), + m_Tag(NONE) +{ + m_Message[0] = 0; +} +CSqlResult::~CSqlResult() { + switch(m_Tag) + { + case NONE: + case MESSAGES: + break; + case LOAD: + //m_Variant.m_LoadTeam.~CSaveTeam(); + break; + case RANDOM_MAP: + m_Variant.m_RandomMap.~CRandomMapResult(); + break; + case MAP_VOTE: + m_Variant.m_MapVote.~CMapVoteResult(); + break; + } +} LOCK CSqlScore::ms_FailureFileLock = lock_create(); -CSqlTeamSave::~CSqlTeamSave() -{ - try - { - ((class CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.SetSaving(m_Team, false); - } - catch (CGameContextError& e) {} -} - - - CSqlScore::CSqlScore(CGameContext *pGameServer) : -m_pGameServer(pGameServer), -m_pServer(pGameServer->Server()) + m_pGameServer(pGameServer), + m_pServer(pGameServer->Server()) { - str_copy(m_aMap, g_Config.m_SvMap, sizeof(m_aMap)); - FormatUuid(m_pGameServer->GameUuid(), m_aGameUuid, sizeof(m_aGameUuid)); - - CSqlData::ms_pGameServer = m_pGameServer; - CSqlData::ms_pServer = m_pServer; - CSqlData::ms_pPlayerData = PlayerData(0); - CSqlData::ms_pMap = m_aMap; - CSqlData::ms_pGameUuid = m_aGameUuid; - - CSqlData::ms_GameContextAvailable = true; - ++CSqlData::ms_Instance; - CSqlConnector::ResetReachable(); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(Init, new CSqlData()), "SqlScore constructor"); -} - - -CSqlScore::~CSqlScore() -{ - CSqlData::ms_GameContextAvailable = false; + thread_init_and_detach(ExecSqlFunc, new CSqlExecData(Init, new CSqlData(nullptr)), "SqlScore constructor"); } void CSqlScore::OnShutdown() { - CSqlData::ms_GameContextAvailable = false; int i = 0; while (CSqlExecData::ms_InstanceCount != 0) { @@ -79,7 +66,7 @@ void CSqlScore::OnShutdown() // print a log about every two seconds if (i % 20 == 0) - dbg_msg("sql", "Waiting for score-threads to complete (%d left)", CSqlExecData::ms_InstanceCount); + dbg_msg("sql", "Waiting for score-threads to complete (%d left)", CSqlExecData::ms_InstanceCount.load()); ++i; thread_sleep(100000); } @@ -124,6 +111,7 @@ void CSqlScore::ExecSqlFunc(void *pUser) bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlData* pData = pGameData; if (HandleFailure) @@ -152,24 +140,24 @@ bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool Han dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Tables were NOT created"); } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Setting best time failed (game reloaded)."); - } + */ return false; } void CSqlScore::CheckBirthday(int ClientID) { + /* CSqlPlayerData *Tmp = new CSqlPlayerData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = Server()->ClientName(ClientID); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(CheckBirthdayThread, Tmp), "birthday check"); + */ } bool CSqlScore::CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* TODO const CSqlPlayerData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -177,6 +165,7 @@ bool CSqlScore::CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGam try { + char aBuf[512]; str_format(aBuf, sizeof(aBuf), "select year(Current) - year(Stamp) as YearsAgo from (select CURRENT_TIMESTAMP as Current, min(Timestamp) as Stamp from %s_race WHERE Name='%s') as l where dayofmonth(Current) = dayofmonth(Stamp) and month(Current) = month(Stamp) and year(Current) > year(Stamp);", pSqlServer->GetPrefix(), pData->m_Name.ClrStr()); @@ -201,27 +190,26 @@ bool CSqlScore::CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGam dbg_msg("sql", "MySQL ERROR: %s", e.what()); dbg_msg("sql", "ERROR: could not check birthday"); } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted checking ddnet-birthday due to reload/change of map."); - return true; - } - + return false; + */ return false; } void CSqlScore::LoadScore(int ClientID) { + /* CSqlPlayerData *Tmp = new CSqlPlayerData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = Server()->ClientName(ClientID); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(LoadScoreThread, Tmp), "load score"); + */ } // update stuff bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlPlayerData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -270,10 +258,13 @@ bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameDat return true; } return false; + */ + return false; } void CSqlScore::MapVote(std::shared_ptr *ppResult, int ClientID, const char* MapName) { + /* *ppResult = std::make_shared(); CSqlMapVoteData *Tmp = new CSqlMapVoteData(); @@ -285,10 +276,12 @@ void CSqlScore::MapVote(std::shared_ptr *ppResult, int ClientID, sqlstr::FuzzyString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(MapVoteThread, Tmp), "map vote"); + */ } bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlMapVoteData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -360,10 +353,13 @@ bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, return true; } return false; + */ + return false; } void CSqlScore::MapInfo(int ClientID, const char* MapName) { + /* CSqlMapData *Tmp = new CSqlMapData(); Tmp->m_ClientID = ClientID; Tmp->m_RequestedMap = MapName; @@ -373,10 +369,12 @@ void CSqlScore::MapInfo(int ClientID, const char* MapName) sqlstr::FuzzyString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(MapInfoThread, Tmp), "map info"); + */ } bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlMapData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -459,10 +457,13 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, return true; } return false; + */ + return false; } void CSqlScore::SaveScore(int ClientID, float Time, const char *pTimestamp, float CpTime[NUM_CHECKPOINTS], bool NotEligible) { + /* CConsole* pCon = (CConsole*)GameServer()->Console(); if(pCon->m_Cheated) return; @@ -476,10 +477,12 @@ void CSqlScore::SaveScore(int ClientID, float Time, const char *pTimestamp, floa Tmp->m_aCpCurrent[i] = CpTime[i]; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(SaveScoreThread, Tmp, false), "save score"); + */ } bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -559,10 +562,13 @@ bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameDat dbg_msg("sql", "ERROR: Could not update time"); } return false; + */ + return false; } void CSqlScore::SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, const char *pTimestamp) { + /* CConsole* pCon = (CConsole*)GameServer()->Console(); if(pCon->m_Cheated) return; @@ -579,10 +585,12 @@ void CSqlScore::SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, co str_copy(Tmp->m_aTimestamp, pTimestamp, sizeof(Tmp->m_aTimestamp)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(SaveTeamScoreThread, Tmp, false), "save team score"); + */ } bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlTeamScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -717,10 +725,13 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGam dbg_msg("sql", "ERROR: Could not update time"); } return false; + */ + return false; } void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; @@ -728,10 +739,12 @@ void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowRankThread, Tmp), "show rank"); + */ } bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -786,10 +799,13 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData return true; } return false; + */ + return false; } void CSqlScore::ShowTeamRank(int ClientID, const char* pName, bool Search) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; @@ -797,10 +813,12 @@ void CSqlScore::ShowTeamRank(int ClientID, const char* pName, bool Search) str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTeamRankThread, Tmp), "show team rank"); + */ } bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -873,19 +891,24 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGame return true; } return false; + */ + return false; } -void CSqlScore::ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTop5Thread, Tmp), "show top5"); + */ } bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -933,19 +956,24 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData return true; } return false; + */ + return false; } -void CSqlScore::ShowTeamTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTeamTop5Thread, Tmp), "show team top5"); + */ } bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1039,21 +1067,25 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGame dbg_msg("sql", "WARNING: Aborted showing teamtop5 due to reload/change of map."); return true; } + */ return false; } void CSqlScore::ShowTimes(int ClientID, int Debut) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; Tmp->m_Search = false; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTimesThread, Tmp), "show times"); + */ } void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; @@ -1061,10 +1093,12 @@ void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut) Tmp->m_Search = true; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTimesThread, Tmp), "show name's times"); + */ } bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1138,10 +1172,13 @@ bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameDat dbg_msg("sql", "WARNING: Aborted showing times due to reload/change of map."); return true; } + */ + return false; } void CSqlScore::ShowPoints(int ClientID, const char* pName, bool Search) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; @@ -1149,10 +1186,12 @@ void CSqlScore::ShowPoints(int ClientID, const char* pName, bool Search) str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowPointsThread, Tmp), "show points"); + */ } bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1195,20 +1234,24 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameDa dbg_msg("sql", "WARNING: Aborted showing points due to reload/change of map."); return true; } + */ return false; } -void CSqlScore::ShowTopPoints(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut) { + /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTopPointsThread, Tmp), "show top points"); + */ } bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlScoreData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1251,18 +1294,18 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGam return true; } + */ return false; } -void CSqlScore::RandomMap(std::shared_ptr *ppResult, int ClientID, int Stars) +void CSqlScore::RandomMap(int ClientID, int Stars) { - *ppResult = std::make_shared(); + CSqlRandomMap *Tmp = new CSqlRandomMap(nullptr); + Tmp->m_Stars = Stars; - CSqlRandomMap *Tmp = new CSqlRandomMap(); - Tmp->m_Num = Stars; - Tmp->m_ClientID = ClientID; - Tmp->m_Name = GameServer()->Server()->ClientName(ClientID); - Tmp->m_pResult = *ppResult; + // TODO: Set Client result Tmp->m_ClientID = ClientID; + strncpy(Tmp->m_aCurrentMap, m_aMap, sizeof(Tmp->m_aCurrentMap)); + strncpy(Tmp->m_aServerType, g_Config.m_SvServerType, sizeof(Tmp->m_aServerType)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(RandomMapThread, Tmp), "random map"); } @@ -1277,26 +1320,47 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameDat try { char aBuf[512]; - if(pData->m_Num >= 0) - str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" and Stars = \"%d\" order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap, pData->m_Num); + if(0 <= pData->m_Stars && pData->m_Stars <= 5) + { + str_format(aBuf, sizeof(aBuf), + "SELECT * FROM %s_maps " + "WHERE Server = \"%s\" AND Map != \"%s\" AND Stars = \"%d\" " + "ORDER BY RAND() LIMIT 1;", + pSqlServer->GetPrefix(), + pData->m_aServerType, + pData->m_aCurrentMap, + pData->m_Stars + ); + } else - str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap); + { + str_format(aBuf, sizeof(aBuf), + "SELECT * FROM %s_maps " + "WHERE Server = \"%s\" AND Map != \"%s\" " + "ORDER BY RAND() LIMIT 1;", + pSqlServer->GetPrefix(), + pData->m_aServerType, + pData->m_aCurrentMap + ); + } pSqlServer->executeSqlQuery(aBuf); + pData->m_pSqlResult->m_Tag = CSqlResult::RANDOM_MAP; if(pSqlServer->GetResults()->rowsCount() != 1) { - pData->GameServer()->SendChatTarget(pData->m_ClientID, "No maps found on this server!"); - pData->GameServer()->m_LastMapVote = 0; + pData->m_pSqlResult->m_MessageTarget = CSqlResult::DIRECT; + strncpy(pData->m_pSqlResult->m_Message, "No maps found on this server!", sizeof(pData->m_pSqlResult->m_Message)); + pData->m_pSqlResult->m_Variant.m_RandomMap.m_aMap[0] = 0; } else { pSqlServer->GetResults()->next(); std::string Map = pSqlServer->GetResults()->getString("Map"); - str_copy(pData->m_pResult->m_aMap, Map.c_str(), sizeof(pData->m_pResult->m_aMap)); - pData->m_pResult->m_Done = true; + str_copy(pData->m_pSqlResult->m_Variant.m_RandomMap.m_aMap, Map.c_str(), sizeof(pData->m_pSqlResult->m_Variant.m_RandomMap.m_aMap)); } dbg_msg("sql", "voting random map done"); + pData->m_pSqlResult->m_Done = true; return true; } catch (sql::SQLException &e) @@ -1304,16 +1368,12 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameDat dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Could not vote random map"); } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted random-map-thread due to reload/change of map."); - return true; - } return false; } void CSqlScore::RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int Stars) { + /* *ppResult = std::make_shared(); CSqlRandomMap *Tmp = new CSqlRandomMap(); @@ -1323,10 +1383,12 @@ void CSqlScore::RandomUnfinishedMap(std::shared_ptr *ppResult, Tmp->m_pResult = *ppResult; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(RandomUnfinishedMapThread, Tmp), "random unfinished map"); + */ } bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlRandomMap *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1368,63 +1430,46 @@ bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData return true; } return false; + */ + return false; } void CSqlScore::SaveTeam(int Team, const char* Code, int ClientID, const char* Server) { - if((g_Config.m_SvTeam == 3 || (Team > 0 && Team < MAX_CLIENTS)) && ((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.Count(Team) > 0) - { - if(((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.GetSaving(Team)) - return; - ((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.SetSaving(Team, true); - } - else - { - GameServer()->SendChatTarget(ClientID, "You have to be in a team (from 1-63)"); + /* + if(((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.GetSaving(Team)) return; - } + + CSaveTeam SavedTeam(GameServer()->m_pController); + int Result = SavedTeam.save(Team); + if(CSaveTeam::HandleSaveError(Result, ClientID, GameServer())) + return; + // disable joining team while saving + ((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.SetSaving(Team, true); CSqlTeamSave *Tmp = new CSqlTeamSave(); Tmp->m_Team = Team; Tmp->m_ClientID = ClientID; + // copy both save code and save state into the thread struct Tmp->m_Code = Code; + Tmp->m_SaveState = SavedTeam.GetString(); str_copy(Tmp->m_Server, Server, sizeof(Tmp->m_Server)); str_copy(Tmp->m_ClientName, this->Server()->ClientName(Tmp->m_ClientID), sizeof(Tmp->m_ClientName)); + // TODO: log event in Teehistorian + // TODO: find a way to send all players in the team the save code + ((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.KillSavedTeam(Team); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(SaveTeamThread, Tmp, false), "save team"); + */ } bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlTeamSave *pData = dynamic_cast(pGameData); try { - int Team = pData->m_Team; - - char TeamString[65536]; - - int Num = -1; - - if((g_Config.m_SvTeam == 3 || (Team > 0 && Team < MAX_CLIENTS)) && ((CGameControllerDDRace*)(pData->GameServer()->m_pController))->m_Teams.Count(Team) > 0) - { - CSaveTeam SavedTeam(pData->GameServer()->m_pController); - Num = SavedTeam.save(Team); - if(CSaveTeam::HandleSaveError(Num, pData->m_ClientID, pData->GameServer())) - return true; - - if(!Num) - { - str_copy(TeamString, SavedTeam.GetString(), sizeof(TeamString)); - sqlstr::ClearString(TeamString, sizeof(TeamString)); - } - } - else - pData->GameServer()->SendChatTarget(pData->m_ClientID, "You have to be in a team (from 1-63)"); - - if (Num) - return true; - if (HandleFailure) { if (!g_Config.m_SvSqlFailureFile[0]) @@ -1437,7 +1482,11 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData dbg_msg("sql", "ERROR: Could not save Teamsave, writing insert to a file now..."); char aBuf[65536]; - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %%s_saves(Savegame, Map, Code, Timestamp, Server, DDNet7) VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', false);", TeamString, pData->m_Map.ClrStr(), pData->m_Code.ClrStr(), pData->m_Server); + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %%s_saves(Savegame, Map, Code, Timestamp, Server, DDNet7) " + "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', false);", + pData->m_SaveState, pData->m_Map.ClrStr(), pData->m_Code.ClrStr(), pData->m_Server + ); io_write(File, aBuf, str_length(aBuf)); io_write_newline(File); io_close(File); @@ -1463,7 +1512,10 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData if (pSqlServer->GetResults()->rowsCount() == 0) { char aBuf[65536]; - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, DDNet7) VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', false)", pSqlServer->GetPrefix(), TeamString, pData->m_Map.ClrStr(), pData->m_Code.ClrStr(), pData->m_Server); + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, DDNet7) VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', false)", + pSqlServer->GetPrefix(), pData->m_SaveState.ClrStr(), pData->m_Map.ClrStr(), pData->m_Code.ClrStr(), pData->m_Server + ); dbg_msg("sql", "%s", aBuf); pSqlServer->executeSql(aBuf); @@ -1472,7 +1524,6 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData char aBuf2[512]; str_format(aBuf2, sizeof(aBuf2), "Team successfully saved by %s. Use '/load %s' to continue", pData->m_ClientName, pData->m_Code.Str()); pData->GameServer()->SendChatTeam(Team, aBuf2); - ((CGameControllerDDRace*)(pData->GameServer()->m_pController))->m_Teams.KillSavedTeam(Team); } else { @@ -1501,20 +1552,25 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData pSqlServer->executeSql("unlock tables;"); return true; + */ + return false; } void CSqlScore::LoadTeam(const char* Code, int ClientID) { + /* CSqlTeamLoad *Tmp = new CSqlTeamLoad(); Tmp->m_Code = Code; Tmp->m_ClientID = ClientID; str_copy(Tmp->m_ClientName, Server()->ClientName(Tmp->m_ClientID), sizeof(Tmp->m_ClientName)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(LoadTeamThread, Tmp), "load team"); + */ } bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlTeamLoad *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1633,19 +1689,24 @@ bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData return true; } return false; + */ + return false; } void CSqlScore::GetSaves(int ClientID) { + /* CSqlGetSavesData *Tmp = new CSqlGetSavesData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = Server()->ClientName(ClientID); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(GetSavesThread, Tmp, false), "get saves"); + */ } bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlGetSavesData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1670,8 +1731,9 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData str_format(aLastSavedString, sizeof(aLastSavedString), ", last saved %s ago", aAgoString); } - str_format(aBuf, sizeof(aBuf), "%s has %d save%s on %s%s", pData->m_Name.Str(), NumSaves, NumSaves == 1 ? "" : "s", pData->m_Map.Str(), aLastSavedString); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + str_format(pData->m_pSqlResult->m_Message, + sizeof(pData->m_pSqlResult->m_Message), + "%s has %d save%s on %s%s", pData->m_Name.Str(), NumSaves, NumSaves == 1 ? "" : "s", pData->m_Map.Str(), aLastSavedString); } dbg_msg("sql", "Showing saves done"); @@ -1683,11 +1745,8 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData dbg_msg("sql", "ERROR: Could not get saves"); pData->GameServer()->SendChatTarget(pData->m_ClientID, "MySQL Error: Could not get saves"); } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted getting saves due to reload/change of map."); - return true; - } + return false; + */ return false; } diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 2f0d98fd5..4a0c4bc95 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -1,82 +1,62 @@ /* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */ /* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */ /* CSqlScore Class by Sushi Tee*/ -#ifndef GAME_SERVER_SCORE_SQL_SCORE_H -#define GAME_SERVER_SCORE_SQL_SCORE_H +#ifndef GAME_SERVER_SCORE_SQL_H +#define GAME_SERVER_SCORE_SQL_H #include #include -#include #include #include +#include -#include "../score.h" +#include - -class CGameContextError : public std::runtime_error +// result only valid if m_Done is set to true +class CSqlResult { public: - CGameContextError(const char* pMsg) : std::runtime_error(pMsg) {} + std::atomic_bool m_Done; + // specify where chat messages should be returned + enum + { + DIRECT, + TEAM, + ALL, + } m_MessageTarget; + int m_TeamMessageTo; // store team id, if player changes team after /save + char m_Message[512]; + // TODO: replace this with a type-safe std::variant (C++17) + enum + { + NONE, + LOAD, + RANDOM_MAP, + MAP_VOTE, + MESSAGES, // relevant for TOP5 + } m_Tag; + + union + { + //CSaveTeam m_LoadTeam; + CRandomMapResult m_RandomMap; + CMapVoteResult m_MapVote; + char m_Messages[512][8]; // Space for extra messages + } m_Variant; + + CSqlResult(); + ~CSqlResult(); }; - -// generic implementation to provide gameserver and server +// holding relevant data for one thread, and function pointer for return values struct CSqlData { - CSqlData() : m_Map(ms_pMap), m_GameUuid(ms_pGameUuid) - { - m_Instance = ms_Instance; - } - - virtual ~CSqlData() {} - - bool isGameContextVaild() const - { - return m_Instance == ms_Instance && ms_GameContextAvailable; - } - - CGameContext* GameServer() const { return isGameContextVaild() ? ms_pGameServer : throw CGameContextError("[CSqlData]: GameServer() unavailable."); } - IServer* Server() const { return isGameContextVaild() ? ms_pServer : throw CGameContextError("[CSqlData]: Server() unavailable."); } - CPlayerData* PlayerData(int ID) const { return isGameContextVaild() ? &ms_pPlayerData[ID] : throw CGameContextError("[CSqlData]: PlayerData() unavailable."); } - - sqlstr::CSqlString<128> m_Map; - sqlstr::CSqlString m_GameUuid; - - // counter to keep track to which instance of GameServer this object belongs to. - int m_Instance; - - static CGameContext *ms_pGameServer; - static IServer *ms_pServer; - static CPlayerData *ms_pPlayerData; - static const char *ms_pMap; - static const char *ms_pGameUuid; - - static bool ms_GameContextAvailable; - // contains the instancecount of the current GameServer - static int ms_Instance; -}; - -struct CSqlExecData -{ - CSqlExecData(bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), CSqlData *pSqlData, bool ReadOnly = true) : - m_pFuncPtr(pFuncPtr), - m_pSqlData(pSqlData), - m_ReadOnly(ReadOnly) - { - ++ms_InstanceCount; - } - ~CSqlExecData() - { - --ms_InstanceCount; - } - - bool (*m_pFuncPtr) (CSqlServer*, const CSqlData *, bool); - CSqlData *m_pSqlData; - bool m_ReadOnly; - - // keeps track of score-threads - volatile static int ms_InstanceCount; + CSqlData(std::shared_ptr pSqlResult) : + m_pSqlResult(pSqlResult) + { } + std::shared_ptr m_pSqlResult; + virtual ~CSqlData() = default; }; struct CSqlPlayerData : CSqlData @@ -130,10 +110,11 @@ struct CSqlTeamSave : CSqlData { virtual ~CSqlTeamSave(); - int m_Team; - int m_ClientID; char m_ClientName[MAX_NAME_LENGTH]; + CUuid m_SaveUuid; + sqlstr::CSqlString<128> m_Code; + sqlstr::CSqlString<65536> m_SaveState; char m_Server[5]; }; @@ -146,32 +127,55 @@ struct CSqlTeamLoad : CSqlData struct CSqlGetSavesData: CSqlData { - int m_ClientID; sqlstr::CSqlString m_Name; }; -struct CSqlRandomMap : CSqlScoreData +struct CSqlRandomMap : CSqlData { - std::shared_ptr m_pResult; + using CSqlData::CSqlData; + int m_Stars; + char m_aCurrentMap[64]; + char m_aServerType[64]; }; +// controls one thread +struct CSqlExecData +{ + CSqlExecData( + bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), + CSqlData *pSqlResult, + bool ReadOnly = true + ) : + m_pFuncPtr(pFuncPtr), + m_pSqlData(pSqlResult), + m_ReadOnly(ReadOnly) + { + ++ms_InstanceCount; + } + ~CSqlExecData() + { + --ms_InstanceCount; + } + + bool (*m_pFuncPtr) (CSqlServer*, const CSqlData *, bool); + CSqlData *m_pSqlData; + bool m_ReadOnly; + + // keeps track of score-threads + static std::atomic_int ms_InstanceCount; +}; + +class IServer; +class CGameContext; + class CSqlScore: public IScore { - CGameContext *GameServer() { return m_pGameServer; } - IServer *Server() { return m_pServer; } - - CGameContext *m_pGameServer; - IServer *m_pServer; + static LOCK ms_FailureFileLock; static void ExecSqlFunc(void *pUser); static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); - char m_aMap[64]; - char m_aGameUuid[UUID_MAXSTRSIZE]; - - static LOCK ms_FailureFileLock; - static bool CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -191,10 +195,20 @@ class CSqlScore: public IScore static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + CGameContext *GameServer() { return m_pGameServer; } + IServer *Server() { return m_pServer; } + + CGameContext *m_pGameServer; + IServer *m_pServer; + + + char m_aMap[64]; + char m_aGameUuid[UUID_MAXSTRSIZE]; + public: CSqlScore(CGameContext *pGameServer); - ~CSqlScore(); + ~CSqlScore() {} virtual void CheckBirthday(int ClientID); virtual void LoadScore(int ClientID); @@ -207,14 +221,14 @@ public: virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); virtual void ShowTimes(int ClientID, int Debut = 1); - virtual void ShowTop5(IConsole::IResult *pResult, int ClientID, + virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void ShowTeamTop5(IConsole::IResult *pResult, int ClientID, + virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void ShowPoints(int ClientID, const char* pName, bool Search = false); - virtual void ShowTopPoints(IConsole::IResult *pResult, int ClientID, + virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void RandomMap(std::shared_ptr *ppResult, int ClientID, int stars); + virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int stars); virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); @@ -223,4 +237,4 @@ public: virtual void OnShutdown(); }; -#endif // GAME_SERVER_SCORE_SQL_SCORE_H +#endif // GAME_SERVER_SCORE_SQL_H From 0891a0fd51e893afd3b4890f9c91bca05b2cbc2b Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 13:29:42 +0200 Subject: [PATCH 05/44] Remove some unnecessary header includes --- src/game/server/save.cpp | 5 +++-- src/game/server/save.h | 5 +++-- src/game/server/score/sql_score.cpp | 1 + src/game/server/score/sql_score.h | 8 +++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index bd9993250..601a1e1ed 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -1,9 +1,10 @@ +#include "save.h" + #include #include -#include "save.h" #include "teams.h" -#include "./gamemodes/DDRace.h" +#include "gamemodes/DDRace.h" #include CSaveTee::CSaveTee() diff --git a/src/game/server/save.h b/src/game/server/save.h index d6cab6816..08cb62571 100644 --- a/src/game/server/save.h +++ b/src/game/server/save.h @@ -1,8 +1,9 @@ #ifndef GAME_SERVER_SAVE_H #define GAME_SERVER_SAVE_H -#include "./entities/character.h" -#include +#include "entities/character.h" +class IGameController; +class CGameContext; class CSaveTee { diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index d9fb362a9..f52bf105c 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 4a0c4bc95..b3af793cf 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -4,14 +4,12 @@ #ifndef GAME_SERVER_SCORE_SQL_H #define GAME_SERVER_SCORE_SQL_H -#include - -#include -#include #include #include -#include +#include "../score.h" + +class CSqlServer; // result only valid if m_Done is set to true class CSqlResult From 3af18f5f6017d70c402c1981be25bd51b3ede3e5 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 14:09:06 +0200 Subject: [PATCH 06/44] Create shared pointer on demand --- src/game/server/score/sql_score.cpp | 14 +++++++++++++- src/game/server/score/sql_score.h | 3 +++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index f52bf105c..c46bbd4d0 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -44,6 +44,15 @@ CSqlResult::~CSqlResult() { } } +std::shared_ptr CSqlScore::NewSqlResult(int ClientID) +{ + CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientID]; + if(pCurPlayer->m_SqlQueryResult != nullptr) + return nullptr; + pCurPlayer->m_SqlQueryResult = std::make_shared(); + return pCurPlayer->m_SqlQueryResult; +} + LOCK CSqlScore::ms_FailureFileLock = lock_create(); CSqlScore::CSqlScore(CGameContext *pGameServer) : @@ -1301,7 +1310,10 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGam void CSqlScore::RandomMap(int ClientID, int Stars) { - CSqlRandomMap *Tmp = new CSqlRandomMap(nullptr); + std::shared_ptr pResult = NewSqlResult(ClientID); + if(pResult == nullptr) + return; + CSqlRandomMap *Tmp = new CSqlRandomMap(pResult); Tmp->m_Stars = Stars; // TODO: Set Client result Tmp->m_ClientID = ClientID; diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index b3af793cf..f5bf5365f 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -203,6 +203,9 @@ class CSqlScore: public IScore char m_aMap[64]; char m_aGameUuid[UUID_MAXSTRSIZE]; + // returns new SqlResult bound to the player, if no current Thread is active for this player + std::shared_ptr NewSqlResult(int ClientID); + public: CSqlScore(CGameContext *pGameServer); From a990acc512209b66691c549948c008c450aa72d9 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 15:29:14 +0200 Subject: [PATCH 07/44] Remove existing CRandomMapResult to simplify IScore interface --- src/game/server/ddracechat.cpp | 2 +- src/game/server/gamecontext.cpp | 5 +---- src/game/server/gamecontext.h | 5 ----- src/game/server/score.h | 4 ++-- src/game/server/score/file_score.cpp | 5 ++--- src/game/server/score/file_score.h | 4 ++-- src/game/server/score/sql_score.cpp | 8 ++++---- src/game/server/score/sql_score.h | 17 +++++++++++------ 8 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 7be20d337..e76715bcd 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -526,7 +526,7 @@ void CGameContext::ConMap(IConsole::IResult *pResult, void *pUserData) return; #endif - pSelf->Score()->MapVote(&pSelf->m_pMapVoteResult, pResult->m_ClientID, pResult->GetString(0)); + pSelf->Score()->MapVote(pResult->m_ClientID, pResult->GetString(0)); #if defined(CONF_SQL) if(g_Config.m_SvUseSQL) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index b02d8ff5c..b32e813b7 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -57,9 +57,6 @@ void CGameContext::Construct(int Resetting) m_ChatResponseTargetID = -1; m_aDeleteTempfile[0] = 0; m_TeeHistorianActive = false; - - m_pRandomMapResult = nullptr; - m_pMapVoteResult = nullptr; } CGameContext::CGameContext(int Resetting) @@ -2254,7 +2251,7 @@ void CGameContext::ConRandomUnfinishedMap(IConsole::IResult *pResult, void *pUse int Stars = pResult->NumArguments() ? pResult->GetInteger(0) : -1; - pSelf->m_pScore->RandomUnfinishedMap(&pSelf->m_pRandomMapResult, pSelf->m_VoteCreator, Stars); + pSelf->m_pScore->RandomUnfinishedMap(pSelf->m_VoteCreator, Stars); } void CGameContext::ConRestart(IConsole::IResult *pResult, void *pUserData) diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 061344005..4a6d04299 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -57,8 +57,6 @@ enum class IConsole; class IEngine; class IStorage; -class CRandomMapResult; -class CMapVoteResult; struct CAntibotData; class CGameContext : public IGameServer @@ -81,9 +79,6 @@ class CGameContext : public IGameServer CMapBugs m_MapBugs; CPrng m_Prng; - std::shared_ptr m_pRandomMapResult; - std::shared_ptr m_pMapVoteResult; - static void CommandCallback(int ClientID, int FlagMask, const char *pCmd, IConsole::IResult *pResult, void *pUser); static void TeeHistorianWrite(const void *pData, int DataSize, void *pUser); diff --git a/src/game/server/score.h b/src/game/server/score.h index a04b4a6a6..a2e3a88c8 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -61,7 +61,7 @@ public: CPlayerData *PlayerData(int ID) { return &m_aPlayerData[ID]; } virtual void MapInfo(int ClientID, const char *pMapName) = 0; - virtual void MapVote(std::shared_ptr *ppResult, int ClientID, const char *pMapName) = 0; + virtual void MapVote(int ClientID, const char *pMapName) = 0; virtual void CheckBirthday(int ClientID) = 0; virtual void LoadScore(int ClientID) = 0; virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, float aCpTime[NUM_CHECKPOINTS], bool NotEligible) = 0; @@ -78,7 +78,7 @@ public: virtual void ShowPoints(int ClientID, const char *pName, bool Search=false) = 0; virtual void RandomMap(int ClientID, int Stars) = 0; - virtual void RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int Stars) = 0; + virtual void RandomUnfinishedMap(int ClientID, int Stars) = 0; virtual void SaveTeam(int Team, const char *pCode, int ClientID, const char *pServer) = 0; virtual void LoadTeam(const char *pCode, int ClientID) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index fa1d5c543..f28d354a6 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -58,7 +58,7 @@ void CFileScore::MapInfo(int ClientID, const char* MapName) // TODO: implement } -void CFileScore::MapVote(std::shared_ptr *ppResult, int ClientID, const char* MapName) +void CFileScore::MapVote(int ClientID, const char* MapName) { // TODO: implement } @@ -335,12 +335,11 @@ void CFileScore::RandomMap(int ClientID, int stars) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int stars) +void CFileScore::RandomUnfinishedMap(int ClientID, int stars) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Random unfinished map not supported in file based servers"); GameServer()->SendChatTarget(ClientID, aBuf); - *ppResult = NULL; } void CFileScore::SaveTeam(int Team, const char* Code, int ClientID, const char* Server) diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index b45c9d37e..59c4d11f3 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -66,7 +66,7 @@ public: virtual void CheckBirthday(int ClientID); virtual void LoadScore(int ClientID); virtual void MapInfo(int ClientID, const char* MapName); - virtual void MapVote(std::shared_ptr *ppResult, int ClientID, const char* MapName); + virtual void MapVote(int ClientID, const char* MapName); virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, float CpTime[NUM_CHECKPOINTS], bool NotEligible); virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp); @@ -82,7 +82,7 @@ public: virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut); virtual void ShowPoints(int ClientID, const char* pName, bool Search); virtual void RandomMap(int ClientID, int stars); - virtual void RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int stars); + virtual void RandomUnfinishedMap(int ClientID, int stars); virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); virtual void GetSaves(int ClientID); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index c46bbd4d0..5d1ffacae 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -47,7 +47,7 @@ CSqlResult::~CSqlResult() { std::shared_ptr CSqlScore::NewSqlResult(int ClientID) { CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientID]; - if(pCurPlayer->m_SqlQueryResult != nullptr) + if(pCurPlayer->m_SqlQueryResult != nullptr) // TODO: send player a message: "too many requests" return nullptr; pCurPlayer->m_SqlQueryResult = std::make_shared(); return pCurPlayer->m_SqlQueryResult; @@ -272,7 +272,7 @@ bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameDat return false; } -void CSqlScore::MapVote(std::shared_ptr *ppResult, int ClientID, const char* MapName) +void CSqlScore::MapVote(int ClientID, const char* MapName) { /* *ppResult = std::make_shared(); @@ -1310,7 +1310,7 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGam void CSqlScore::RandomMap(int ClientID, int Stars) { - std::shared_ptr pResult = NewSqlResult(ClientID); + auto pResult = NewSqlResult(ClientID); if(pResult == nullptr) return; CSqlRandomMap *Tmp = new CSqlRandomMap(pResult); @@ -1384,7 +1384,7 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameDat return false; } -void CSqlScore::RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int Stars) +void CSqlScore::RandomUnfinishedMap(int ClientID, int Stars) { /* *ppResult = std::make_shared(); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index f5bf5365f..535c51ce7 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -211,13 +211,15 @@ public: CSqlScore(CGameContext *pGameServer); ~CSqlScore() {} + // Requested by game context virtual void CheckBirthday(int ClientID); virtual void LoadScore(int ClientID); + virtual void RandomMap(int ClientID, int Stars); + virtual void RandomUnfinishedMap(int ClientID, int Stars); + + // Requested by players (fails if another request by this player is active) virtual void MapInfo(int ClientID, const char* MapName); - virtual void MapVote(std::shared_ptr *ppResult, int ClientID, const char* MapName); - virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, - float CpTime[NUM_CHECKPOINTS], bool NotEligible); - virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, const char *pTimestamp); + virtual void MapVote(int ClientID, const char* MapName); virtual void ShowRank(int ClientID, const char* pName, bool Search = false); virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); @@ -229,12 +231,15 @@ public: virtual void ShowPoints(int ClientID, const char* pName, bool Search = false); virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void RandomMap(int ClientID, int Stars); - virtual void RandomUnfinishedMap(std::shared_ptr *ppResult, int ClientID, int stars); virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); virtual void GetSaves(int ClientID); + // Game relevant not allowed to fail + virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, + float CpTime[NUM_CHECKPOINTS], bool NotEligible); + virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, const char *pTimestamp); + virtual void OnShutdown(); }; From 3d259530368470df1bfd3f184179732cf59453f7 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 16:49:16 +0200 Subject: [PATCH 08/44] Ordered functions --- src/game/server/score/file_score.cpp | 4 ++-- src/game/server/score/file_score.h | 4 ++-- src/game/server/score/sql_score.cpp | 4 ++-- src/game/server/score/sql_score.h | 15 +++++++-------- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index f28d354a6..e7a0cff45 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -328,14 +328,14 @@ void CFileScore::ShowPoints(int ClientID, const char* pName, bool Search) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::RandomMap(int ClientID, int stars) +void CFileScore::RandomMap(int ClientID, int Stars) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Random map not supported in file based servers"); GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::RandomUnfinishedMap(int ClientID, int stars) +void CFileScore::RandomUnfinishedMap(int ClientID, int Stars) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Random unfinished map not supported in file based servers"); diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 59c4d11f3..4a4ac58d9 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -81,8 +81,8 @@ public: virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut); virtual void ShowPoints(int ClientID, const char* pName, bool Search); - virtual void RandomMap(int ClientID, int stars); - virtual void RandomUnfinishedMap(int ClientID, int stars); + virtual void RandomMap(int ClientID, int Stars); + virtual void RandomUnfinishedMap(int ClientID, int Stars); virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); virtual void GetSaves(int ClientID); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 5d1ffacae..8beefb9e4 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -404,7 +404,7 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, { pSqlServer->GetResults()->next(); int points = pSqlServer->GetResults()->getInt("Points"); - int stars = pSqlServer->GetResults()->getInt("Stars"); + int Stars = pSqlServer->GetResults()->getInt("Stars"); int finishes = pSqlServer->GetResults()->getInt("Finishes"); int finishers = pSqlServer->GetResults()->getInt("Finishers"); int average = pSqlServer->GetResults()->getInt("Average"); @@ -433,7 +433,7 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, } char aStars[20]; - switch(stars) + switch(Stars) { case 0: strcpy(aStars, "✰✰✰✰✰"); break; case 1: strcpy(aStars, "★✰✰✰✰"); break; diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 535c51ce7..35a270c6f 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -216,24 +216,23 @@ public: virtual void LoadScore(int ClientID); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); + virtual void MapVote(int ClientID, const char* MapName); // Requested by players (fails if another request by this player is active) virtual void MapInfo(int ClientID, const char* MapName); - virtual void MapVote(int ClientID, const char* MapName); virtual void ShowRank(int ClientID, const char* pName, bool Search = false); virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); virtual void ShowTimes(int ClientID, int Debut = 1); - virtual void ShowTop5(void *pResult, int ClientID, - void *pUserData, int Debut = 1); - virtual void ShowTeamTop5(void *pResult, int ClientID, - void *pUserData, int Debut = 1); + virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); + virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void ShowPoints(int ClientID, const char* pName, bool Search = false); - virtual void ShowTopPoints(void *pResult, int ClientID, - void *pUserData, int Debut = 1); + virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut = 1); + virtual void GetSaves(int ClientID); + + // requested by teams virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); - virtual void GetSaves(int ClientID); // Game relevant not allowed to fail virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, From 69cf72dae4758ff73659a5c8db0814873efc9fd0 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 17:48:34 +0200 Subject: [PATCH 09/44] Adjust file based ShowRank, ShowTeamRank, ShowPoints consistent functions to sql --- src/game/server/ddracechat.cpp | 9 +++----- src/game/server/score.h | 6 ++--- src/game/server/score/file_score.cpp | 20 +++++++---------- src/game/server/score/file_score.h | 13 +++++------ src/game/server/score/sql_score.cpp | 33 ++++++++++++++++++++-------- src/game/server/score/sql_score.h | 28 ++++++++--------------- 6 files changed, 52 insertions(+), 57 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index e76715bcd..659c540c9 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -790,8 +790,7 @@ void CGameContext::ConTeamRank(IConsole::IResult *pResult, void *pUserData) if (pResult->NumArguments() > 0) if (!g_Config.m_SvHideScore) - pSelf->Score()->ShowTeamRank(pResult->m_ClientID, pResult->GetString(0), - true); + pSelf->Score()->ShowTeamRank(pResult->m_ClientID, pResult->GetString(0)); else pSelf->Console()->Print( IConsole::OUTPUT_LEVEL_STANDARD, @@ -825,8 +824,7 @@ void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData) if (pResult->NumArguments() > 0) if (!g_Config.m_SvHideScore) - pSelf->Score()->ShowRank(pResult->m_ClientID, pResult->GetString(0), - true); + pSelf->Score()->ShowRank(pResult->m_ClientID, pResult->GetString(0)); else pSelf->Console()->Print( IConsole::OUTPUT_LEVEL_STANDARD, @@ -1548,8 +1546,7 @@ void CGameContext::ConPoints(IConsole::IResult *pResult, void *pUserData) if (pResult->NumArguments() > 0) if (!g_Config.m_SvHideScore) - pSelf->Score()->ShowPoints(pResult->m_ClientID, pResult->GetString(0), - true); + pSelf->Score()->ShowPoints(pResult->m_ClientID, pResult->GetString(0)); else pSelf->Console()->Print( IConsole::OUTPUT_LEVEL_STANDARD, diff --git a/src/game/server/score.h b/src/game/server/score.h index a2e3a88c8..7c3851260 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -69,13 +69,13 @@ public: virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0; virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; - virtual void ShowRank(int ClientID, const char *pName, bool Search=false) = 0; + virtual void ShowRank(int ClientID, const char *pName) = 0; virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; - virtual void ShowTeamRank(int ClientID, const char *pName, bool Search=false) = 0; + virtual void ShowTeamRank(int ClientID, const char *pName) = 0; virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; - virtual void ShowPoints(int ClientID, const char *pName, bool Search=false) = 0; + virtual void ShowPoints(int ClientID, const char *pName) = 0; virtual void RandomMap(int ClientID, int Stars) = 0; virtual void RandomUnfinishedMap(int ClientID, int Stars) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index e7a0cff45..c14713237 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -262,16 +262,13 @@ void CFileScore::ShowTop5(void *pResult, int ClientID, pSelf->SendChatTarget(ClientID, "------------------------------"); } -void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) +void CFileScore::ShowRank(int ClientID, const char* pName) { CPlayerScore *pScore; int Pos = -2; char aBuf[512]; - if (!Search) - pScore = SearchScore(ClientID, &Pos); - else - pScore = SearchName(pName, &Pos, 1); + pScore = SearchName(pName, &Pos, 1); if (pScore && Pos > -1) { @@ -285,17 +282,16 @@ void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) "%d. %s Time: %d minute(s) %5.2f second(s), requested by (%s)", Pos, pScore->m_aName, (int)Time / 60, Time - ((int)Time / 60 * 60), Server()->ClientName(ClientID)); - if (!Search) - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, ClientID); - else + if (g_Config.m_SvHideScore) GameServer()->SendChatTarget(ClientID, aBuf); + else + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, ClientID); return; } else if (Pos == -1) str_format(aBuf, sizeof(aBuf), "Several players were found."); else - str_format(aBuf, sizeof(aBuf), "%s is not ranked", - Search ? pName : Server()->ClientName(ClientID)); + str_format(aBuf, sizeof(aBuf), "%s is not ranked", pName); GameServer()->SendChatTarget(ClientID, aBuf); } @@ -307,7 +303,7 @@ void CFileScore::ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTeamRank(int ClientID, const char* pName, bool Search) +void CFileScore::ShowTeamRank(int ClientID, const char* pName) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); @@ -321,7 +317,7 @@ void CFileScore::ShowTopPoints(void *pResult, int ClientID, void *pUserData, int GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowPoints(int ClientID, const char* pName, bool Search) +void CFileScore::ShowPoints(int ClientID, const char* pName) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Points not supported in file based servers"); diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 4a4ac58d9..d90d20c15 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -21,10 +21,8 @@ class CFileScore: public IScore float m_Score; float m_aCpTime[NUM_CHECKPOINTS]; - CPlayerScore() - { - } - ; + CPlayerScore() {} + CPlayerScore(const char *pName, float Score, float aCpTime[NUM_CHECKPOINTS]); @@ -49,7 +47,6 @@ class CFileScore: public IScore { return SearchName(Server()->ClientName(ID), pPosition, 0); } - ; CPlayerScore *SearchName(const char *pName, int *pPosition, bool MatchCase); void UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS]); @@ -73,14 +70,14 @@ public: virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void ShowRank(int ClientID, const char* pName, bool Search = false); + virtual void ShowRank(int ClientID, const char* pName); virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); + virtual void ShowTeamRank(int ClientID, const char* pName); virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut); - virtual void ShowPoints(int ClientID, const char* pName, bool Search); + virtual void ShowPoints(int ClientID, const char* pName); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 8beefb9e4..42c512110 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -16,7 +16,7 @@ #include "../gamemodes/DDRace.h" #include "../save.h" -std::atomic_int CSqlExecData::ms_InstanceCount(0); +std::atomic_int CSqlScore::ms_InstanceCount(0); CSqlResult::CSqlResult() : m_Done(false), @@ -26,6 +26,7 @@ CSqlResult::CSqlResult() : { m_Message[0] = 0; } + CSqlResult::~CSqlResult() { switch(m_Tag) { @@ -44,6 +45,23 @@ CSqlResult::~CSqlResult() { } } +CSqlExecData::CSqlExecData( + bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), + CSqlData *pSqlResult, + bool ReadOnly +) : + m_pFuncPtr(pFuncPtr), + m_pSqlData(pSqlResult), + m_ReadOnly(ReadOnly) +{ + ++CSqlScore::ms_InstanceCount; +} + +CSqlExecData::~CSqlExecData() +{ + --CSqlScore::ms_InstanceCount; +} + std::shared_ptr CSqlScore::NewSqlResult(int ClientID) { CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientID]; @@ -67,7 +85,7 @@ CSqlScore::CSqlScore(CGameContext *pGameServer) : void CSqlScore::OnShutdown() { int i = 0; - while (CSqlExecData::ms_InstanceCount != 0) + while (CSqlScore::ms_InstanceCount != 0) { if (i > 600) { dbg_msg("sql", "Waited 60 seconds for score-threads to complete, quitting anyway"); @@ -76,7 +94,7 @@ void CSqlScore::OnShutdown() // print a log about every two seconds if (i % 20 == 0) - dbg_msg("sql", "Waiting for score-threads to complete (%d left)", CSqlExecData::ms_InstanceCount.load()); + dbg_msg("sql", "Waiting for score-threads to complete (%d left)", CSqlScore::ms_InstanceCount.load()); ++i; thread_sleep(100000); } @@ -739,13 +757,12 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData *pGam return false; } -void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) +void CSqlScore::ShowRank(int ClientID, const char* pName) { /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; - Tmp->m_Search = Search; str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowRankThread, Tmp), "show rank"); @@ -813,13 +830,12 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData return false; } -void CSqlScore::ShowTeamRank(int ClientID, const char* pName, bool Search) +void CSqlScore::ShowTeamRank(int ClientID, const char* pName) { /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; - Tmp->m_Search = Search; str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTeamRankThread, Tmp), "show team rank"); @@ -1186,13 +1202,12 @@ bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameDat return false; } -void CSqlScore::ShowPoints(int ClientID, const char* pName, bool Search) +void CSqlScore::ShowPoints(int ClientID, const char* pName) { /* CSqlScoreData *Tmp = new CSqlScoreData(); Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; - Tmp->m_Search = Search; str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowPointsThread, Tmp), "show points"); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 35a270c6f..7ad04af00 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -143,24 +143,12 @@ struct CSqlExecData bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), CSqlData *pSqlResult, bool ReadOnly = true - ) : - m_pFuncPtr(pFuncPtr), - m_pSqlData(pSqlResult), - m_ReadOnly(ReadOnly) - { - ++ms_InstanceCount; - } - ~CSqlExecData() - { - --ms_InstanceCount; - } + ); + ~CSqlExecData(); bool (*m_pFuncPtr) (CSqlServer*, const CSqlData *, bool); CSqlData *m_pSqlData; bool m_ReadOnly; - - // keeps track of score-threads - static std::atomic_int ms_InstanceCount; }; class IServer; @@ -207,26 +195,28 @@ class CSqlScore: public IScore std::shared_ptr NewSqlResult(int ClientID); public: + // keeps track of score-threads + static std::atomic_int ms_InstanceCount; CSqlScore(CGameContext *pGameServer); ~CSqlScore() {} // Requested by game context - virtual void CheckBirthday(int ClientID); - virtual void LoadScore(int ClientID); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); virtual void MapVote(int ClientID, const char* MapName); + virtual void CheckBirthday(int ClientID); + virtual void LoadScore(int ClientID); // Requested by players (fails if another request by this player is active) virtual void MapInfo(int ClientID, const char* MapName); - virtual void ShowRank(int ClientID, const char* pName, bool Search = false); - virtual void ShowTeamRank(int ClientID, const char* pName, bool Search = false); + virtual void ShowRank(int ClientID, const char* pName); + virtual void ShowTeamRank(int ClientID, const char* pName); virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); virtual void ShowTimes(int ClientID, int Debut = 1); virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void ShowPoints(int ClientID, const char* pName, bool Search = false); + virtual void ShowPoints(int ClientID, const char* pName); virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut = 1); virtual void GetSaves(int ClientID); From 4ee08dcc5c639ebbc026f9bd502b2bc13b9f39d6 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 17:52:26 +0200 Subject: [PATCH 10/44] Remove unused arguments from IScore --- src/game/server/ddracechat.cpp | 12 ++++++------ src/game/server/score.h | 6 +++--- src/game/server/score/file_score.cpp | 6 +++--- src/game/server/score/file_score.h | 6 +++--- src/game/server/score/sql_score.cpp | 6 +++--- src/game/server/score/sql_score.h | 6 +++--- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 659c540c9..073edc943 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -386,10 +386,10 @@ void CGameContext::ConTeamTop5(IConsole::IResult *pResult, void *pUserData) } if (pResult->NumArguments() > 0) - pSelf->Score()->ShowTeamTop5(pResult, pResult->m_ClientID, pUserData, + pSelf->Score()->ShowTeamTop5(pResult->m_ClientID, pUserData, pResult->GetInteger(0)); else - pSelf->Score()->ShowTeamTop5(pResult, pResult->m_ClientID, pUserData); + pSelf->Score()->ShowTeamTop5(pResult->m_ClientID, pUserData); #if defined(CONF_SQL) if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) @@ -417,10 +417,10 @@ void CGameContext::ConTop5(IConsole::IResult *pResult, void *pUserData) } if (pResult->NumArguments() > 0) - pSelf->Score()->ShowTop5(pResult, pResult->m_ClientID, pUserData, + pSelf->Score()->ShowTop5(pResult->m_ClientID, pUserData, pResult->GetInteger(0)); else - pSelf->Score()->ShowTop5(pResult, pResult->m_ClientID, pUserData); + pSelf->Score()->ShowTop5(pResult->m_ClientID, pUserData); #if defined(CONF_SQL) if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) @@ -1580,10 +1580,10 @@ void CGameContext::ConTopPoints(IConsole::IResult *pResult, void *pUserData) } if (pResult->NumArguments() > 0) - pSelf->Score()->ShowTopPoints(pResult, pResult->m_ClientID, pUserData, + pSelf->Score()->ShowTopPoints(pResult->m_ClientID, pUserData, pResult->GetInteger(0)); else - pSelf->Score()->ShowTopPoints(pResult, pResult->m_ClientID, pUserData); + pSelf->Score()->ShowTopPoints(pResult->m_ClientID, pUserData); if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); diff --git a/src/game/server/score.h b/src/game/server/score.h index 7c3851260..b31e70752 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -68,13 +68,13 @@ public: virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0; - virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTop5(int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowRank(int ClientID, const char *pName) = 0; - virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTeamTop5(int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowTeamRank(int ClientID, const char *pName) = 0; - virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTopPoints(int ClientID, void *pUserData, int Debut=1) = 0; virtual void ShowPoints(int ClientID, const char *pName) = 0; virtual void RandomMap(int ClientID, int Stars) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index c14713237..8c9e1dc98 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -241,7 +241,7 @@ void CFileScore::SaveScore(int ClientID, float Time, const char *pTimestamp, UpdatePlayer(ClientID, Time, CpTime); } -void CFileScore::ShowTop5(void *pResult, int ClientID, +void CFileScore::ShowTop5(int ClientID, void *pUserData, int Debut) { CGameContext *pSelf = (CGameContext *) pUserData; @@ -296,7 +296,7 @@ void CFileScore::ShowRank(int ClientID, const char* pName) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut) +void CFileScore::ShowTeamTop5(int ClientID, void *pUserData, int Debut) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); @@ -310,7 +310,7 @@ void CFileScore::ShowTeamRank(int ClientID, const char* pName) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut) +void CFileScore::ShowTopPoints(int ClientID, void *pUserData, int Debut) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index d90d20c15..0fae5aa1c 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -68,15 +68,15 @@ public: float CpTime[NUM_CHECKPOINTS], bool NotEligible); virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp); - virtual void ShowTop5(void *pResult, int ClientID, + virtual void ShowTop5(int ClientID, void *pUserData, int Debut = 1); virtual void ShowRank(int ClientID, const char* pName); - virtual void ShowTeamTop5(void *pResult, int ClientID, + virtual void ShowTeamTop5(int ClientID, void *pUserData, int Debut = 1); virtual void ShowTeamRank(int ClientID, const char* pName); - virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut); + virtual void ShowTopPoints(int ClientID, void *pUserData, int Debut); virtual void ShowPoints(int ClientID, const char* pName); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 42c512110..f2e27f3ef 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -921,7 +921,7 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGame return false; } -void CSqlScore::ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTop5(int ClientID, void *pUserData, int Debut) { /* CSqlScoreData *Tmp = new CSqlScoreData(); @@ -986,7 +986,7 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData return false; } -void CSqlScore::ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTeamTop5(int ClientID, void *pUserData, int Debut) { /* CSqlScoreData *Tmp = new CSqlScoreData(); @@ -1263,7 +1263,7 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameDa return false; } -void CSqlScore::ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTopPoints(int ClientID, void *pUserData, int Debut) { /* CSqlScoreData *Tmp = new CSqlScoreData(); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 7ad04af00..c614ca555 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -214,10 +214,10 @@ public: virtual void ShowTeamRank(int ClientID, const char* pName); virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); virtual void ShowTimes(int ClientID, int Debut = 1); - virtual void ShowTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); - virtual void ShowTeamTop5(void *pResult, int ClientID, void *pUserData, int Debut = 1); + virtual void ShowTop5(int ClientID, void *pUserData, int Debut = 1); + virtual void ShowTeamTop5(int ClientID, void *pUserData, int Debut = 1); virtual void ShowPoints(int ClientID, const char* pName); - virtual void ShowTopPoints(void *pResult, int ClientID, void *pUserData, int Debut = 1); + virtual void ShowTopPoints(int ClientID, void *pUserData, int Debut = 1); virtual void GetSaves(int ClientID); // requested by teams From 873f478a82d96f721291ecf9161dc883e4f88bc8 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 17:56:32 +0200 Subject: [PATCH 11/44] Rename Debut to Offset --- src/game/server/score.h | 6 +++--- src/game/server/score/file_score.cpp | 14 +++++++------- src/game/server/score/file_score.h | 6 +++--- src/game/server/score/sql_score.cpp | 20 ++++++++++---------- src/game/server/score/sql_score.h | 10 +++++----- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/game/server/score.h b/src/game/server/score.h index b31e70752..ed369ef3c 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -68,13 +68,13 @@ public: virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0; - virtual void ShowTop5(int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTop5(int ClientID, void *pUserData, int Offset=1) = 0; virtual void ShowRank(int ClientID, const char *pName) = 0; - virtual void ShowTeamTop5(int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTeamTop5(int ClientID, void *pUserData, int Offset=1) = 0; virtual void ShowTeamRank(int ClientID, const char *pName) = 0; - virtual void ShowTopPoints(int ClientID, void *pUserData, int Debut=1) = 0; + virtual void ShowTopPoints(int ClientID, void *pUserData, int Offset=1) = 0; virtual void ShowPoints(int ClientID, const char *pName) = 0; virtual void RandomMap(int ClientID, int Stars) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 8c9e1dc98..8ed2bb39a 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -242,19 +242,19 @@ void CFileScore::SaveScore(int ClientID, float Time, const char *pTimestamp, } void CFileScore::ShowTop5(int ClientID, - void *pUserData, int Debut) + void *pUserData, int Offset) { CGameContext *pSelf = (CGameContext *) pUserData; char aBuf[512]; - Debut = maximum(1, Debut < 0 ? m_Top.size() + Debut - 3 : Debut); + Offset = maximum(1, Offset < 0 ? m_Top.size() + Offset - 3 : Offset); pSelf->SendChatTarget(ClientID, "----------- Top 5 -----------"); for (int i = 0; i < 5; i++) { - if (i + Debut > m_Top.size()) + if (i + Offset > m_Top.size()) break; - CPlayerScore *r = &m_Top[i + Debut - 1]; + CPlayerScore *r = &m_Top[i + Offset - 1]; str_format(aBuf, sizeof(aBuf), - "%d. %s Time: %d minute(s) %5.2f second(s)", i + Debut, + "%d. %s Time: %d minute(s) %5.2f second(s)", i + Offset, r->m_aName, (int)r->m_Score / 60, r->m_Score - ((int)r->m_Score / 60 * 60)); pSelf->SendChatTarget(ClientID, aBuf); @@ -296,7 +296,7 @@ void CFileScore::ShowRank(int ClientID, const char* pName) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTeamTop5(int ClientID, void *pUserData, int Debut) +void CFileScore::ShowTeamTop5(int ClientID, void *pUserData, int Offset) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); @@ -310,7 +310,7 @@ void CFileScore::ShowTeamRank(int ClientID, const char* pName) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTopPoints(int ClientID, void *pUserData, int Debut) +void CFileScore::ShowTopPoints(int ClientID, void *pUserData, int Offset) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 0fae5aa1c..4e51c32dc 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -69,14 +69,14 @@ public: virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp); virtual void ShowTop5(int ClientID, - void *pUserData, int Debut = 1); + void *pUserData, int Offset = 1); virtual void ShowRank(int ClientID, const char* pName); virtual void ShowTeamTop5(int ClientID, - void *pUserData, int Debut = 1); + void *pUserData, int Offset = 1); virtual void ShowTeamRank(int ClientID, const char* pName); - virtual void ShowTopPoints(int ClientID, void *pUserData, int Debut); + virtual void ShowTopPoints(int ClientID, void *pUserData, int Offset); virtual void ShowPoints(int ClientID, const char* pName); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index f2e27f3ef..0dcd2ce0c 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -921,11 +921,11 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGame return false; } -void CSqlScore::ShowTop5(int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTop5(int ClientID, void *pUserData, int Offset) { /* CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; + Tmp->m_Num = Offset; Tmp->m_ClientID = ClientID; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTop5Thread, Tmp), "show top5"); @@ -986,11 +986,11 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData return false; } -void CSqlScore::ShowTeamTop5(int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTeamTop5(int ClientID, void *pUserData, int Offset) { /* CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; + Tmp->m_Num = Offset; Tmp->m_ClientID = ClientID; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTeamTop5Thread, Tmp), "show team top5"); @@ -1097,11 +1097,11 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGame return false; } -void CSqlScore::ShowTimes(int ClientID, int Debut) +void CSqlScore::ShowTimes(int ClientID, int Offset) { /* CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; + Tmp->m_Num = Offset; Tmp->m_ClientID = ClientID; Tmp->m_Search = false; @@ -1109,11 +1109,11 @@ void CSqlScore::ShowTimes(int ClientID, int Debut) */ } -void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut) +void CSqlScore::ShowTimes(int ClientID, const char* pName, int Offset) { /* CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; + Tmp->m_Num = Offset; Tmp->m_ClientID = ClientID; Tmp->m_Name = pName; Tmp->m_Search = true; @@ -1263,11 +1263,11 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameDa return false; } -void CSqlScore::ShowTopPoints(int ClientID, void *pUserData, int Debut) +void CSqlScore::ShowTopPoints(int ClientID, void *pUserData, int Offset) { /* CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Debut; + Tmp->m_Num = Offset; Tmp->m_ClientID = ClientID; thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTopPointsThread, Tmp), "show top points"); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index c614ca555..fd0a73cdb 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -212,12 +212,12 @@ public: virtual void MapInfo(int ClientID, const char* MapName); virtual void ShowRank(int ClientID, const char* pName); virtual void ShowTeamRank(int ClientID, const char* pName); - virtual void ShowTimes(int ClientID, const char* pName, int Debut = 1); - virtual void ShowTimes(int ClientID, int Debut = 1); - virtual void ShowTop5(int ClientID, void *pUserData, int Debut = 1); - virtual void ShowTeamTop5(int ClientID, void *pUserData, int Debut = 1); + virtual void ShowTimes(int ClientID, const char* pName, int Offset = 1); + virtual void ShowTimes(int ClientID, int Offset = 1); + virtual void ShowTop5(int ClientID, void *pUserData, int Offset = 1); + virtual void ShowTeamTop5(int ClientID, void *pUserData, int Offset = 1); virtual void ShowPoints(int ClientID, const char* pName); - virtual void ShowTopPoints(int ClientID, void *pUserData, int Debut = 1); + virtual void ShowTopPoints(int ClientID, void *pUserData, int Offset = 1); virtual void GetSaves(int ClientID); // requested by teams From 9add3543671b79fdb6c6bed97625065b187fb141 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 18:46:39 +0200 Subject: [PATCH 12/44] Use Template for SqlResult --- src/game/server/player.cpp | 28 +++------ src/game/server/player.h | 4 +- src/game/server/score/sql_score.cpp | 67 +++++++++++---------- src/game/server/score/sql_score.h | 91 +++++++++++++++++------------ 4 files changed, 100 insertions(+), 90 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 8b6119f75..2d7f94232 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -793,33 +793,19 @@ void CPlayer::ProcessSqlResult() { if(m_SqlQueryResult == nullptr || !m_SqlQueryResult->m_Done) return; - if(m_SqlQueryResult->m_Message[0] != 0) + for(int i = 0; i < (int)(sizeof(m_SqlQueryResult->m_Message)/sizeof(m_SqlQueryResult->m_Message[0])); i++) { + if(m_SqlQueryResult->m_Message[i][0] == 0) + break; switch(m_SqlQueryResult->m_MessageTarget) { - case CSqlResult::DIRECT: - GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_Message); + case CSqlPlayerResult::DIRECT: + GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_Message[i]); break; - case CSqlResult::TEAM: - if(m_SqlQueryResult->m_TeamMessageTo == -1) - break; - GameServer()->SendChatTeam(m_SqlQueryResult->m_TeamMessageTo, m_SqlQueryResult->m_Message); - break; - case CSqlResult::ALL: - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_Message); + case CSqlPlayerResult::ALL: + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_Message[i]); break; } } - switch(m_SqlQueryResult->m_Tag) - { - case CSqlResult::NONE: - break; - case CSqlResult::LOAD: - break; - case CSqlResult::RANDOM_MAP: - break; - case CSqlResult::MAP_VOTE: - break; - } } #endif diff --git a/src/game/server/player.h b/src/game/server/player.h index 4e41b1274..93ffdf00c 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -10,7 +10,7 @@ #include #if defined(CONF_SQL) -class CSqlResult; +class CSqlPlayerResult; #endif // player object @@ -202,7 +202,7 @@ public: #if defined(CONF_SQL) void ProcessSqlResult(); int64 m_LastSQLQuery; - std::shared_ptr m_SqlQueryResult; + std::shared_ptr m_SqlQueryResult; #endif bool m_NotEligibleForFinish; int64 m_EligibleForFinishCheck; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 0dcd2ce0c..f380498bf 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -45,9 +45,10 @@ CSqlResult::~CSqlResult() { } } -CSqlExecData::CSqlExecData( - bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), - CSqlData *pSqlResult, +template < typename TResult > +CSqlExecData::CSqlExecData( + bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), + CSqlData *pSqlResult, bool ReadOnly ) : m_pFuncPtr(pFuncPtr), @@ -57,17 +58,18 @@ CSqlExecData::CSqlExecData( ++CSqlScore::ms_InstanceCount; } -CSqlExecData::~CSqlExecData() +template < typename TResult > +CSqlExecData::~CSqlExecData() { --CSqlScore::ms_InstanceCount; } -std::shared_ptr CSqlScore::NewSqlResult(int ClientID) +std::shared_ptr CSqlScore::NewSqlPlayerResult(int ClientID) { CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientID]; if(pCurPlayer->m_SqlQueryResult != nullptr) // TODO: send player a message: "too many requests" return nullptr; - pCurPlayer->m_SqlQueryResult = std::make_shared(); + pCurPlayer->m_SqlQueryResult = std::make_shared(); return pCurPlayer->m_SqlQueryResult; } @@ -79,7 +81,7 @@ CSqlScore::CSqlScore(CGameContext *pGameServer) : { CSqlConnector::ResetReachable(); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(Init, new CSqlData(nullptr)), "SqlScore constructor"); + //thread_init_and_detach(ExecSqlFunc, new CSqlExecData(Init, new CSqlData(nullptr)), "SqlScore constructor"); } void CSqlScore::OnShutdown() @@ -102,9 +104,10 @@ void CSqlScore::OnShutdown() lock_destroy(ms_FailureFileLock); } -void CSqlScore::ExecSqlFunc(void *pUser) +template < typename TResult > +void CSqlExecData::ExecSqlFunc(void *pUser) { - CSqlExecData* pData = (CSqlExecData *)pUser; + CSqlExecData* pData = (CSqlExecData *)pUser; CSqlConnector connector; @@ -137,10 +140,10 @@ void CSqlScore::ExecSqlFunc(void *pUser) delete pData; } -bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* - const CSqlData* pData = pGameData; + const CSqlData* pData = pGameData; if (HandleFailure) { @@ -183,7 +186,7 @@ void CSqlScore::CheckBirthday(int ClientID) */ } -bool CSqlScore::CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* TODO const CSqlPlayerData *pData = dynamic_cast(pGameData); @@ -235,7 +238,7 @@ void CSqlScore::LoadScore(int ClientID) } // update stuff -bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlPlayerData *pData = dynamic_cast(pGameData); @@ -307,7 +310,7 @@ void CSqlScore::MapVote(int ClientID, const char* MapName) */ } -bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlMapVoteData *pData = dynamic_cast(pGameData); @@ -400,7 +403,7 @@ void CSqlScore::MapInfo(int ClientID, const char* MapName) */ } -bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlMapData *pData = dynamic_cast(pGameData); @@ -508,7 +511,7 @@ void CSqlScore::SaveScore(int ClientID, float Time, const char *pTimestamp, floa */ } -bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -616,7 +619,7 @@ void CSqlScore::SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, co */ } -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); @@ -769,7 +772,7 @@ void CSqlScore::ShowRank(int ClientID, const char* pName) */ } -bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -842,7 +845,7 @@ void CSqlScore::ShowTeamRank(int ClientID, const char* pName) */ } -bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -932,7 +935,7 @@ void CSqlScore::ShowTop5(int ClientID, void *pUserData, int Offset) */ } -bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -997,7 +1000,7 @@ void CSqlScore::ShowTeamTop5(int ClientID, void *pUserData, int Offset) */ } -bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -1122,7 +1125,7 @@ void CSqlScore::ShowTimes(int ClientID, const char* pName, int Offset) */ } -bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -1214,7 +1217,7 @@ void CSqlScore::ShowPoints(int ClientID, const char* pName) */ } -bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -1274,7 +1277,7 @@ void CSqlScore::ShowTopPoints(int ClientID, void *pUserData, int Offset) */ } -bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlScoreData *pData = dynamic_cast(pGameData); @@ -1325,6 +1328,7 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGam void CSqlScore::RandomMap(int ClientID, int Stars) { + /* auto pResult = NewSqlResult(ClientID); if(pResult == nullptr) return; @@ -1332,13 +1336,14 @@ void CSqlScore::RandomMap(int ClientID, int Stars) Tmp->m_Stars = Stars; // TODO: Set Client result Tmp->m_ClientID = ClientID; - strncpy(Tmp->m_aCurrentMap, m_aMap, sizeof(Tmp->m_aCurrentMap)); - strncpy(Tmp->m_aServerType, g_Config.m_SvServerType, sizeof(Tmp->m_aServerType)); + str_copy(Tmp->m_aCurrentMap, m_aMap, sizeof(Tmp->m_aCurrentMap)); + str_copy(Tmp->m_aServerType, g_Config.m_SvServerType, sizeof(Tmp->m_aServerType)); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(RandomMapThread, Tmp), "random map"); + */ } -bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { const CSqlRandomMap *pData = dynamic_cast(pGameData); @@ -1414,7 +1419,7 @@ void CSqlScore::RandomUnfinishedMap(int ClientID, int Stars) */ } -bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlRandomMap *pData = dynamic_cast(pGameData); @@ -1491,7 +1496,7 @@ void CSqlScore::SaveTeam(int Team, const char* Code, int ClientID, const char* S */ } -bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlTeamSave *pData = dynamic_cast(pGameData); @@ -1596,7 +1601,7 @@ void CSqlScore::LoadTeam(const char* Code, int ClientID) */ } -bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlTeamLoad *pData = dynamic_cast(pGameData); @@ -1732,7 +1737,7 @@ void CSqlScore::GetSaves(int ClientID) */ } -bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlGetSavesData *pData = dynamic_cast(pGameData); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index fd0a73cdb..ae5dc53dd 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -11,11 +11,25 @@ class CSqlServer; +class CSqlPlayerResult { +public: + std::atomic_bool m_Done; + bool m_Failed; + + enum + { + DIRECT, + ALL, + } m_MessageTarget; + char m_Message[512][7]; +}; + // result only valid if m_Done is set to true class CSqlResult { public: std::atomic_bool m_Done; + bool m_Failed; // specify where chat messages should be returned enum { @@ -48,23 +62,24 @@ public: }; // holding relevant data for one thread, and function pointer for return values +template < typename TResult > struct CSqlData { - CSqlData(std::shared_ptr pSqlResult) : + CSqlData(std::shared_ptr pSqlResult) : m_pSqlResult(pSqlResult) { } - std::shared_ptr m_pSqlResult; + std::shared_ptr m_pSqlResult; virtual ~CSqlData() = default; }; -struct CSqlPlayerData : CSqlData +struct CSqlPlayerData : CSqlData { int m_ClientID; sqlstr::CSqlString m_Name; }; // used for mapinfo -struct CSqlMapData : CSqlData +struct CSqlMapData : CSqlData { int m_ClientID; @@ -79,7 +94,7 @@ struct CSqlMapVoteData : CSqlMapData std::shared_ptr m_pResult; }; -struct CSqlScoreData : CSqlData +struct CSqlScoreData : CSqlData { int m_ClientID; @@ -94,7 +109,7 @@ struct CSqlScoreData : CSqlData char m_aRequestingPlayer[MAX_NAME_LENGTH]; }; -struct CSqlTeamScoreData : CSqlData +struct CSqlTeamScoreData : CSqlData { bool m_NotEligible; float m_Time; @@ -104,7 +119,7 @@ struct CSqlTeamScoreData : CSqlData sqlstr::CSqlString m_aNames[MAX_CLIENTS]; }; -struct CSqlTeamSave : CSqlData +struct CSqlTeamSave : CSqlData { virtual ~CSqlTeamSave(); @@ -116,19 +131,19 @@ struct CSqlTeamSave : CSqlData char m_Server[5]; }; -struct CSqlTeamLoad : CSqlData +struct CSqlTeamLoad : CSqlData { sqlstr::CSqlString<128> m_Code; int m_ClientID; char m_ClientName[MAX_NAME_LENGTH]; }; -struct CSqlGetSavesData: CSqlData +struct CSqlGetSavesData: CSqlData { sqlstr::CSqlString m_Name; }; -struct CSqlRandomMap : CSqlData +struct CSqlRandomMap : CSqlData { using CSqlData::CSqlData; int m_Stars; @@ -137,18 +152,21 @@ struct CSqlRandomMap : CSqlData }; // controls one thread +template < typename TResult > struct CSqlExecData { CSqlExecData( - bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), - CSqlData *pSqlResult, + bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), + CSqlData *pSqlResult, bool ReadOnly = true ); ~CSqlExecData(); - bool (*m_pFuncPtr) (CSqlServer*, const CSqlData *, bool); - CSqlData *m_pSqlData; + bool (*m_pFuncPtr) (CSqlServer*, const CSqlData *, bool); + CSqlData *m_pSqlData; bool m_ReadOnly; + + static void ExecSqlFunc(void *pUser); }; class IServer; @@ -158,28 +176,29 @@ class CSqlScore: public IScore { static LOCK ms_FailureFileLock; - static void ExecSqlFunc(void *pUser); - static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); + static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); - static bool CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool LoadScoreThread(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 ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + + static bool MapInfoThread(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 ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + + static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); CGameContext *GameServer() { return m_pGameServer; } IServer *Server() { return m_pServer; } @@ -192,7 +211,7 @@ class CSqlScore: public IScore char m_aGameUuid[UUID_MAXSTRSIZE]; // returns new SqlResult bound to the player, if no current Thread is active for this player - std::shared_ptr NewSqlResult(int ClientID); + std::shared_ptr NewSqlPlayerResult(int ClientID); public: // keeps track of score-threads @@ -201,7 +220,7 @@ public: CSqlScore(CGameContext *pGameServer); ~CSqlScore() {} - // Requested by game context + // Requested by game context, shouldn't fail in case the player started another thread virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); virtual void MapVote(int ClientID, const char* MapName); From 4152a1995e05fedd6055a48b4fdf8637eac44674 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 23:57:59 +0200 Subject: [PATCH 13/44] Add missing dependeny to sql string helper --- src/engine/server/sql_string_helpers.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/server/sql_string_helpers.h b/src/engine/server/sql_string_helpers.h index bb9ae80bb..120a221af 100644 --- a/src/engine/server/sql_string_helpers.h +++ b/src/engine/server/sql_string_helpers.h @@ -1,6 +1,8 @@ #ifndef ENGINE_SERVER_SQL_STRING_HELPERS_H #define ENGINE_SERVER_SQL_STRING_HELPERS_H +#include + namespace sqlstr { From 76c227a5422cc7eba3090c92740189e3e1c9f06f Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 28 May 2020 23:58:20 +0200 Subject: [PATCH 14/44] Reimplement /mapinfo command --- src/game/server/player.cpp | 8 +-- src/game/server/score/sql_score.cpp | 98 ++++++++++++++++++++--------- src/game/server/score/sql_score.h | 12 ++-- 3 files changed, 80 insertions(+), 38 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 2d7f94232..774f0a477 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -793,17 +793,17 @@ void CPlayer::ProcessSqlResult() { if(m_SqlQueryResult == nullptr || !m_SqlQueryResult->m_Done) return; - for(int i = 0; i < (int)(sizeof(m_SqlQueryResult->m_Message)/sizeof(m_SqlQueryResult->m_Message[0])); i++) + for(int i = 0; i < (int)(sizeof(m_SqlQueryResult->m_aaMessages)/sizeof(m_SqlQueryResult->m_aaMessages[0])); i++) { - if(m_SqlQueryResult->m_Message[i][0] == 0) + if(m_SqlQueryResult->m_aaMessages[i][0] == 0) break; switch(m_SqlQueryResult->m_MessageTarget) { case CSqlPlayerResult::DIRECT: - GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_Message[i]); + GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_aaMessages[i]); break; case CSqlPlayerResult::ALL: - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_Message[i]); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_aaMessages[i]); break; } } diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index f380498bf..05d956bf4 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -18,6 +19,15 @@ std::atomic_int CSqlScore::ms_InstanceCount(0); +CSqlPlayerResult::CSqlPlayerResult() : + m_Done(0), + m_Failed(0), + m_MessageTarget(DIRECT) +{ + for(int i = 0; i < (int)(sizeof(m_aaMessages)/sizeof(m_aaMessages[0])); i++) + m_aaMessages[i][0] = 0; +} + CSqlResult::CSqlResult() : m_Done(false), m_MessageTarget(DIRECT), @@ -31,7 +41,6 @@ CSqlResult::~CSqlResult() { switch(m_Tag) { case NONE: - case MESSAGES: break; case LOAD: //m_Variant.m_LoadTeam.~CSaveTeam(); @@ -390,8 +399,11 @@ bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData void CSqlScore::MapInfo(int ClientID, const char* MapName) { - /* - CSqlMapData *Tmp = new CSqlMapData(); + auto pResult = NewSqlPlayerResult(ClientID); + if(pResult == nullptr) + return; + CSqlMapData *Tmp = new CSqlMapData(pResult); + Tmp->m_ClientID = ClientID; Tmp->m_RequestedMap = MapName; Tmp->m_Name = Server()->ClientName(ClientID); @@ -399,13 +411,13 @@ void CSqlScore::MapInfo(int ClientID, const char* MapName) sqlstr::ClearString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); sqlstr::FuzzyString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(MapInfoThread, Tmp), "map info"); - */ + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(MapInfoThread, Tmp), + "map info"); } bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* const CSqlMapData *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -414,17 +426,43 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), pSqlServer->GetPrefix(), pData->m_aFuzzyMap, pData->m_RequestedMap.ClrStr(), pData->m_RequestedMap.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT l.Map, l.Server, Mapper, Points, Stars, " + "(select count(Name) from %s_race where Map = l.Map) as Finishes, " + "(select count(distinct Name) from %s_race where Map = l.Map) as Finishers, " + "(select round(avg(Time)) from %s_race where Map = l.Map) as Average, " + "UNIX_TIMESTAMP(l.Timestamp) as Stamp, " + "UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(l.Timestamp) as Ago, " + "(select min(Time) from %s_race where Map = l.Map and Name = '%s') as OwnTime " + "FROM (" + "SELECT * FROM %s_maps " + "WHERE Map LIKE '%s' COLLATE utf8mb4_general_ci " + "ORDER BY " + "CASE WHEN Map = '%s' THEN 0 ELSE 1 END, " + "CASE WHEN Map LIKE '%s%%' THEN 0 ELSE 1 END, " + "LENGTH(Map), " + "Map " + "LIMIT 1" + ") as l;", + pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), + pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), + pData->m_Name.ClrStr(), + pSqlServer->GetPrefix(), + pData->m_aFuzzyMap, + pData->m_RequestedMap.ClrStr(), + pData->m_RequestedMap.ClrStr() + ); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->rowsCount() != 1) { - str_format(aBuf, sizeof(aBuf), "No map like \"%s\" found.", pData->m_RequestedMap.Str()); + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "No map like \"%s\" found.", pData->m_RequestedMap.Str()); } else { pSqlServer->GetResults()->next(); - int points = pSqlServer->GetResults()->getInt("Points"); + int Points = pSqlServer->GetResults()->getInt("Points"); int Stars = pSqlServer->GetResults()->getInt("Stars"); int finishes = pSqlServer->GetResults()->getInt("Finishes"); int finishers = pSqlServer->GetResults()->getInt("Finishers"); @@ -468,27 +506,31 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData 0) { - str_format(aOwnFinishesString, sizeof(aOwnFinishesString), ", your time: %02d:%05.2f", (int)(ownTime/60), ownTime-((int)ownTime/60*60)); + str_format(aOwnFinishesString, sizeof(aOwnFinishesString), + ", your time: %02d:%05.2f", (int)(ownTime/60), ownTime-((int)ownTime/60*60) + ); } - str_format(aBuf, sizeof(aBuf), "\"%s\" by %s on %s, %s, %d %s%s, %d %s by %d %s%s%s", aMap, aMapper, aServer, aStars, points, points == 1 ? "point" : "points", aReleasedString, finishes, finishes == 1 ? "finish" : "finishes", finishers, finishers == 1 ? "tee" : "tees", aAverageString, aOwnFinishesString); + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "\"%s\" by %s on %s, %s, %d %s%s, %d %s by %d %s%s%s", + aMap, aMapper, aServer, aStars, + Points, Points == 1 ? "point" : "points", + aReleasedString, + finishes, finishes == 1 ? "finish" : "finishes", + finishers, finishers == 1 ? "tee" : "tees", + aAverageString, aOwnFinishesString + ); } - - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + pData->m_pResult->m_Done = true; return true; } catch (sql::SQLException &e) { dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Could not get Mapinfo"); + pData->m_pResult->m_Failed = true; + pData->m_pResult->m_Done = true; } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted mapinfo-thread due to reload/change of map."); - return true; - } - return false; - */ return false; } @@ -1378,22 +1420,22 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlDataexecuteSqlQuery(aBuf); - pData->m_pSqlResult->m_Tag = CSqlResult::RANDOM_MAP; + pData->m_pResult->m_Tag = CSqlResult::RANDOM_MAP; if(pSqlServer->GetResults()->rowsCount() != 1) { - pData->m_pSqlResult->m_MessageTarget = CSqlResult::DIRECT; - strncpy(pData->m_pSqlResult->m_Message, "No maps found on this server!", sizeof(pData->m_pSqlResult->m_Message)); - pData->m_pSqlResult->m_Variant.m_RandomMap.m_aMap[0] = 0; + pData->m_pResult->m_MessageTarget = CSqlResult::DIRECT; + strncpy(pData->m_pResult->m_Message, "No maps found on this server!", sizeof(pData->m_pResult->m_Message)); + pData->m_pResult->m_Variant.m_RandomMap.m_aMap[0] = 0; } else { pSqlServer->GetResults()->next(); std::string Map = pSqlServer->GetResults()->getString("Map"); - str_copy(pData->m_pSqlResult->m_Variant.m_RandomMap.m_aMap, Map.c_str(), sizeof(pData->m_pSqlResult->m_Variant.m_RandomMap.m_aMap)); + str_copy(pData->m_pResult->m_Variant.m_RandomMap.m_aMap, Map.c_str(), sizeof(pData->m_pResult->m_Variant.m_RandomMap.m_aMap)); } dbg_msg("sql", "voting random map done"); - pData->m_pSqlResult->m_Done = true; + pData->m_pResult->m_Done = true; return true; } catch (sql::SQLException &e) @@ -1764,8 +1806,8 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlDatam_pSqlResult->m_Message, - sizeof(pData->m_pSqlResult->m_Message), + str_format(pData->m_pResult->m_Message, + sizeof(pData->m_pResult->m_Message), "%s has %d save%s on %s%s", pData->m_Name.Str(), NumSaves, NumSaves == 1 ? "" : "s", pData->m_Map.Str(), aLastSavedString); } diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index ae5dc53dd..92243d4f3 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -13,6 +13,7 @@ class CSqlServer; class CSqlPlayerResult { public: + CSqlPlayerResult(); std::atomic_bool m_Done; bool m_Failed; @@ -21,7 +22,7 @@ public: DIRECT, ALL, } m_MessageTarget; - char m_Message[512][7]; + char m_aaMessages[7][512]; }; // result only valid if m_Done is set to true @@ -46,7 +47,6 @@ public: LOAD, RANDOM_MAP, MAP_VOTE, - MESSAGES, // relevant for TOP5 } m_Tag; union @@ -54,7 +54,6 @@ public: //CSaveTeam m_LoadTeam; CRandomMapResult m_RandomMap; CMapVoteResult m_MapVote; - char m_Messages[512][8]; // Space for extra messages } m_Variant; CSqlResult(); @@ -66,9 +65,9 @@ template < typename TResult > struct CSqlData { CSqlData(std::shared_ptr pSqlResult) : - m_pSqlResult(pSqlResult) + m_pResult(pSqlResult) { } - std::shared_ptr m_pSqlResult; + std::shared_ptr m_pResult; virtual ~CSqlData() = default; }; @@ -79,8 +78,9 @@ struct CSqlPlayerData : CSqlData }; // used for mapinfo -struct CSqlMapData : CSqlData +struct CSqlMapData : CSqlData { + using CSqlData::CSqlData; int m_ClientID; sqlstr::CSqlString<128> m_RequestedMap; From 91e286f54de81e2d0ec857632511eaaf0499569c Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 29 May 2020 00:11:27 +0200 Subject: [PATCH 15/44] Fix parsing responses from player --- src/game/server/player.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 774f0a477..1b7e5991b 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -147,6 +147,9 @@ void CPlayer::Tick() { #ifdef CONF_DEBUG if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies) +#endif +#if defined(CONF_SQL) + ProcessSqlResult(); #endif if(!Server()->ClientIngame(m_ClientID)) return; @@ -807,5 +810,6 @@ void CPlayer::ProcessSqlResult() break; } } + m_SqlQueryResult = nullptr; } #endif From eda0bab4bb25f73064024ef81a77bab05a3f3fcc Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 29 May 2020 14:45:05 +0200 Subject: [PATCH 16/44] Thread safe /rank --- src/game/server/score/sql_score.cpp | 66 ++++++++++++++++++----------- src/game/server/score/sql_score.h | 13 +++++- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 05d956bf4..a1cb4b785 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -188,7 +188,7 @@ bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameDa void CSqlScore::CheckBirthday(int ClientID) { /* - CSqlPlayerData *Tmp = new CSqlPlayerData(); + CSqlPlayerRequest *Tmp = new CSqlPlayerRequest(); Tmp->m_ClientID = ClientID; Tmp->m_Name = Server()->ClientName(ClientID); thread_init_and_detach(ExecSqlFunc, new CSqlExecData(CheckBirthdayThread, Tmp), "birthday check"); @@ -804,20 +804,22 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlDatam_ClientID = ClientID; - Tmp->m_Name = pName; - str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); + auto pResult = NewSqlPlayerResult(ClientID); + if(pResult == nullptr) + return; + CSqlPlayerRequest *Tmp = new CSqlPlayerRequest(pResult); + Tmp->m_Player = pName; + Tmp->m_Map = g_Config.m_SvMap; + Tmp->m_RequestingPlayer = Server()->ClientName(ClientID); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowRankThread, Tmp), "show rank"); - */ + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(ShowRankThread, Tmp), + "show rank"); } bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); if (HandleFailure) return true; @@ -830,14 +832,33 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); pSqlServer->executeSql("SET @rank := 1;"); pSqlServer->executeSql("SET @pos := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT Rank, Name, Time FROM (SELECT Name, (@pos := @pos+1) pos, (@rank := IF(@prev = Time,@rank, @pos)) rank, (@prev := Time) Time FROM (SELECT Name, min(Time) as Time FROM %s_race WHERE Map = '%s' GROUP BY Name ORDER BY `Time` ASC) as a) as b WHERE Name = '%s';", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT Rank, Name, Time " + "FROM (" + "SELECT " + "Name, (@pos := @pos+1) pos, " + "(@rank := IF(@prev = Time,@rank, @pos)) rank, " + "(@prev := Time) Time " + "FROM (" + "SELECT Name, min(Time) as Time " + "FROM %s_race " + "WHERE Map = '%s' " + "GROUP BY Name " + "ORDER BY `Time` ASC" + ") as a" + ") as b " + "WHERE Name = '%s';", + pSqlServer->GetPrefix(), + pData->m_Map.ClrStr(), + pData->m_Player.ClrStr() + ); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->rowsCount() != 1) { - str_format(aBuf, sizeof(aBuf), "%s is not ranked", pData->m_Name.Str()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "%s is not ranked", pData->m_Player.Str()); } else { @@ -847,17 +868,21 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->getInt("Rank"); if(g_Config.m_SvHideScore) { - str_format(aBuf, sizeof(aBuf), "Your time: %02d:%05.2f", (int)(Time/60), Time-((int)Time/60*60)); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "Your time: %02d:%05.2f", (int)(Time/60), Time-((int)Time/60*60)); } else { - str_format(aBuf, sizeof(aBuf), "%d. %s Time: %02d:%05.2f, requested by %s", Rank, pSqlServer->GetResults()->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60), pData->m_aRequestingPlayer); - pData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + pData->m_pResult->m_MessageTarget = CSqlPlayerResult::ALL; + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "%d. %s Time: %02d:%05.2f, requested by %s", + Rank, pSqlServer->GetResults()->getString("Name").c_str(), + (int)(Time/60), Time-((int)Time/60*60), pData->m_RequestingPlayer.Str()); } } dbg_msg("sql", "Showing rank done"); + pData->m_pResult->m_Done = true; return true; } catch (sql::SQLException &e) @@ -865,13 +890,6 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData sqlstr::CSqlString m_Name; }; +struct CSqlPlayerRequest : CSqlData +{ + using CSqlData::CSqlData; + sqlstr::CSqlString m_Player; + sqlstr::CSqlString m_Map; + sqlstr::CSqlString m_RequestingPlayer; +}; + // used for mapvote struct CSqlMapVoteData : CSqlMapData { @@ -186,8 +194,6 @@ class CSqlScore: public IScore static bool LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapInfoThread(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 ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -200,6 +206,9 @@ class CSqlScore: public IScore static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); 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); + CGameContext *GameServer() { return m_pGameServer; } IServer *Server() { return m_pServer; } From 68aa157c1150c24a5c6f981f09de2fb339a055ee Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 29 May 2020 16:38:48 +0200 Subject: [PATCH 17/44] Simplify /rank sql request --- src/game/server/score/sql_score.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index a1cb4b785..2afb14268 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -829,24 +829,15 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); - pSqlServer->executeSql("SET @rank := 1;"); - pSqlServer->executeSql("SET @pos := 0;"); str_format(aBuf, sizeof(aBuf), "SELECT Rank, Name, Time " "FROM (" - "SELECT " - "Name, (@pos := @pos+1) pos, " - "(@rank := IF(@prev = Time,@rank, @pos)) rank, " - "(@prev := Time) Time " - "FROM (" - "SELECT Name, min(Time) as Time " - "FROM %s_race " - "WHERE Map = '%s' " - "GROUP BY Name " - "ORDER BY `Time` ASC" - ") as a" - ") as b " + "SELECT RANK() OVER w AS Rank, Name, MIN(Time) AS Time " + "FROM %s_race " + "WHERE Map = '%s' " + "GROUP BY Name " + "WINDOW w AS (ORDER BY Time)" + ") as a " "WHERE Name = '%s';", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), @@ -881,7 +872,6 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_Done = true; return true; } From e311c391518efd448c0d8bcea81d583293c22908 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 29 May 2020 16:50:01 +0200 Subject: [PATCH 18/44] Remove unused parameters from IScore interface --- src/game/server/ddracechat.cpp | 15 ++++++--------- src/game/server/score.h | 6 +++--- src/game/server/score/file_score.cpp | 14 ++++++-------- src/game/server/score/file_score.h | 8 +++----- src/game/server/score/sql_score.cpp | 6 +++--- src/game/server/score/sql_score.h | 10 +++++----- 6 files changed, 26 insertions(+), 33 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 073edc943..79de8e55c 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -386,10 +386,9 @@ void CGameContext::ConTeamTop5(IConsole::IResult *pResult, void *pUserData) } if (pResult->NumArguments() > 0) - pSelf->Score()->ShowTeamTop5(pResult->m_ClientID, pUserData, - pResult->GetInteger(0)); + pSelf->Score()->ShowTeamTop5(pResult->m_ClientID, pResult->GetInteger(0)); else - pSelf->Score()->ShowTeamTop5(pResult->m_ClientID, pUserData); + pSelf->Score()->ShowTeamTop5(pResult->m_ClientID); #if defined(CONF_SQL) if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) @@ -417,10 +416,9 @@ void CGameContext::ConTop5(IConsole::IResult *pResult, void *pUserData) } if (pResult->NumArguments() > 0) - pSelf->Score()->ShowTop5(pResult->m_ClientID, pUserData, - pResult->GetInteger(0)); + pSelf->Score()->ShowTop5(pResult->m_ClientID, pResult->GetInteger(0)); else - pSelf->Score()->ShowTop5(pResult->m_ClientID, pUserData); + pSelf->Score()->ShowTop5(pResult->m_ClientID); #if defined(CONF_SQL) if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) @@ -1580,10 +1578,9 @@ void CGameContext::ConTopPoints(IConsole::IResult *pResult, void *pUserData) } if (pResult->NumArguments() > 0) - pSelf->Score()->ShowTopPoints(pResult->m_ClientID, pUserData, - pResult->GetInteger(0)); + pSelf->Score()->ShowTopPoints(pResult->m_ClientID, pResult->GetInteger(0)); else - pSelf->Score()->ShowTopPoints(pResult->m_ClientID, pUserData); + pSelf->Score()->ShowTopPoints(pResult->m_ClientID); if(pSelf->m_apPlayers[pResult->m_ClientID] && g_Config.m_SvUseSQL) pSelf->m_apPlayers[pResult->m_ClientID]->m_LastSQLQuery = pSelf->Server()->Tick(); diff --git a/src/game/server/score.h b/src/game/server/score.h index ed369ef3c..4d3feaca0 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -68,13 +68,13 @@ public: virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0; - virtual void ShowTop5(int ClientID, void *pUserData, int Offset=1) = 0; + virtual void ShowTop5(int ClientID, int Offset=1) = 0; virtual void ShowRank(int ClientID, const char *pName) = 0; - virtual void ShowTeamTop5(int ClientID, void *pUserData, int Offset=1) = 0; + virtual void ShowTeamTop5(int ClientID, int Offset=1) = 0; virtual void ShowTeamRank(int ClientID, const char *pName) = 0; - virtual void ShowTopPoints(int ClientID, void *pUserData, int Offset=1) = 0; + virtual void ShowTopPoints(int ClientID, int Offset=1) = 0; virtual void ShowPoints(int ClientID, const char *pName) = 0; virtual void RandomMap(int ClientID, int Stars) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 8ed2bb39a..8a6f4eb82 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -241,13 +241,11 @@ void CFileScore::SaveScore(int ClientID, float Time, const char *pTimestamp, UpdatePlayer(ClientID, Time, CpTime); } -void CFileScore::ShowTop5(int ClientID, - void *pUserData, int Offset) +void CFileScore::ShowTop5(int ClientID, int Offset) { - CGameContext *pSelf = (CGameContext *) pUserData; char aBuf[512]; Offset = maximum(1, Offset < 0 ? m_Top.size() + Offset - 3 : Offset); - pSelf->SendChatTarget(ClientID, "----------- Top 5 -----------"); + GameServer()->SendChatTarget(ClientID, "----------- Top 5 -----------"); for (int i = 0; i < 5; i++) { if (i + Offset > m_Top.size()) @@ -257,9 +255,9 @@ void CFileScore::ShowTop5(int ClientID, "%d. %s Time: %d minute(s) %5.2f second(s)", i + Offset, r->m_aName, (int)r->m_Score / 60, r->m_Score - ((int)r->m_Score / 60 * 60)); - pSelf->SendChatTarget(ClientID, aBuf); + GameServer()->SendChatTarget(ClientID, aBuf); } - pSelf->SendChatTarget(ClientID, "------------------------------"); + GameServer()->SendChatTarget(ClientID, "------------------------------"); } void CFileScore::ShowRank(int ClientID, const char* pName) @@ -296,7 +294,7 @@ void CFileScore::ShowRank(int ClientID, const char* pName) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTeamTop5(int ClientID, void *pUserData, int Offset) +void CFileScore::ShowTeamTop5(int ClientID, int Offset) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); @@ -310,7 +308,7 @@ void CFileScore::ShowTeamRank(int ClientID, const char* pName) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::ShowTopPoints(int ClientID, void *pUserData, int Offset) +void CFileScore::ShowTopPoints(int ClientID, int Offset) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers"); diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 4e51c32dc..0cc4b88e4 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -68,15 +68,13 @@ public: float CpTime[NUM_CHECKPOINTS], bool NotEligible); virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp); - virtual void ShowTop5(int ClientID, - void *pUserData, int Offset = 1); + virtual void ShowTop5(int ClientID, int Offset = 1); virtual void ShowRank(int ClientID, const char* pName); - virtual void ShowTeamTop5(int ClientID, - void *pUserData, int Offset = 1); + virtual void ShowTeamTop5(int ClientID, int Offset = 1); virtual void ShowTeamRank(int ClientID, const char* pName); - virtual void ShowTopPoints(int ClientID, void *pUserData, int Offset); + virtual void ShowTopPoints(int ClientID, int Offset); virtual void ShowPoints(int ClientID, const char* pName); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 2afb14268..8a7653949 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -974,7 +974,7 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -240,12 +240,12 @@ public: virtual void MapInfo(int ClientID, const char* MapName); virtual void ShowRank(int ClientID, const char* pName); virtual void ShowTeamRank(int ClientID, const char* pName); + virtual void ShowPoints(int ClientID, const char* pName); virtual void ShowTimes(int ClientID, const char* pName, int Offset = 1); virtual void ShowTimes(int ClientID, int Offset = 1); - virtual void ShowTop5(int ClientID, void *pUserData, int Offset = 1); - virtual void ShowTeamTop5(int ClientID, void *pUserData, int Offset = 1); - virtual void ShowPoints(int ClientID, const char* pName); - virtual void ShowTopPoints(int ClientID, void *pUserData, int Offset = 1); + virtual void ShowTop5(int ClientID, int Offset = 1); + virtual void ShowTeamTop5(int ClientID, int Offset = 1); + virtual void ShowTopPoints(int ClientID, int Offset = 1); virtual void GetSaves(int ClientID); // requested by teams From f18daa949c50709cc71ecaff6184b3acf1d90c12 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 29 May 2020 17:38:47 +0200 Subject: [PATCH 19/44] Thread safe /teamrank --- src/game/server/score/sql_score.cpp | 86 +++++++++++++++++++---------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 8a7653949..124a45fe5 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -820,9 +820,11 @@ void CSqlScore::ShowRank(int ClientID, const char* pName) bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { const CSqlPlayerRequest *pData = dynamic_cast(pGameData); - if (HandleFailure) + { + pData->m_pResult->m_Done = true; return true; + } try { @@ -873,6 +875,7 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_Done = true; + dbg_msg("sql", "Showing rank done"); return true; } catch (sql::SQLException &e) @@ -885,23 +888,27 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDatam_ClientID = ClientID; - Tmp->m_Name = pName; - str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); + auto pResult = NewSqlPlayerResult(ClientID); + if(pResult == nullptr) + return; + CSqlPlayerRequest *Tmp = new CSqlPlayerRequest(pResult); + Tmp->m_Player = pName; + Tmp->m_Map = g_Config.m_SvMap; + Tmp->m_RequestingPlayer = Server()->ClientName(ClientID); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTeamRankThread, Tmp), "show team rank"); - */ + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(ShowTeamRankThread, Tmp), + "show team rank"); } bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); - + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); if (HandleFailure) + { + pData->m_pResult->m_Done = true; return true; + } try { @@ -913,7 +920,37 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); pSqlServer->executeSql("SET @rank := 1;"); pSqlServer->executeSql("SET @pos := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT Rank, Name, Time FROM (SELECT Rank, l2.ID FROM ((SELECT ID, (@pos := @pos+1) pos, (@rank := IF(@prev = Time,@rank,@pos)) rank, (@prev := Time) Time FROM (SELECT ID, Time FROM %s_teamrace WHERE Map = '%s' GROUP BY ID ORDER BY Time) as ll) as l2) LEFT JOIN %s_teamrace as r2 ON l2.ID = r2.ID WHERE Map = '%s' AND Name = '%s' ORDER BY Rank LIMIT 1) as l LEFT JOIN %s_teamrace as r ON l.ID = r.ID ORDER BY Name;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), pSqlServer->GetPrefix()); + str_format(aBuf, sizeof(aBuf), + "SELECT Rank, Name, Time " + "FROM (" + "SELECT Rank, l2.ID " + "FROM ((" + "SELECT " + "ID, " + "(@pos := @pos+1) pos, " + "(@rank := IF(@prev = Time,@rank,@pos)) rank, " + "(@prev := Time) Time " + "FROM (" + "SELECT ID, Time " + "FROM %s_teamrace " + "WHERE Map = '%s' " + "GROUP BY ID " + "ORDER BY Time" + ") as ll) " + "as l2) " + "LEFT JOIN %s_teamrace as r2 ON l2.ID = r2.ID " + "WHERE Map = '%s' AND Name = '%s' " + "ORDER BY Rank LIMIT 1" + ") as l " + "LEFT JOIN %s_teamrace as r ON l.ID = r.ID " + "ORDER BY Name;", + pSqlServer->GetPrefix(), + pData->m_Map.ClrStr(), + pSqlServer->GetPrefix(), + pData->m_Map.ClrStr(), + pData->m_Player.ClrStr(), + pSqlServer->GetPrefix() + ); pSqlServer->executeSqlQuery(aBuf); @@ -921,13 +958,12 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDatam_Name.Str()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "%s has no team ranks", pData->m_Player.Str()); } else { pSqlServer->GetResults()->first(); - float Time = (float)pSqlServer->GetResults()->getDouble("Time"); int Rank = pSqlServer->GetResults()->getInt("Rank"); @@ -942,20 +978,21 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->first(); - if(g_Config.m_SvHideScore) { - str_format(aBuf, sizeof(aBuf), "Your team time: %02d:%05.02f", (int)(Time/60), Time-((int)Time/60*60)); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "Your team time: %02d:%05.02f", (int)(Time/60), Time-((int)Time/60*60)); } else { - str_format(aBuf, sizeof(aBuf), "%d. %s Team time: %02d:%05.02f, requested by %s", Rank, aNames, (int)(Time/60), Time-((int)Time/60*60), pData->m_aRequestingPlayer); - pData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + pData->m_pResult->m_MessageTarget = CSqlPlayerResult::ALL; + str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), + "%d. %s Team time: %02d:%05.02f, requested by %s", + Rank, aNames, (int)(Time/60), Time-((int)Time/60*60), pData->m_RequestingPlayer.Str()); } } + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing teamrank done"); return true; } @@ -964,13 +1001,6 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData Date: Fri, 29 May 2020 21:35:37 +0200 Subject: [PATCH 20/44] Reduce boilerplate code needed to start player info requesting thread --- src/game/server/score/sql_score.cpp | 86 +++++++++++++---------------- src/game/server/score/sql_score.h | 17 +++++- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 124a45fe5..b23533a8b 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -82,6 +82,27 @@ std::shared_ptr CSqlScore::NewSqlPlayerResult(int ClientID) return pCurPlayer->m_SqlQueryResult; } +void CSqlScore::ExecPlayerThread( + bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), + const char* pThreadName, + int ClientID, + const char* pName, + int Offset +) { + auto pResult = NewSqlPlayerResult(ClientID); + if(pResult == nullptr) + return; + CSqlPlayerRequest *Tmp = new CSqlPlayerRequest(pResult); + Tmp->m_Name = pName; + Tmp->m_Map = g_Config.m_SvMap; + Tmp->m_RequestingPlayer = Server()->ClientName(ClientID); + Tmp->m_Offset = Offset; + + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(pFuncPtr, Tmp), + pThreadName); +} + LOCK CSqlScore::ms_FailureFileLock = lock_create(); CSqlScore::CSqlScore(CGameContext *pGameServer) : @@ -399,32 +420,23 @@ bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData void CSqlScore::MapInfo(int ClientID, const char* MapName) { - auto pResult = NewSqlPlayerResult(ClientID); - if(pResult == nullptr) - return; - CSqlMapData *Tmp = new CSqlMapData(pResult); - - Tmp->m_ClientID = ClientID; - Tmp->m_RequestedMap = MapName; - Tmp->m_Name = Server()->ClientName(ClientID); - str_copy(Tmp->m_aFuzzyMap, MapName, sizeof(Tmp->m_aFuzzyMap)); - sqlstr::ClearString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); - sqlstr::FuzzyString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); - - thread_init_and_detach(CSqlExecData::ExecSqlFunc, - new CSqlExecData(MapInfoThread, Tmp), - "map info"); + ExecPlayerThread(MapInfoThread, "map info", ClientID, MapName, 0); } bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - const CSqlMapData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); if (HandleFailure) return true; try { + char aFuzzyMap[128]; + str_copy(aFuzzyMap, pData->m_Name.Str(), sizeof(aFuzzyMap)); + sqlstr::ClearString(aFuzzyMap, sizeof(aFuzzyMap)); + sqlstr::FuzzyString(aFuzzyMap, sizeof(aFuzzyMap)); + char aBuf[1024]; str_format(aBuf, sizeof(aBuf), "SELECT l.Map, l.Server, Mapper, Points, Stars, " @@ -446,18 +458,18 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), - pData->m_Name.ClrStr(), + pData->m_RequestingPlayer.ClrStr(), pSqlServer->GetPrefix(), - pData->m_aFuzzyMap, - pData->m_RequestedMap.ClrStr(), - pData->m_RequestedMap.ClrStr() + aFuzzyMap, + pData->m_Name.ClrStr(), + pData->m_Name.ClrStr() ); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->rowsCount() != 1) { str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), - "No map like \"%s\" found.", pData->m_RequestedMap.Str()); + "No map like \"%s\" found.", pData->m_Name.Str()); } else { @@ -804,17 +816,7 @@ bool CSqlScore::SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlDatam_Player = pName; - Tmp->m_Map = g_Config.m_SvMap; - Tmp->m_RequestingPlayer = Server()->ClientName(ClientID); - - thread_init_and_detach(CSqlExecData::ExecSqlFunc, - new CSqlExecData(ShowRankThread, Tmp), - "show rank"); + ExecPlayerThread(ShowRankThread, "show rank", ClientID, pName, 0); } bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) @@ -843,7 +845,7 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), pData->m_Map.ClrStr(), - pData->m_Player.ClrStr() + pData->m_Name.ClrStr() ); pSqlServer->executeSqlQuery(aBuf); @@ -851,7 +853,7 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->rowsCount() != 1) { str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), - "%s is not ranked", pData->m_Player.Str()); + "%s is not ranked", pData->m_Name.Str()); } else { @@ -888,17 +890,7 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDatam_Player = pName; - Tmp->m_Map = g_Config.m_SvMap; - Tmp->m_RequestingPlayer = Server()->ClientName(ClientID); - - thread_init_and_detach(CSqlExecData::ExecSqlFunc, - new CSqlExecData(ShowTeamRankThread, Tmp), - "show team rank"); + ExecPlayerThread(ShowTeamRankThread, "show team rank", ClientID, pName, 0); } bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) @@ -948,7 +940,7 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDatam_Map.ClrStr(), pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), - pData->m_Player.ClrStr(), + pData->m_Name.ClrStr(), pSqlServer->GetPrefix() ); @@ -959,7 +951,7 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), - "%s has no team ranks", pData->m_Player.Str()); + "%s has no team ranks", pData->m_Name.Str()); } else { diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index d14114831..55268caff 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -84,16 +84,19 @@ struct CSqlMapData : CSqlData int m_ClientID; sqlstr::CSqlString<128> m_RequestedMap; - char m_aFuzzyMap[128]; sqlstr::CSqlString m_Name; }; struct CSqlPlayerRequest : CSqlData { using CSqlData::CSqlData; - sqlstr::CSqlString m_Player; - sqlstr::CSqlString m_Map; + // object being requested, either map (128 bytes) or player (16 bytes) + sqlstr::CSqlString<128> m_Name; + // current map + sqlstr::CSqlString<128> m_Map; sqlstr::CSqlString m_RequestingPlayer; + // relevant for /top5 kind of requests + int m_Offset; }; // used for mapvote @@ -221,6 +224,14 @@ class CSqlScore: public IScore // returns new SqlResult bound to the player, if no current Thread is active for this player std::shared_ptr NewSqlPlayerResult(int ClientID); + // Creates for player database requests + void ExecPlayerThread( + bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), + const char* pThreadName, + int ClientID, + const char* pName, + int OffSet + ); public: // keeps track of score-threads From 9cf28354d7831104d388c0fd8d3d0fd0d6421f62 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 29 May 2020 21:58:21 +0200 Subject: [PATCH 21/44] Thread safe /top5 command --- src/game/server/score/sql_score.cpp | 72 ++++++++++++++++++----------- src/game/server/score/sql_score.h | 2 +- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index b23533a8b..fd5adb7fd 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -998,25 +998,22 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDatam_Num = Offset; - Tmp->m_ClientID = ClientID; - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTop5Thread, Tmp), "show top5"); - */ + ExecPlayerThread(ShowTop5Thread, "show top5", ClientID, "", Offset); } bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); if (HandleFailure) + { + pData->m_pResult->m_Failed = true; + pData->m_pResult->m_Done = true; return true; + } - int LimitStart = maximum(abs(pData->m_Num)-1, 0); - const char *pOrder = pData->m_Num >= 0 ? "ASC" : "DESC"; + int LimitStart = maximum(abs(pData->m_Offset)-1, 0); + const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC"; try { @@ -1025,24 +1022,49 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); pSqlServer->executeSql("SET @rank := 1;"); pSqlServer->executeSql("SET @pos := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, Rank FROM (SELECT Name, (@pos := @pos+1) pos, (@rank := IF(@prev = Time,@rank, @pos)) Rank, (@prev := Time) Time FROM (SELECT Name, min(Time) as Time FROM %s_race WHERE Map = '%s' GROUP BY Name ORDER BY `Time` ASC) as a) as b ORDER BY Rank %s LIMIT %d, 5;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pOrder, LimitStart); + str_format(aBuf, sizeof(aBuf), + "SELECT Name, Time, Rank " + "FROM (" + "SELECT " + "Name, " + "(@pos := @pos+1) pos, " + "(@rank := IF(@prev = Time,@rank, @pos)) Rank, " + "(@prev := Time) Time " + "FROM (" + "SELECT Name, min(Time) as Time " + "FROM %s_race " + "WHERE Map = '%s' " + "GROUP BY Name " + "ORDER BY `Time` ASC" + ") as a" + ") as b " + "ORDER BY Rank %s " + "LIMIT %d, 5;", + pSqlServer->GetPrefix(), + pData->m_Map.ClrStr(), + pOrder, + LimitStart + ); pSqlServer->executeSqlQuery(aBuf); // show top5 - pData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------"); + strcpy(pData->m_pResult->m_aaMessages[0], "----------- Top 5 -----------"); - int Rank = 0; - float Time = 0; + int Line = 1; while(pSqlServer->GetResults()->next()) { - Time = (float)pSqlServer->GetResults()->getDouble("Time"); - Rank = (float)pSqlServer->GetResults()->getInt("Rank"); - str_format(aBuf, sizeof(aBuf), "%d. %s Time: %02d:%05.2f", Rank, pSqlServer->GetResults()->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60)); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - //Rank++; + float Time = (float)pSqlServer->GetResults()->getDouble("Time"); + int Rank = pSqlServer->GetResults()->getInt("Rank"); + str_format(pData->m_pResult->m_aaMessages[Line], sizeof(pData->m_pResult->m_aaMessages[0]), + "%d. %s Time: %02d:%05.2f", + Rank, pSqlServer->GetResults()->getString("Name").c_str(), + (int)(Time/60), Time-((int)Time/60*60) + ); + Line++; } - pData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); + strcpy(pData->m_pResult->m_aaMessages[Line], "-------------------------------"); + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing top5 done"); return true; } @@ -1051,13 +1073,7 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData Date: Fri, 29 May 2020 22:02:05 +0200 Subject: [PATCH 22/44] Simplify /top5 SQL statement --- src/game/server/score/sql_score.cpp | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index fd5adb7fd..abe8e24ec 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1019,25 +1019,15 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); - pSqlServer->executeSql("SET @rank := 1;"); - pSqlServer->executeSql("SET @pos := 0;"); str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, Rank " "FROM (" - "SELECT " - "Name, " - "(@pos := @pos+1) pos, " - "(@rank := IF(@prev = Time,@rank, @pos)) Rank, " - "(@prev := Time) Time " - "FROM (" - "SELECT Name, min(Time) as Time " - "FROM %s_race " - "WHERE Map = '%s' " - "GROUP BY Name " - "ORDER BY `Time` ASC" - ") as a" - ") as b " + "SELECT RANK() OVER w AS Rank, Name, MIN(Time) AS Time " + "FROM %s_race " + "WHERE Map = '%s' " + "GROUP BY Name " + "WINDOW w AS (ORDER BY Time)" + ") as a " "ORDER BY Rank %s " "LIMIT %d, 5;", pSqlServer->GetPrefix(), From 83fbee5f815398eefa90f179e1d774699a184263 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sat, 30 May 2020 12:30:52 +0200 Subject: [PATCH 23/44] Optimize /teamrank database query --- src/game/server/score/sql_score.cpp | 43 ++++++++++++----------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index abe8e24ec..db85f0fec 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -909,39 +909,30 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); - pSqlServer->executeSql("SET @rank := 1;"); - pSqlServer->executeSql("SET @pos := 0;"); str_format(aBuf, sizeof(aBuf), - "SELECT Rank, Name, Time " - "FROM (" - "SELECT Rank, l2.ID " - "FROM ((" - "SELECT " - "ID, " - "(@pos := @pos+1) pos, " - "(@rank := IF(@prev = Time,@rank,@pos)) rank, " - "(@prev := Time) Time " - "FROM (" - "SELECT ID, Time " - "FROM %s_teamrace " - "WHERE Map = '%s' " - "GROUP BY ID " - "ORDER BY Time" - ") as ll) " - "as l2) " - "LEFT JOIN %s_teamrace as r2 ON l2.ID = r2.ID " - "WHERE Map = '%s' AND Name = '%s' " - "ORDER BY Rank LIMIT 1" + "SELECT Time, Rank, Name " + "FROM (" // teamrank score board + "SELECT RANK() OVER w AS Rank, Id " + "FROM %s_teamrace " + "WHERE Map = '%s' " + "GROUP BY Id " + "WINDOW w AS (ORDER BY Time)" ") as l " - "LEFT JOIN %s_teamrace as r ON l.ID = r.ID " + "INNER JOIN %s_teamrace as r ON l.ID = r.ID " + "WHERE l.ID = (" // find id for top teamrank of player + "SELECT Id " + "FROM %s_teamrace " + "WHERE Map = '%s' AND Name = '%s' " + "ORDER BY Time " + "LIMIT 1" + ") " "ORDER BY Name;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pSqlServer->GetPrefix(), + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), - pData->m_Name.ClrStr(), - pSqlServer->GetPrefix() + pData->m_Name.ClrStr() ); pSqlServer->executeSqlQuery(aBuf); From 1d7289c8f082eea7224d40a22eaaef814a5e6e88 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sat, 30 May 2020 15:09:24 +0200 Subject: [PATCH 24/44] Thread safe /top5team and optimize database query --- src/game/server/score/sql_score.cpp | 122 +++++++++++----------------- 1 file changed, 48 insertions(+), 74 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index db85f0fec..8ad60243d 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -995,7 +995,6 @@ void CSqlScore::ShowTop5(int ClientID, int Offset) bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { const CSqlPlayerRequest *pData = dynamic_cast(pGameData); - if (HandleFailure) { pData->m_pResult->m_Failed = true; @@ -1060,98 +1059,79 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlDatam_Num = Offset; - Tmp->m_ClientID = ClientID; - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTeamTop5Thread, Tmp), "show team top5"); - */ + ExecPlayerThread(ShowTeamTop5Thread, "show team top5", ClientID, "", Offset); } bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); - + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) + { + pData->m_pResult->m_Failed = true; + pData->m_pResult->m_Done = true; return true; + } - int LimitStart = maximum(abs(pData->m_Num)-1, 0); - const char *pOrder = pData->m_Num >= 0 ? "ASC" : "DESC"; + int LimitStart = maximum(abs(pData->m_Offset)-1, 0); + const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC"; try { // check sort method - char aBuf[2400]; + char aBuf[512]; - pSqlServer->executeSql("SET @prev := NULL;"); - pSqlServer->executeSql("SET @previd := NULL;"); - pSqlServer->executeSql("SET @rank := 1;"); pSqlServer->executeSql("SET @pos := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT ID, Name, Time, Rank FROM (SELECT r.ID, Name, Rank, l.Time FROM ((SELECT ID, Rank, Time FROM (SELECT ID, (@pos := IF(@previd = ID,@pos,@pos+1)) pos, (@previd := ID), (@rank := IF(@prev = Time,@rank,@pos)) Rank, (@prev := Time) Time FROM (SELECT ID, MIN(Time) as Time FROM %s_teamrace WHERE Map = '%s' GROUP BY ID ORDER BY `Time` ASC) as all_top_times) as a ORDER BY Rank %s LIMIT %d, 5) as l) LEFT JOIN %s_teamrace as r ON l.ID = r.ID ORDER BY Time ASC, r.ID, Name ASC) as a;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pOrder, LimitStart, pSqlServer->GetPrefix()); + str_format(aBuf, sizeof(aBuf), + "SELECT Name, Time, Rank, TeamSize " + "FROM (" // limit to 5 + "SELECT Rank, ID, TeamSize " + "FROM (" // teamrank score board + "SELECT RANK() OVER w AS Rank, ID, COUNT(*) AS Teamsize " + "FROM %s_teamrace " + "WHERE Map = '%s' " + "GROUP BY Id " + "WINDOW w AS (ORDER BY Time)" + ") as l1 " + "ORDER BY Rank %s " + "LIMIT %d, 5" + ") as l2 " + "INNER JOIN %s_teamrace as r ON l2.ID = r.ID " + "ORDER BY Rank %s, r.ID, Name ASC;", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pOrder, LimitStart, pSqlServer->GetPrefix(), pOrder + ); pSqlServer->executeSqlQuery(aBuf); // show teamtop5 - pData->GameServer()->SendChatTarget(pData->m_ClientID, "------- Team Top 5 -------"); - - int Rows = pSqlServer->GetResults()->rowsCount(); - - if (Rows >= 1) + pSqlServer->GetResults()->first(); + strcpy(paMessages[0], "------- Team Top 5 -------"); + int Line; + for(Line = 1; Line < 6; Line++) // print { - char aID[17]; - char aID2[17]; - char aNames[2300]; - int Rank = 0; - float Time = 0; - int aCuts[320]; // 64 * 5 - int CutPos = 0; + if(pSqlServer->GetResults()->isAfterLast()) + break; + int TeamSize = pSqlServer->GetResults()->getInt("TeamSize"); + float Time = (float)pSqlServer->GetResults()->getDouble("Time"); + int Rank = pSqlServer->GetResults()->getInt("Rank"); + printf("%d", TeamSize); - aNames[0] = '\0'; - aCuts[0] = -1; - - pSqlServer->GetResults()->first(); - strcpy(aID, pSqlServer->GetResults()->getString("ID").c_str()); - for(int Row = 0; Row < Rows; Row++) + char aNames[2300] = { 0 }; + for(int i = 0; i < TeamSize; i++) { - strcpy(aID2, pSqlServer->GetResults()->getString("ID").c_str()); - if (str_comp(aID, aID2) != 0) - { - strcpy(aID, aID2); - aCuts[CutPos++] = Row - 1; - } - pSqlServer->GetResults()->next(); - } - aCuts[CutPos] = Rows - 1; - - CutPos = 0; - pSqlServer->GetResults()->first(); - for(int Row = 0; Row < Rows; Row++) - { - str_append(aNames, pSqlServer->GetResults()->getString("Name").c_str(), sizeof(aNames)); - - if (Row < aCuts[CutPos] - 1) + auto Name = pSqlServer->GetResults()->getString("Name"); + str_append(aNames, Name.c_str(), sizeof(aNames)); + if (i < TeamSize - 2) str_append(aNames, ", ", sizeof(aNames)); - else if (Row < aCuts[CutPos]) + else if (i == TeamSize - 2) str_append(aNames, " & ", sizeof(aNames)); - - Time = (float)pSqlServer->GetResults()->getDouble("Time"); - Rank = (float)pSqlServer->GetResults()->getInt("Rank"); - - if (Row == aCuts[CutPos]) - { - str_format(aBuf, sizeof(aBuf), "%d. %s Team Time: %02d:%05.2f", Rank, aNames, (int)(Time/60), Time-((int)Time/60*60)); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - CutPos++; - aNames[0] = '\0'; - } - pSqlServer->GetResults()->next(); } + str_format(paMessages[Line], sizeof(paMessages[0]), "%d. %s Team Time: %02d:%05.2f", + Rank, aNames, (int)(Time/60), Time-((int)Time/60*60)); } + strcpy(paMessages[Line], "-------------------------------"); - pData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); - + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing teamtop5 done"); return true; } @@ -1160,12 +1140,6 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData Date: Sat, 30 May 2020 17:41:34 +0200 Subject: [PATCH 25/44] Reworked PlayerResult to signal finished with use_count == 1 Therefore if the sql thread crashes, the result is deleted automatically --- src/game/server/player.cpp | 26 +++++++++++++++----------- src/game/server/score/sql_score.cpp | 11 ----------- src/game/server/score/sql_score.h | 2 -- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 1b7e5991b..3f0e91e48 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -794,20 +794,24 @@ void CPlayer::SpectatePlayerName(const char *pName) #if defined(CONF_SQL) void CPlayer::ProcessSqlResult() { - if(m_SqlQueryResult == nullptr || !m_SqlQueryResult->m_Done) + if(m_SqlQueryResult == nullptr || m_SqlQueryResult.use_count() != 1) return; - for(int i = 0; i < (int)(sizeof(m_SqlQueryResult->m_aaMessages)/sizeof(m_SqlQueryResult->m_aaMessages[0])); i++) + + if(m_SqlQueryResult->m_Done) // sql was query successful { - if(m_SqlQueryResult->m_aaMessages[i][0] == 0) - break; - switch(m_SqlQueryResult->m_MessageTarget) + for(int i = 0; i < (int)(sizeof(m_SqlQueryResult->m_aaMessages)/sizeof(m_SqlQueryResult->m_aaMessages[0])); i++) { - case CSqlPlayerResult::DIRECT: - GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_aaMessages[i]); - break; - case CSqlPlayerResult::ALL: - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_aaMessages[i]); - break; + if(m_SqlQueryResult->m_aaMessages[i][0] == 0) + break; + switch(m_SqlQueryResult->m_MessageTarget) + { + case CSqlPlayerResult::DIRECT: + GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_aaMessages[i]); + break; + case CSqlPlayerResult::ALL: + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_aaMessages[i]); + break; + } } } m_SqlQueryResult = nullptr; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 8ad60243d..1c1dabb46 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -21,7 +21,6 @@ std::atomic_int CSqlScore::ms_InstanceCount(0); CSqlPlayerResult::CSqlPlayerResult() : m_Done(0), - m_Failed(0), m_MessageTarget(DIRECT) { for(int i = 0; i < (int)(sizeof(m_aaMessages)/sizeof(m_aaMessages[0])); i++) @@ -540,8 +539,6 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_Failed = true; - pData->m_pResult->m_Done = true; } return false; } @@ -996,11 +993,7 @@ bool CSqlScore::ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData(pGameData); if (HandleFailure) - { - pData->m_pResult->m_Failed = true; - pData->m_pResult->m_Done = true; return true; - } int LimitStart = maximum(abs(pData->m_Offset)-1, 0); const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC"; @@ -1067,11 +1060,7 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData(pGameData); auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) - { - pData->m_pResult->m_Failed = true; - pData->m_pResult->m_Done = true; return true; - } int LimitStart = maximum(abs(pData->m_Offset)-1, 0); const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC"; diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 046e80bd0..0185cbf4a 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -15,7 +15,6 @@ class CSqlPlayerResult { public: CSqlPlayerResult(); std::atomic_bool m_Done; - bool m_Failed; enum { @@ -30,7 +29,6 @@ class CSqlResult { public: std::atomic_bool m_Done; - bool m_Failed; // specify where chat messages should be returned enum { From e37cf5f3cb353d49d02d80191500b3c4fd1a7937 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sat, 30 May 2020 18:14:21 +0200 Subject: [PATCH 26/44] Thread safe /times --- src/game/server/score/sql_score.cpp | 108 ++++++++++++++-------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 1c1dabb46..f0d7698d9 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1134,91 +1134,102 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlDatam_Num = Offset; - Tmp->m_ClientID = ClientID; - Tmp->m_Search = false; - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTimesThread, Tmp), "show times"); - */ + ExecPlayerThread(ShowTimesThread, "show times", ClientID, "", Offset); } void CSqlScore::ShowTimes(int ClientID, const char* pName, int Offset) { - /* - CSqlScoreData *Tmp = new CSqlScoreData(); - Tmp->m_Num = Offset; - Tmp->m_ClientID = ClientID; - Tmp->m_Name = pName; - Tmp->m_Search = true; - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTimesThread, Tmp), "show name's times"); - */ + ExecPlayerThread(ShowTimesThread, "show times", ClientID, pName, Offset); } bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) return true; - int LimitStart = maximum(abs(pData->m_Num)-1, 0); - const char *pOrder = pData->m_Num >= 0 ? "DESC" : "ASC"; + int LimitStart = maximum(abs(pData->m_Offset)-1, 0); + const char *pOrder = pData->m_Offset >= 0 ? "DESC" : "ASC"; try { char aBuf[512]; - if(pData->m_Search) // last 5 times of a player - str_format(aBuf, sizeof(aBuf), "SELECT Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_race WHERE Map = '%s' AND Name = '%s' ORDER BY Timestamp %s LIMIT %d, 5;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), pOrder, LimitStart); - else// last 5 times of server - str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_race WHERE Map = '%s' ORDER BY Timestamp %s LIMIT %d, 5;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pOrder, LimitStart); - + if(pData->m_Name.Str()[0] != '\0') // last 5 times of a player + { + str_format(aBuf, sizeof(aBuf), + "SELECT Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp " + "FROM %s_race " + "WHERE Map = '%s' AND Name = '%s' " + "ORDER BY Timestamp %s " + "LIMIT %d, 5;", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), pOrder, LimitStart + ); + } + else // last 5 times of server + { + str_format(aBuf, sizeof(aBuf), + "SELECT Name, Time, " + "UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, " + "UNIX_TIMESTAMP(Timestamp) as Stamp " + "FROM %s_race " + "WHERE Map = '%s' " + "ORDER BY Timestamp %s " + "LIMIT %d, 5;", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pOrder, LimitStart + ); + } pSqlServer->executeSqlQuery(aBuf); // show top5 if(pSqlServer->GetResults()->rowsCount() == 0) { - pData->GameServer()->SendChatTarget(pData->m_ClientID, "There are no times in the specified range"); + strcpy(paMessages[0], "There are no times in the specified range"); + pData->m_pResult->m_Done = true; return true; } - pData->GameServer()->SendChatTarget(pData->m_ClientID, "------------- Last Times -------------"); - - float pTime = 0; - int pSince = 0; - int pStamp = 0; - + strcpy(paMessages[0], "------------- Last Times -------------"); + int Line = 1; while(pSqlServer->GetResults()->next()) { char aAgoString[40] = "\0"; - pSince = pSqlServer->GetResults()->getInt("Ago"); - pStamp = pSqlServer->GetResults()->getInt("Stamp"); - pTime = (float)pSqlServer->GetResults()->getDouble("Time"); + int pSince = pSqlServer->GetResults()->getInt("Ago"); + int pStamp = pSqlServer->GetResults()->getInt("Stamp"); + float pTime = (float)pSqlServer->GetResults()->getDouble("Time"); - sqlstr::AgoTimeToString(pSince,aAgoString); + sqlstr::AgoTimeToString(pSince, aAgoString); - if(pData->m_Search) // last 5 times of a player + if(pData->m_Name.Str()[0] != '\0') // last 5 times of a player { if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet - str_format(aBuf, sizeof(aBuf), "%02d:%05.02f, don't know how long ago", (int)(pTime/60), pTime-((int)pTime/60*60)); + str_format(paMessages[Line], sizeof(paMessages[0]), + "%02d:%05.02f, don't know how long ago", + (int)(pTime/60), pTime-((int)pTime/60*60)); else - str_format(aBuf, sizeof(aBuf), "%s ago, %02d:%05.02f", aAgoString, (int)(pTime/60), pTime-((int)pTime/60*60)); + str_format(paMessages[Line], sizeof(paMessages[0]), + "%s ago, %02d:%05.02f", + aAgoString, (int)(pTime/60), pTime-((int)pTime/60*60)); } else // last 5 times of the server { + auto Name = pSqlServer->GetResults()->getString("Name"); if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet - str_format(aBuf, sizeof(aBuf), "%s, %02d:%05.02f, don't know when", pSqlServer->GetResults()->getString("Name").c_str(), (int)(pTime/60), pTime-((int)pTime/60*60)); + str_format(paMessages[Line], sizeof(paMessages[0]), + "%s, %02d:%05.02f, don't know when", + Name.c_str(), (int)(pTime/60), pTime-((int)pTime/60*60)); else - str_format(aBuf, sizeof(aBuf), "%s, %s ago, %02d:%05.02f", pSqlServer->GetResults()->getString("Name").c_str(), aAgoString, (int)(pTime/60), pTime-((int)pTime/60*60)); + str_format(paMessages[Line], sizeof(paMessages[0]), + "%s, %s ago, %02d:%05.02f", + Name.c_str(), aAgoString, (int)(pTime/60), pTime-((int)pTime/60*60)); } - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + Line++; } - pData->GameServer()->SendChatTarget(pData->m_ClientID, "----------------------------------------------------"); + strcpy(paMessages[Line], "----------------------------------------------------"); + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing times done"); return true; } @@ -1226,14 +1237,7 @@ bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->rowsCount() != 1) { pData->m_pResult->m_MessageTarget = CSqlResult::DIRECT; - strncpy(pData->m_pResult->m_Message, "No maps found on this server!", sizeof(pData->m_pResult->m_Message)); + str_copy(pData->m_pResult->m_Message, "No maps found on this server!", sizeof(pData->m_pResult->m_Message)); pData->m_pResult->m_Variant.m_RandomMap.m_aMap[0] = 0; } else From 0f3f845a7f664caefd6cfbd932cdf0c24307674a Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sat, 30 May 2020 18:22:56 +0200 Subject: [PATCH 27/44] Thread safe /points --- src/game/server/score/sql_score.cpp | 53 ++++++++++++++++------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index f0d7698d9..4f5475d5e 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1243,20 +1243,13 @@ bool CSqlScore::ShowTimesThread(CSqlServer* pSqlServer, const CSqlDatam_ClientID = ClientID; - Tmp->m_Name = pName; - str_copy(Tmp->m_aRequestingPlayer, Server()->ClientName(ClientID), sizeof(Tmp->m_aRequestingPlayer)); - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowPointsThread, Tmp), "show points"); - */ + ExecPlayerThread(ShowPointsThread, "show points", ClientID, pName, 0); } bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) return true; @@ -1268,23 +1261,43 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @pos := 0;"); char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "SELECT Rank, Points, Name FROM (SELECT Name, (@pos := @pos+1) pos, (@rank := IF(@prev = Points, @rank, @pos)) Rank, (@prev := Points) Points FROM (SELECT Name, Points FROM %s_points GROUP BY Name ORDER BY Points DESC) as a) as b where Name = '%s';", pSqlServer->GetPrefix(), pData->m_Name.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT Rank, Points, Name " + "FROM (" + "SELECT Name, " + "(@pos := @pos+1) pos, " + "(@rank := IF(@prev = Points, @rank, @pos)) Rank, " + "(@prev := Points) Points " + "FROM (" + "SELECT Name, Points " + "FROM %s_points " + "GROUP BY Name " + "ORDER BY Points DESC" + ") as a" + ") as b " + "WHERE Name = '%s';", + pSqlServer->GetPrefix(), pData->m_Name.ClrStr() + ); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->rowsCount() != 1) { - str_format(aBuf, sizeof(aBuf), "%s has not collected any points so far", pData->m_Name.Str()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + str_format(paMessages[0], sizeof(paMessages[0]), + "%s has not collected any points so far", pData->m_Name.Str()); } else { pSqlServer->GetResults()->next(); - int count = pSqlServer->GetResults()->getInt("Points"); - int rank = pSqlServer->GetResults()->getInt("Rank"); - str_format(aBuf, sizeof(aBuf), "%d. %s Points: %d, requested by %s", rank, pSqlServer->GetResults()->getString("Name").c_str(), count, pData->m_aRequestingPlayer); - pData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); + int Count = pSqlServer->GetResults()->getInt("Points"); + int Rank = pSqlServer->GetResults()->getInt("Rank"); + auto Name = pSqlServer->GetResults()->getString("Name"); + pData->m_pResult->m_MessageTarget = CSqlPlayerResult::ALL; + str_format(paMessages[0], sizeof(paMessages[0]), + "%d. %s Points: %d, requested by %s", + Rank, Name.c_str(), Count, pData->m_RequestingPlayer.Str()); } + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing points done"); return true; } @@ -1293,12 +1306,6 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlData Date: Sat, 30 May 2020 18:30:04 +0200 Subject: [PATCH 28/44] Optimize /points database query --- src/game/server/score/sql_score.cpp | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 4f5475d5e..8ea1b11cd 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1256,25 +1256,14 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); - pSqlServer->executeSql("SET @rank := 1;"); - pSqlServer->executeSql("SET @pos := 0;"); - char aBuf[512]; str_format(aBuf, sizeof(aBuf), "SELECT Rank, Points, Name " "FROM (" - "SELECT Name, " - "(@pos := @pos+1) pos, " - "(@rank := IF(@prev = Points, @rank, @pos)) Rank, " - "(@prev := Points) Points " - "FROM (" - "SELECT Name, Points " - "FROM %s_points " - "GROUP BY Name " - "ORDER BY Points DESC" - ") as a" - ") as b " + "SELECT RANK() OVER w AS Rank, Points, Name " + "FROM %s_points " + "WINDOW w as (ORDER BY Points DESC)" + ") as a " "WHERE Name = '%s';", pSqlServer->GetPrefix(), pData->m_Name.ClrStr() ); From fa8972deb6dfec5d33bad293ffcbd39020ba2ac5 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sat, 30 May 2020 18:37:38 +0200 Subject: [PATCH 29/44] Thread safe /top5points --- src/game/server/score/sql_score.cpp | 56 +++++++++++++++++------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 8ea1b11cd..fcbe407cd 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1300,25 +1300,19 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlDatam_Num = Offset; - Tmp->m_ClientID = ClientID; - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(ShowTopPointsThread, Tmp), "show top points"); - */ + ExecPlayerThread(ShowTopPointsThread, "show top points", ClientID, "", Offset); } bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlScoreData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) return true; - int LimitStart = maximum(abs(pData->m_Num)-1, 0); - const char *pOrder = pData->m_Num >= 0 ? "ASC" : "DESC"; + int LimitStart = maximum(abs(pData->m_Offset)-1, 0); + const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC"; try { @@ -1326,20 +1320,43 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); pSqlServer->executeSql("SET @rank := 1;"); pSqlServer->executeSql("SET @pos := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT Rank, Points, Name FROM (SELECT Name, (@pos := @pos+1) pos, (@rank := IF(@prev = Points,@rank, @pos)) Rank, (@prev := Points) Points FROM (SELECT Name, Points FROM %s_points GROUP BY Name ORDER BY Points DESC) as a) as b ORDER BY Rank %s LIMIT %d, 5;", pSqlServer->GetPrefix(), pOrder, LimitStart); + str_format(aBuf, sizeof(aBuf), + "SELECT Rank, Points, Name " + "FROM (" + "SELECT Name, " + "(@pos := @pos+1) pos, " + "(@rank := IF(@prev = Points,@rank, @pos)) Rank, " + "(@prev := Points) Points " + "FROM (" + "SELECT Name, Points " + "FROM %s_points " + "GROUP BY Name " + "ORDER BY Points DESC" + ") as a" + ") as b " + "ORDER BY Rank %s " + "LIMIT %d, 5;", + pSqlServer->GetPrefix(), pOrder, LimitStart + ); pSqlServer->executeSqlQuery(aBuf); // show top points - pData->GameServer()->SendChatTarget(pData->m_ClientID, "-------- Top Points --------"); + strcpy(paMessages[0], "-------- Top Points --------"); + int Line = 1; while(pSqlServer->GetResults()->next()) { - str_format(aBuf, sizeof(aBuf), "%d. %s Points: %d", pSqlServer->GetResults()->getInt("Rank"), pSqlServer->GetResults()->getString("Name").c_str(), pSqlServer->GetResults()->getInt("Points")); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); + int Rank = pSqlServer->GetResults()->getInt("Rank"); + auto Name = pSqlServer->GetResults()->getString("Name"); + int Points = pSqlServer->GetResults()->getInt("Points"); + str_format(paMessages[Line], sizeof(paMessages[0]), + "%d. %s Points: %d", Rank, Name.c_str(), Points); + Line++; } - pData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------"); + strcpy(paMessages[Line], "-------------------------------"); + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing toppoints done"); return true; } @@ -1348,13 +1365,6 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData Date: Sat, 30 May 2020 18:42:40 +0200 Subject: [PATCH 30/44] Optimize /top5points SQL query --- src/game/server/score/sql_score.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index fcbe407cd..7a74e036d 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1317,23 +1317,13 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @prev := NULL;"); - pSqlServer->executeSql("SET @rank := 1;"); - pSqlServer->executeSql("SET @pos := 0;"); str_format(aBuf, sizeof(aBuf), "SELECT Rank, Points, Name " "FROM (" - "SELECT Name, " - "(@pos := @pos+1) pos, " - "(@rank := IF(@prev = Points,@rank, @pos)) Rank, " - "(@prev := Points) Points " - "FROM (" - "SELECT Name, Points " - "FROM %s_points " - "GROUP BY Name " - "ORDER BY Points DESC" - ") as a" - ") as b " + "SELECT RANK() OVER w AS Rank, Points, Name " + "FROM %s_points " + "WINDOW w as (ORDER BY Points DESC)" + ") as a " "ORDER BY Rank %s " "LIMIT %d, 5;", pSqlServer->GetPrefix(), pOrder, LimitStart From f5ebbf59b6e26e81cea65a2767f26ae4ac33260a Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sat, 30 May 2020 18:55:19 +0200 Subject: [PATCH 31/44] Thread safe /load without paramters --- src/game/server/score/sql_score.cpp | 34 +++++++++++++++-------------- src/game/server/score/sql_score.h | 5 ----- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 7a74e036d..7deb8a4f8 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1760,19 +1760,13 @@ bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlDatam_ClientID = ClientID; - Tmp->m_Name = Server()->ClientName(ClientID); - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(GetSavesThread, Tmp, false), "get saves"); - */ + ExecPlayerThread(GetSavesThread, "get saves", ClientID, "", 0); } bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlGetSavesData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) return true; @@ -1781,7 +1775,15 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT COUNT(*) as NumSaves, " + "UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(max(Timestamp)) as Ago " + "FROM %s_saves " + "WHERE Map='%s' AND Savegame regexp '\\n%s\\t';", + pSqlServer->GetPrefix(), + pData->m_Map.ClrStr(), + pData->m_RequestingPlayer.ClrStr() + ); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->next()) { @@ -1796,11 +1798,14 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_Message, - sizeof(pData->m_pResult->m_Message), - "%s has %d save%s on %s%s", pData->m_Name.Str(), NumSaves, NumSaves == 1 ? "" : "s", pData->m_Map.Str(), aLastSavedString); + str_format(paMessages[0], sizeof(paMessages[0]), + "%s has %d save%s on %s%s", + pData->m_RequestingPlayer.Str(), + NumSaves, NumSaves == 1 ? "" : "s", + pData->m_Map.Str(), aLastSavedString); } + pData->m_pResult->m_Done = true; dbg_msg("sql", "Showing saves done"); return true; } @@ -1808,11 +1813,8 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlDataGameServer()->SendChatTarget(pData->m_ClientID, "MySQL Error: Could not get saves"); } return false; - */ - return false; } #endif diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 0185cbf4a..5cce6348f 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -147,11 +147,6 @@ struct CSqlTeamLoad : CSqlData char m_ClientName[MAX_NAME_LENGTH]; }; -struct CSqlGetSavesData: CSqlData -{ - sqlstr::CSqlString m_Name; -}; - struct CSqlRandomMap : CSqlData { using CSqlData::CSqlData; From 031ac52320d94d757f066d5905dede2306a3d79b Mon Sep 17 00:00:00 2001 From: Zwelf Date: Tue, 2 Jun 2020 16:27:31 +0200 Subject: [PATCH 32/44] Thread safe /save --- src/engine/server/sql_server.cpp | 2 +- src/game/server/ddracechat.cpp | 4 +- src/game/server/entities/character.cpp | 26 ++- src/game/server/gamemodes/DDRace.cpp | 3 + src/game/server/player.cpp | 42 ++--- src/game/server/player.h | 2 - src/game/server/save.cpp | 45 ++++- src/game/server/save.h | 11 +- src/game/server/score.h | 2 +- src/game/server/score/file_score.cpp | 2 +- src/game/server/score/file_score.h | 2 +- src/game/server/score/sql_score.cpp | 246 +++++++++++-------------- src/game/server/score/sql_score.h | 48 ++++- src/game/server/teams.cpp | 85 +++++++-- src/game/server/teams.h | 27 ++- 15 files changed, 323 insertions(+), 224 deletions(-) diff --git a/src/engine/server/sql_server.cpp b/src/engine/server/sql_server.cpp index 32304dbaf..642dd32c8 100644 --- a/src/engine/server/sql_server.cpp +++ b/src/engine/server/sql_server.cpp @@ -186,7 +186,7 @@ void CSqlServer::CreateTables() str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_maps (Map VARCHAR(128) BINARY NOT NULL, Server VARCHAR(32) BINARY NOT NULL, Mapper VARCHAR(128) BINARY NOT NULL, Points INT DEFAULT 0, Stars INT DEFAULT 0, Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY Map (Map)) CHARACTER SET utf8mb4;", m_aPrefix); executeSql(aBuf); - str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_saves (Savegame TEXT CHARACTER SET utf8mb4 BINARY NOT NULL, Map VARCHAR(128) BINARY NOT NULL, Code VARCHAR(128) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Server CHAR(4), DDNet7 BOOL DEFAULT FALSE, UNIQUE KEY (Map, Code)) CHARACTER SET utf8mb4;", m_aPrefix); + str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_saves (Savegame TEXT CHARACTER SET utf8mb4 BINARY NOT NULL, Map VARCHAR(128) BINARY NOT NULL, Code VARCHAR(128) BINARY NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, Server CHAR(4), DDNet7 BOOL DEFAULT FALSE, SaveID VARCHAR(36), UNIQUE KEY (Map, Code)) CHARACTER SET utf8mb4;", m_aPrefix); executeSql(aBuf); str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_points (Name VARCHAR(%d) BINARY NOT NULL, Points INT DEFAULT 0, UNIQUE KEY Name (Name)) CHARACTER SET utf8mb4;", m_aPrefix, MAX_NAME_LENGTH); diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 79de8e55c..d4781775e 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -693,8 +693,6 @@ void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData) if(pPlayer->m_LastSQLQuery + g_Config.m_SvSqlQueriesDelay * pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) return; - int Team = ((CGameControllerDDRace*) pSelf->m_pController)->m_Teams.m_Core.Team(pResult->m_ClientID); - const char* pCode = pResult->GetString(0); char aCountry[5]; if(str_length(pCode) > 3 && pCode[0] >= 'A' && pCode[0] <= 'Z' && pCode[1] >= 'A' @@ -722,7 +720,7 @@ void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData) if(str_in_list(g_Config.m_SvSqlValidServerNames, ",", aCountry)) { - pSelf->Score()->SaveTeam(Team, pCode, pResult->m_ClientID, aCountry); + pSelf->Score()->SaveTeam(pResult->m_ClientID, pCode, aCountry); if(g_Config.m_SvUseSQL) pPlayer->m_LastSQLQuery = pSelf->Server()->Tick(); diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 8e7ed6273..363c8b54c 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1503,6 +1503,22 @@ void CCharacter::HandleTiles(int Index) // start if(((m_TileIndex == TILE_BEGIN) || (m_TileFIndex == TILE_BEGIN) || FTile1 == TILE_BEGIN || FTile2 == TILE_BEGIN || FTile3 == TILE_BEGIN || FTile4 == TILE_BEGIN || Tile1 == TILE_BEGIN || Tile2 == TILE_BEGIN || Tile3 == TILE_BEGIN || Tile4 == TILE_BEGIN) && (m_DDRaceState == DDRACE_NONE || m_DDRaceState == DDRACE_FINISHED || (m_DDRaceState == DDRACE_STARTED && !Team() && g_Config.m_SvTeam != 3))) { + if(Teams()->GetSaving(Team())) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "Your team is currently getting saved"); // TODO: better message + Die(GetPlayer()->GetCID(), WEAPON_WORLD); + return; + } + if(g_Config.m_SvTeam == 2 && (Team() == TEAM_FLOCK || Teams()->Count(Team()) <= 1)) + { + if(m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed()) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You have to be in a team with other tees to start"); + m_LastStartWarning = Server()->Tick(); + } + Die(GetPlayer()->GetCID(), WEAPON_WORLD); + return; + } if(g_Config.m_SvResetPickups) { for (int i = WEAPON_SHOTGUN; i < NUM_WEAPONS; ++i) @@ -1512,16 +1528,6 @@ void CCharacter::HandleTiles(int Index) m_Core.m_ActiveWeapon = WEAPON_GUN; } } - if(g_Config.m_SvTeam == 2 && (Team() == TEAM_FLOCK || Teams()->Count(Team()) <= 1)) - { - if(m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed()) - { - GameServer()->SendChatTarget(GetPlayer()->GetCID(),"You have to be in a team with other tees to start"); - m_LastStartWarning = Server()->Tick(); - } - Die(GetPlayer()->GetCID(), WEAPON_WORLD); - return; - } Teams()->OnCharacterStart(m_pPlayer->GetCID()); m_CpActive = -2; diff --git a/src/game/server/gamemodes/DDRace.cpp b/src/game/server/gamemodes/DDRace.cpp index f0476e752..b183b9f0d 100644 --- a/src/game/server/gamemodes/DDRace.cpp +++ b/src/game/server/gamemodes/DDRace.cpp @@ -25,6 +25,9 @@ CGameControllerDDRace::~CGameControllerDDRace() void CGameControllerDDRace::Tick() { IGameController::Tick(); +#if defined(CONF_SQL) + m_Teams.ProcessSaveTeam(); +#endif } void CGameControllerDDRace::InitTeleporter() diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 3f0e91e48..b700eed7e 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -45,7 +45,6 @@ void CPlayer::Reset() m_JoinTick = Server()->Tick(); delete m_pCharacter; m_pCharacter = 0; - m_KillMe = 0; m_SpectatorID = SPEC_FREEVIEW; m_LastActionTick = Server()->Tick(); m_TeamChangeTick = Server()->Tick(); @@ -154,13 +153,6 @@ void CPlayer::Tick() if(!Server()->ClientIngame(m_ClientID)) return; - if(m_KillMe != 0) - { - KillCharacter(m_KillMe); - m_KillMe = 0; - return; - } - if (m_ChatScore > 0) m_ChatScore--; @@ -499,11 +491,6 @@ CCharacter *CPlayer::GetCharacter() return 0; } -void CPlayer::ThreadKillCharacter(int Weapon) -{ - m_KillMe = Weapon; -} - void CPlayer::KillCharacter(int Weapon) { if(m_pCharacter) @@ -797,21 +784,30 @@ void CPlayer::ProcessSqlResult() if(m_SqlQueryResult == nullptr || m_SqlQueryResult.use_count() != 1) return; - if(m_SqlQueryResult->m_Done) // sql was query successful + if(m_SqlQueryResult->m_Done) // SQL request was successful { - for(int i = 0; i < (int)(sizeof(m_SqlQueryResult->m_aaMessages)/sizeof(m_SqlQueryResult->m_aaMessages[0])); i++) + int NumMessages = (int)(sizeof(m_SqlQueryResult->m_aaMessages)/sizeof(m_SqlQueryResult->m_aaMessages[0])); + switch(m_SqlQueryResult->m_MessageKind) { - if(m_SqlQueryResult->m_aaMessages[i][0] == 0) - break; - switch(m_SqlQueryResult->m_MessageTarget) + case CSqlPlayerResult::DIRECT: + for(int i = 0; i < NumMessages; i++) { - case CSqlPlayerResult::DIRECT: + if(m_SqlQueryResult->m_aaMessages[i][0] == 0) + break; GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_aaMessages[i]); - break; - case CSqlPlayerResult::ALL: - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_aaMessages[i]); - break; } + break; + case CSqlPlayerResult::ALL: + for(int i = 0; i < NumMessages; i++) + { + if(m_SqlQueryResult->m_aaMessages[i][0] == 0) + break; + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_aaMessages[i]); + } + break; + case CSqlPlayerResult::MAP_VOTE: + // TODO: start vote + break; } } m_SqlQueryResult = nullptr; diff --git a/src/game/server/player.h b/src/game/server/player.h index 93ffdf00c..f6be28f4a 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -47,7 +47,6 @@ public: void OnPredictedEarlyInput(CNetObj_PlayerInput *NewInput); void OnDisconnect(const char *pReason); - void ThreadKillCharacter(int Weapon = WEAPON_GAME); void KillCharacter(int Weapon = WEAPON_GAME); CCharacter *GetCharacter(); @@ -174,7 +173,6 @@ public: bool m_SpecTeam; bool m_NinjaJetpack; bool m_Afk; - int m_KillMe; bool m_HasFinishScore; int m_ChatScore; diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index 601a1e1ed..7b33a53a5 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -52,7 +52,9 @@ void CSaveTee::save(CCharacter *pChr) m_TuneZoneOld = pChr->m_TuneZoneOld; if(pChr->m_StartTime) - m_Time = pChr->Server()->Tick() - pChr->m_StartTime + 60 * pChr->Server()->TickSpeed(); + m_Time = pChr->Server()->Tick() - pChr->m_StartTime; + else + m_Time = 0; m_Pos = pChr->m_Pos; m_PrevPos = pChr->m_PrevPos; @@ -183,6 +185,9 @@ void CSaveTee::load(CCharacter *pChr, int Team) char* CSaveTee::GetString() { + // Add time penalty of 60 seconds (only to the database) + int Time = m_Time + 60 * SERVER_TICK_SPEED; + str_format(m_aString, sizeof(m_aString), "%s\t%d\t%d\t%d\t%d\t%d\t" // weapons @@ -223,7 +228,7 @@ char* CSaveTee::GetString() m_LastWeapon, m_QueuedWeapon, // tee states m_SuperJump, m_Jetpack, m_NinjaJetpack, m_FreezeTime, m_FreezeTick, m_DeepFreeze, m_EndlessHook, - m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, m_Time, + m_DDRaceState, m_Hit, m_Collision, m_TuneZone, m_TuneZoneOld, m_Hook, Time, (int)m_Pos.x, (int)m_Pos.y, (int)m_PrevPos.x, (int)m_PrevPos.y, m_TeleCheckpoint, m_LastPenalty, (int)m_CorePos.x, (int)m_CorePos.y, m_Vel.x, m_Vel.y, @@ -420,6 +425,38 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon return true; } +void CSaveTeam::HandleLoadError(int Result, int ClientID, const CSaveTeam &SavedTeam, CGameContext *pGameContext) +{ + if(Result == 1) + { + pGameContext->SendChatTarget(ClientID, "You have to be in a team (from 1-63)"); + } + else if(Result == 2) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Too many players in this team, should be %d", SavedTeam.GetMembersCount()); + pGameContext->SendChatTarget(ClientID, aBuf); + } + else if(Result >= 10 && Result < 100) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Unable to find player: '%s'", SavedTeam.m_pSavedTees[Result-10].GetName()); + pGameContext->SendChatTarget(ClientID, aBuf); + } + else if(Result >= 100 && Result < 200) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "%s is racing right now, Team can't be loaded if a Tee is racing already", SavedTeam.m_pSavedTees[Result-100].GetName()); + pGameContext->SendChatTarget(ClientID, aBuf); + } + else if(Result >= 200) + { + char aBuf[256]; + str_format(aBuf, sizeof(aBuf), "Everyone has to be in a team, %s is in team 0 or the wrong team", SavedTeam.m_pSavedTees[Result-200].GetName()); + pGameContext->SendChatTarget(ClientID, aBuf); + } +} + int CSaveTeam::load(int Team) { if(Team <= 0 || Team >= MAX_CLIENTS) @@ -474,7 +511,7 @@ int CSaveTeam::load(int Team) return 0; } -int CSaveTeam::MatchPlayer(char name[16]) +int CSaveTeam::MatchPlayer(const char name[16]) { for (int i = 0; i < MAX_CLIENTS; i++) { @@ -486,7 +523,7 @@ int CSaveTeam::MatchPlayer(char name[16]) return -1; } -CCharacter* CSaveTeam::MatchCharacter(char name[16], int SaveID) +CCharacter* CSaveTeam::MatchCharacter(const char name[16], int SaveID) { int ID = MatchPlayer(name); if(ID >= 0 && m_pController->GameServer()->m_apPlayers[ID]) diff --git a/src/game/server/save.h b/src/game/server/save.h index 08cb62571..65fa7f435 100644 --- a/src/game/server/save.h +++ b/src/game/server/save.h @@ -14,8 +14,8 @@ public: void load(CCharacter* pchr, int Team); char* GetString(); int LoadString(char* String); - vec2 GetPos() { return m_Pos; } - char* GetName() { return m_aName; } + vec2 GetPos() const { return m_Pos; } + const char* GetName() const { return m_aName; } private: @@ -95,7 +95,7 @@ public: CSaveTeam(IGameController* Controller); ~CSaveTeam(); char* GetString(); - int GetMembersCount() { return m_MembersCount; } + int GetMembersCount() const { return m_MembersCount; } int LoadString(const char* String); int save(int Team); int load(int Team); @@ -103,9 +103,10 @@ public: // returns true if an error occured static bool HandleSaveError(int Result, int ClientID, CGameContext *pGameContext); + static void HandleLoadError(int Result, int ClientID, const CSaveTeam &SavedTeam, CGameContext *pGameContext); private: - int MatchPlayer(char name[16]); - CCharacter* MatchCharacter(char name[16], int SaveID); + int MatchPlayer(const char name[16]); + CCharacter* MatchCharacter(const char name[16], int SaveID); IGameController* m_pController; diff --git a/src/game/server/score.h b/src/game/server/score.h index 4d3feaca0..4a5b606ad 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -80,7 +80,7 @@ public: virtual void RandomMap(int ClientID, int Stars) = 0; virtual void RandomUnfinishedMap(int ClientID, int Stars) = 0; - virtual void SaveTeam(int Team, const char *pCode, int ClientID, const char *pServer) = 0; + virtual void SaveTeam(int ClientID, const char *pCode, const char *pServer) = 0; virtual void LoadTeam(const char *pCode, int ClientID) = 0; virtual void GetSaves(int ClientID) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 8a6f4eb82..6b330f536 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -336,7 +336,7 @@ void CFileScore::RandomUnfinishedMap(int ClientID, int Stars) GameServer()->SendChatTarget(ClientID, aBuf); } -void CFileScore::SaveTeam(int Team, const char* Code, int ClientID, const char* Server) +void CFileScore::SaveTeam(int ClientID, const char* Code, const char* Server) { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers"); diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 0cc4b88e4..41525a4a2 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -78,7 +78,7 @@ public: virtual void ShowPoints(int ClientID, const char* pName); virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); - virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); + virtual void SaveTeam(int ClientID, const char* Code, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); virtual void GetSaves(int ClientID); diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 7deb8a4f8..bc6cad695 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -21,7 +21,7 @@ std::atomic_int CSqlScore::ms_InstanceCount(0); CSqlPlayerResult::CSqlPlayerResult() : m_Done(0), - m_MessageTarget(DIRECT) + m_MessageKind(DIRECT) { for(int i = 0; i < (int)(sizeof(m_aaMessages)/sizeof(m_aaMessages[0])); i++) m_aaMessages[i][0] = 0; @@ -324,82 +324,58 @@ bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData(); - - CSqlMapVoteData *Tmp = new CSqlMapVoteData(); - Tmp->m_ClientID = ClientID; - Tmp->m_RequestedMap = MapName; - Tmp->m_pResult = *ppResult; - str_copy(Tmp->m_aFuzzyMap, MapName, sizeof(Tmp->m_aFuzzyMap)); - sqlstr::ClearString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); - sqlstr::FuzzyString(Tmp->m_aFuzzyMap, sizeof(Tmp->m_aFuzzyMap)); - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(MapVoteThread, Tmp), "map vote"); - */ + ExecPlayerThread(MapVoteThread, "map vote", ClientID, MapName, 0); } -bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlMapVoteData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; if (HandleFailure) return true; try { + char aFuzzyMap[128]; + str_copy(aFuzzyMap, pData->m_Name.Str(), sizeof(aFuzzyMap)); + sqlstr::ClearString(aFuzzyMap, sizeof(aFuzzyMap)); + sqlstr::FuzzyString(aFuzzyMap, sizeof(aFuzzyMap)); + char aBuf[768]; - str_format(aBuf, sizeof(aBuf), "SELECT Map, Server FROM %s_maps WHERE Map LIKE '%s' COLLATE utf8mb4_general_ci ORDER BY CASE WHEN Map = '%s' THEN 0 ELSE 1 END, CASE WHEN Map LIKE '%s%%' THEN 0 ELSE 1 END, LENGTH(Map), Map LIMIT 1;", pSqlServer->GetPrefix(), pData->m_aFuzzyMap, pData->m_RequestedMap.ClrStr(), pData->m_RequestedMap.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT Map, Server " + "FROM %s_maps " + "WHERE Map LIKE '%s' COLLATE utf8mb4_general_ci " + "ORDER BY " + "CASE WHEN Map = '%s' THEN 0 ELSE 1 END, " + "CASE WHEN Map LIKE '%s%%' THEN 0 ELSE 1 END, " + "LENGTH(Map), Map " + "LIMIT 1;", + pSqlServer->GetPrefix(), aFuzzyMap, + pData->m_Name.ClrStr(), pData->m_Name.ClrStr() + ); pSqlServer->executeSqlQuery(aBuf); - - CPlayer *pPlayer = pData->GameServer()->m_apPlayers[pData->m_ClientID]; - - int64 Now = pData->Server()->Tick(); - int Timeleft = 0; - - if(!pPlayer) - goto end; - - Timeleft = pPlayer->m_LastVoteCall + pData->Server()->TickSpeed()*g_Config.m_SvVoteDelay - Now; - if(pSqlServer->GetResults()->rowsCount() != 1) { - str_format(aBuf, sizeof(aBuf), "No map like \"%s\" found. Try adding a '%%' at the start if you don't know the first character. Example: /map %%castle for \"Out of Castle\"", pData->m_RequestedMap.Str()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - else if(Now < pPlayer->m_FirstVoteTick) - { - char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "You must wait %d seconds before making your first vote", (int)((pPlayer->m_FirstVoteTick - Now) / pData->Server()->TickSpeed()) + 1); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - else if(pPlayer->m_LastVoteCall && Timeleft > 0) - { - char aChatmsg[512] = {0}; - str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote", (Timeleft/pData->Server()->TickSpeed())+1); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aChatmsg); - } - else if(time_get() < pData->GameServer()->m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay)) - { - char chatmsg[512] = {0}; - str_format(chatmsg, sizeof(chatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", g_Config.m_SvVoteMapTimeDelay, (int)(((pData->GameServer()->m_LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq()))); - pData->GameServer()->SendChatTarget(pData->m_ClientID, chatmsg); + str_format(paMessages[0], sizeof(paMessages[0]), + "No map like \"%s\" found. " + "Try adding a '%%' at the start if you don't know the first character. " + "Example: /map %%castle for \"Out of Castle\"", + pData->m_Name.Str()); } else { - pSqlServer->GetResults()->next(); - str_copy(pData->m_pResult->m_aMap, pSqlServer->GetResults()->getString("Map").c_str(), sizeof(pData->m_pResult->m_aMap)); - str_copy(pData->m_pResult->m_aServer, pSqlServer->GetResults()->getString("Server").c_str(), sizeof(pData->m_pResult->m_aServer)); + auto Server = pSqlServer->GetResults()->getString("Server"); + auto Map = pSqlServer->GetResults()->getString("Map"); + strcpy(paMessages[0], "/map"); // reason + str_copy(paMessages[1], Server.c_str(), sizeof(paMessages[0])); + str_copy(paMessages[2], Map.c_str(), sizeof(paMessages[0])); - for(char *p = pData->m_pResult->m_aServer; *p; p++) + for(char *p = paMessages[1]; *p; p++) // lower case server *p = tolower(*p); - - pData->m_pResult->m_ClientID = pData->m_ClientID; - - pData->m_pResult->m_Done = true; } - end: + pData->m_pResult->m_Done = true; return true; } catch (sql::SQLException &e) @@ -407,13 +383,6 @@ bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlData dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Could not start Mapvote"); } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted mapvote due to reload/change of map."); - return true; - } - return false; - */ return false; } @@ -865,7 +834,7 @@ bool CSqlScore::ShowRankThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_MessageTarget = CSqlPlayerResult::ALL; + pData->m_pResult->m_MessageKind = CSqlPlayerResult::ALL; str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), "%d. %s Time: %02d:%05.2f, requested by %s", Rank, pSqlServer->GetResults()->getString("Name").c_str(), @@ -965,7 +934,7 @@ bool CSqlScore::ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_MessageTarget = CSqlPlayerResult::ALL; + pData->m_pResult->m_MessageKind = CSqlPlayerResult::ALL; str_format(pData->m_pResult->m_aaMessages[0], sizeof(pData->m_pResult->m_aaMessages[0]), "%d. %s Team time: %02d:%05.02f, requested by %s", Rank, aNames, (int)(Time/60), Time-((int)Time/60*60), pData->m_RequestingPlayer.Str()); @@ -1102,7 +1071,6 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlDataGetResults()->getInt("TeamSize"); float Time = (float)pSqlServer->GetResults()->getDouble("Time"); int Rank = pSqlServer->GetResults()->getInt("Rank"); - printf("%d", TeamSize); char aNames[2300] = { 0 }; for(int i = 0; i < TeamSize; i++) @@ -1280,7 +1248,7 @@ bool CSqlScore::ShowPointsThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->getInt("Points"); int Rank = pSqlServer->GetResults()->getInt("Rank"); auto Name = pSqlServer->GetResults()->getString("Name"); - pData->m_pResult->m_MessageTarget = CSqlPlayerResult::ALL; + pData->m_pResult->m_MessageKind = CSqlPlayerResult::ALL; str_format(paMessages[0], sizeof(paMessages[0]), "%d. %s Points: %d, requested by %s", Rank, Name.c_str(), Count, pData->m_RequestingPlayer.Str()); @@ -1499,126 +1467,128 @@ bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData return false; } -void CSqlScore::SaveTeam(int Team, const char* Code, int ClientID, const char* Server) +void CSqlScore::SaveTeam(int ClientID, const char* Code, const char* Server) { - /* - if(((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.GetSaving(Team)) + auto pController = ((CGameControllerDDRace*)(GameServer()->m_pController)); + int Team = pController->m_Teams.m_Core.Team(ClientID); + if(pController->m_Teams.GetSaving(Team)) return; - CSaveTeam SavedTeam(GameServer()->m_pController); - int Result = SavedTeam.save(Team); + auto SaveResult = std::make_shared(ClientID, pController); + int Result = SaveResult->m_SavedTeam.save(Team); if(CSaveTeam::HandleSaveError(Result, ClientID, GameServer())) return; - // disable joining team while saving - ((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.SetSaving(Team, true); + pController->m_Teams.SetSaving(Team, SaveResult); - CSqlTeamSave *Tmp = new CSqlTeamSave(); - Tmp->m_Team = Team; - Tmp->m_ClientID = ClientID; - // copy both save code and save state into the thread struct + CSqlTeamSave *Tmp = new CSqlTeamSave(SaveResult); Tmp->m_Code = Code; - Tmp->m_SaveState = SavedTeam.GetString(); + Tmp->m_Map = g_Config.m_SvMap; + FormatUuid(RandomUuid(), Tmp->m_Uuid, UUID_MAXSTRSIZE); str_copy(Tmp->m_Server, Server, sizeof(Tmp->m_Server)); - str_copy(Tmp->m_ClientName, this->Server()->ClientName(Tmp->m_ClientID), sizeof(Tmp->m_ClientName)); + str_copy(Tmp->m_ClientName, this->Server()->ClientName(ClientID), sizeof(Tmp->m_ClientName)); - // TODO: log event in Teehistorian - // TODO: find a way to send all players in the team the save code - ((CGameControllerDDRace*)(GameServer()->m_pController))->m_Teams.KillSavedTeam(Team); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(SaveTeamThread, Tmp, false), "save team"); - */ + // TODO: log event in teehistorian + pController->m_Teams.KillSavedTeam(ClientID, Team); + char aBuf[512]; + // TODO: better message, maybe hint that one should wait until the next message to leave + str_format(aBuf, sizeof(aBuf), "Saving team initiated by '%s'", this->Server()->ClientName(ClientID)); + GameServer()->SendChatTeam(Team, aBuf); + thread_init_and_detach( + CSqlExecData::ExecSqlFunc, + new CSqlExecData(SaveTeamThread, Tmp, false), + "save team"); } -bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* const CSqlTeamSave *pData = dynamic_cast(pGameData); - try + sqlstr::CSqlString<10> SaveState = pData->m_pResult->m_SavedTeam.GetString(); + if(HandleFailure) { - if (HandleFailure) + if (!g_Config.m_SvSqlFailureFile[0]) + return true; + + lock_wait(ms_FailureFileLock); + IOHANDLE File = io_open(g_Config.m_SvSqlFailureFile, IOFLAG_APPEND); + if(File) { - if (!g_Config.m_SvSqlFailureFile[0]) - return true; + dbg_msg("sql", "ERROR: Could not save Teamsave, writing insert to a file now..."); - lock_wait(ms_FailureFileLock); - IOHANDLE File = io_open(g_Config.m_SvSqlFailureFile, IOFLAG_APPEND); - if(File) - { - dbg_msg("sql", "ERROR: Could not save Teamsave, writing insert to a file now..."); - - char aBuf[65536]; - str_format(aBuf, sizeof(aBuf), - "INSERT IGNORE INTO %%s_saves(Savegame, Map, Code, Timestamp, Server, DDNet7) " - "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', false);", - pData->m_SaveState, pData->m_Map.ClrStr(), pData->m_Code.ClrStr(), pData->m_Server - ); - io_write(File, aBuf, str_length(aBuf)); - io_write_newline(File); - io_close(File); - lock_unlock(ms_FailureFileLock); - - pData->GameServer()->SendBroadcast("Database connection failed, teamsave written to a file instead. Admins will add it manually in a few days.", -1); - - return true; - } + char aBuf[65536]; + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %%s_saves(Savegame, Map, Code, Timestamp, Server, SaveID, DDNet7) " + "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', '%s', false)", + SaveState.ClrStr(), pData->m_Map.ClrStr(), + pData->m_Code.ClrStr(), pData->m_Server, pData->m_Uuid + ); + io_write(File, aBuf, str_length(aBuf)); + io_write_newline(File); + io_close(File); lock_unlock(ms_FailureFileLock); - dbg_msg("sql", "ERROR: Could not save Teamsave, NOT even to a file"); - return false; - } + pData->m_pResult->m_Status = CSqlSaveResult::SAVE_SUCCESS; + strcpy(pData->m_pResult->m_aBroadcast, + "Database connection failed, teamsave written to a file instead. Admins will add it manually in a few days."); + + return true; + } + lock_unlock(ms_FailureFileLock); + dbg_msg("sql", "ERROR: Could not save Teamsave, NOT even to a file"); + return false; + } + else + { try { char aBuf[512]; str_format(aBuf, sizeof(aBuf), "lock tables %s_saves write;", pSqlServer->GetPrefix()); pSqlServer->executeSql(aBuf); - str_format(aBuf, sizeof(aBuf), "select Savegame from %s_saves where Code = '%s' and Map = '%s';", pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT Savegame " + "FROM %s_saves " + "WHERE Code = '%s' AND Map = '%s';", + pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr()); pSqlServer->executeSqlQuery(aBuf); if (pSqlServer->GetResults()->rowsCount() == 0) { char aBuf[65536]; str_format(aBuf, sizeof(aBuf), - "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, DDNet7) VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', false)", - pSqlServer->GetPrefix(), pData->m_SaveState.ClrStr(), pData->m_Map.ClrStr(), pData->m_Code.ClrStr(), pData->m_Server + "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, SaveID, DDNet7) " + "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', '%s', false)", + pSqlServer->GetPrefix(), SaveState.ClrStr(), pData->m_Map.ClrStr(), + pData->m_Code.ClrStr(), pData->m_Server, pData->m_Uuid ); dbg_msg("sql", "%s", aBuf); pSqlServer->executeSql(aBuf); - // be sure to keep all calls to pData->GameServer() after inserting the save, otherwise it might be lost due to CGameContextError. - - char aBuf2[512]; - str_format(aBuf2, sizeof(aBuf2), "Team successfully saved by %s. Use '/load %s' to continue", pData->m_ClientName, pData->m_Code.Str()); - pData->GameServer()->SendChatTeam(Team, aBuf2); + str_format(pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage), + "Team successfully saved by %s. Use '/load %s' to continue", + pData->m_ClientName, pData->m_Code.Str()); + pData->m_pResult->m_Status = CSqlSaveResult::SAVE_SUCCESS; } else { dbg_msg("sql", "ERROR: This save-code already exists"); - pData->GameServer()->SendChatTarget(pData->m_ClientID, "This save-code already exists"); + pData->m_pResult->m_Status = CSqlSaveResult::SAVE_FAILED; + strcpy(pData->m_pResult->m_aMessage, "This save-code already exists"); } - } catch (sql::SQLException &e) { + pData->m_pResult->m_Status = CSqlSaveResult::SAVE_FAILED; dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Could not save the team"); - pData->GameServer()->SendChatTarget(pData->m_ClientID, "MySQL Error: Could not save the team"); + + strcpy(pData->m_pResult->m_aMessage, "MySQL Error: Could not save the team"); pSqlServer->executeSql("unlock tables;"); return false; } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Could not send chatmessage during saving team due to reload/change of map."); - } - } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted saving team due to reload/change of map."); } pSqlServer->executeSql("unlock tables;"); return true; - */ - return false; } void CSqlScore::LoadTeam(const char* Code, int ClientID) diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 5cce6348f..d66d1cc43 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -5,7 +5,6 @@ #define GAME_SERVER_SCORE_SQL_H #include -#include #include "../score.h" @@ -20,10 +19,40 @@ public: { DIRECT, ALL, - } m_MessageTarget; + MAP_VOTE, // 3 Messages: Reason, Server, Map + } m_MessageKind; char m_aaMessages[7][512]; }; +class CSqlSaveResult { +public: + CSqlSaveResult(int PlayerID, IGameController* Controller) : + m_Status(SAVE_SUCCESS), + m_SavedTeam(CSaveTeam(Controller)), + m_RequestingPlayer(PlayerID) + { + m_aMessage[0] = '\0'; + m_aBroadcast[0] = '\0'; + } + enum + { + SAVE_SUCCESS, + // load team in the following two cases + SAVE_FAILED, + LOAD_SUCCESS, + LOAD_FAILED, + } m_Status; + char m_aMessage[512]; + char m_aBroadcast[512]; + CSaveTeam m_SavedTeam; + int m_RequestingPlayer; +}; + +class CSqlMapResult { + CSqlMapResult(); + std::atomic_bool m_Done; +}; + // result only valid if m_Done is set to true class CSqlResult { @@ -128,15 +157,16 @@ struct CSqlTeamScoreData : CSqlData sqlstr::CSqlString m_aNames[MAX_CLIENTS]; }; -struct CSqlTeamSave : CSqlData +struct CSqlTeamSave : CSqlData { - virtual ~CSqlTeamSave(); + using CSqlData::CSqlData; + virtual ~CSqlTeamSave() {}; char m_ClientName[MAX_NAME_LENGTH]; - CUuid m_SaveUuid; + sqlstr::CSqlString<128> m_Map; sqlstr::CSqlString<128> m_Code; - sqlstr::CSqlString<65536> m_SaveState; + char m_Uuid[UUID_MAXSTRSIZE]; char m_Server[5]; }; @@ -185,7 +215,7 @@ class CSqlScore: public IScore static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -199,7 +229,7 @@ class CSqlScore: public IScore static bool ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -253,7 +283,7 @@ public: virtual void GetSaves(int ClientID); // requested by teams - virtual void SaveTeam(int Team, const char* Code, int ClientID, const char* Server); + virtual void SaveTeam(int ClientID, const char* Code, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); // Game relevant not allowed to fail diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index c5a0217cf..065609e90 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -2,6 +2,9 @@ #include "teams.h" #include "score.h" #include +#if defined(CONF_SQL) +#include "score/sql_score.h" +#endif CGameTeams::CGameTeams(CGameContext *pGameContext) : m_pGameContext(pGameContext) @@ -19,9 +22,11 @@ void CGameTeams::Reset() m_MembersCount[i] = 0; m_LastChat[i] = 0; m_TeamLocked[i] = false; - m_IsSaving[i] = false; m_Invited[i] = 0; m_Practice[i] = false; +#if defined(CONF_SQL) + m_pSaveTeamResult[i] = nullptr; +#endif } } @@ -252,7 +257,8 @@ bool CGameTeams::SetCharacterTeam(int ClientID, int Team) //you can not join a team which is currently in the process of saving, //because the save-process can fail and then the team is reset into the game - if(Team != TEAM_SUPER && m_IsSaving[Team]) + if((Team != TEAM_SUPER && GetSaving(Team)) + || (m_Core.Team(ClientID) != TEAM_SUPER && GetSaving(m_Core.Team(ClientID)))) return false; SetForceCharacterTeam(ClientID, Team); @@ -331,6 +337,9 @@ void CGameTeams::ForceLeaveTeam(int ClientID) SetTeamLock(m_Core.Team(ClientID), false); ResetInvited(m_Core.Team(ClientID)); m_Practice[m_Core.Team(ClientID)] = false; +#if defined(CONF_SQL) + m_pSaveTeamResult[m_Core.Team(ClientID)] = nullptr; +#endif } } @@ -674,10 +683,47 @@ void CGameTeams::OnFinish(CPlayer* Player, float Time, const char *pTimestamp) } } +#if defined(CONF_SQL) +void CGameTeams::ProcessSaveTeam() +{ + for(int Team = 0; Team < MAX_CLIENTS; Team++) + { + if(m_pSaveTeamResult[Team] == nullptr || m_pSaveTeamResult[Team].use_count() != 1) + continue; + if(m_pSaveTeamResult[Team]->m_aBroadcast[0] != '\0') + GameServer()->SendBroadcast(m_pSaveTeamResult[Team]->m_aBroadcast, -1); + if(m_pSaveTeamResult[Team]->m_aMessage[0] != '\0') + GameServer()->SendChatTeam(Team, m_pSaveTeamResult[Team]->m_aMessage); + switch(m_pSaveTeamResult[Team]->m_Status) + { + case CSqlSaveResult::SAVE_SUCCESS: + { + ResetSavedTeam(m_pSaveTeamResult[Team]->m_RequestingPlayer, Team); + break; + } + case CSqlSaveResult::SAVE_FAILED: + { + int Result = m_pSaveTeamResult[Team]->m_SavedTeam.load(Team); + if(Result != 0) + CSaveTeam::HandleLoadError(Result, m_pSaveTeamResult[Team]->m_RequestingPlayer, m_pSaveTeamResult[Team]->m_SavedTeam, m_pGameContext); + break; + } + case CSqlSaveResult::LOAD_SUCCESS: + case CSqlSaveResult::LOAD_FAILED: + break; // TODO + } + m_pSaveTeamResult[Team] = nullptr; + } +} +#endif + void CGameTeams::OnCharacterSpawn(int ClientID) { m_Core.SetSolo(ClientID, false); + if(GetSaving(m_Core.Team(ClientID))) + return; + if (m_Core.Team(ClientID) >= TEAM_SUPER || !m_TeamLocked[m_Core.Team(ClientID)]) // Important to only set a new team here, don't remove from an existing // team since a newly joined player does by definition not have an old team @@ -690,6 +736,8 @@ void CGameTeams::OnCharacterDeath(int ClientID, int Weapon) m_Core.SetSolo(ClientID, false); int Team = m_Core.Team(ClientID); + if(GetSaving(Team)) + return; bool Locked = TeamLocked(Team) && Weapon != WEAPON_GAME; if(!Locked) @@ -750,30 +798,27 @@ void CGameTeams::SetClientInvited(int Team, int ClientID, bool Invited) } } -void CGameTeams::KillSavedTeam(int Team) +#if defined(CONF_SQL) +void CGameTeams::KillSavedTeam(int ClientID, int Team) { - // Set so that no finish is accidentally given to some of the players - ChangeTeamState(Team, CGameTeams::TEAMSTATE_OPEN); + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) + { + GameServer()->m_apPlayers[i]->m_VotedForPractice = false; + GameServer()->m_apPlayers[i]->KillCharacter(WEAPON_SELF); + } + } +} +void CGameTeams::ResetSavedTeam(int ClientID, int Team) +{ for (int i = 0; i < MAX_CLIENTS; i++) { if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) { - // Set so that no finish is accidentally given to some of the players - GameServer()->m_apPlayers[i]->GetCharacter()->m_DDRaceState = DDRACE_NONE; - m_TeeFinished[i] = false; + SetForceCharacterTeam(i, 0); } } - - for (int i = 0; i < MAX_CLIENTS; i++) - if(m_Core.Team(i) == Team && GameServer()->m_apPlayers[i]) - GameServer()->m_apPlayers[i]->ThreadKillCharacter(-2); - - ChangeTeamState(Team, CGameTeams::TEAMSTATE_EMPTY); - - // unlock team when last player leaves - SetTeamLock(Team, false); - ResetInvited(Team); - - m_Practice[Team] = false; } +#endif diff --git a/src/game/server/teams.h b/src/game/server/teams.h index 36fd34d32..e5fdbfc9c 100644 --- a/src/game/server/teams.h +++ b/src/game/server/teams.h @@ -5,15 +5,21 @@ #include #include +#if defined(CONF_SQL) +class CSqlSaveResult; +#endif + class CGameTeams { int m_TeamState[MAX_CLIENTS]; int m_MembersCount[MAX_CLIENTS]; bool m_TeeFinished[MAX_CLIENTS]; bool m_TeamLocked[MAX_CLIENTS]; - bool m_IsSaving[MAX_CLIENTS]; uint64_t m_Invited[MAX_CLIENTS]; bool m_Practice[MAX_CLIENTS]; +#if defined(CONF_SQL) + std::shared_ptr m_pSaveTeamResult[MAX_CLIENTS]; +#endif class CGameContext * m_pGameContext; @@ -85,7 +91,11 @@ public: void SetDDRaceState(CPlayer* Player, int DDRaceState); void SetStartTime(CPlayer* Player, int StartTime); void SetCpActive(CPlayer* Player, int CpActive); - void KillSavedTeam(int Team); +#if defined(CONF_SQL) + void KillSavedTeam(int ClientID, int Team); + void ResetSavedTeam(int ClientID, int Team); + void ProcessSaveTeam(); +#endif bool TeeFinished(int ClientID) { @@ -114,15 +124,20 @@ public: { m_TeeFinished[ClientID] = finished; } - - void SetSaving(int TeamID, bool Value) +#if defined(CONF_SQL) + void SetSaving(int TeamID, std::shared_ptr SaveResult) { - m_IsSaving[TeamID] = Value; + m_pSaveTeamResult[TeamID] = SaveResult; } +#endif bool GetSaving(int TeamID) { - return m_IsSaving[TeamID]; +#if defined(CONF_SQL) + return m_pSaveTeamResult[TeamID] != nullptr; +#else + return false; +#endif } void EnablePractice(int Team) From e9242f45f111f9eeac93fca6c21bf97b84262318 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 3 Jun 2020 22:08:21 +0200 Subject: [PATCH 33/44] Thread safe /load --- src/game/server/entities/character.cpp | 6 +- src/game/server/save.cpp | 147 +++++++---------- src/game/server/save.h | 14 +- src/game/server/score/sql_score.cpp | 209 ++++++++++++------------- src/game/server/score/sql_score.h | 13 +- src/game/server/teams.cpp | 11 +- 6 files changed, 186 insertions(+), 214 deletions(-) diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 363c8b54c..a523bb562 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -1505,7 +1505,11 @@ void CCharacter::HandleTiles(int Index) { if(Teams()->GetSaving(Team())) { - GameServer()->SendChatTarget(GetPlayer()->GetCID(), "Your team is currently getting saved"); // TODO: better message + if(m_LastStartWarning < Server()->Tick() - 3 * Server()->TickSpeed()) + { + GameServer()->SendChatTarget(GetPlayer()->GetCID(), "You can't start while loading/saving of team is in progress"); + m_LastStartWarning = Server()->Tick(); + } Die(GetPlayer()->GetCID(), WEAPON_WORLD); return; } diff --git a/src/game/server/save.cpp b/src/game/server/save.cpp index 7b33a53a5..2953bfdb3 100644 --- a/src/game/server/save.cpp +++ b/src/game/server/save.cpp @@ -17,13 +17,14 @@ CSaveTee::~CSaveTee() void CSaveTee::save(CCharacter *pChr) { - str_copy(m_aName, pChr->m_pPlayer->Server()->ClientName(pChr->m_pPlayer->GetCID()), sizeof(m_aName)); + m_ClientID = pChr->m_pPlayer->GetCID(); + str_copy(m_aName, pChr->m_pPlayer->Server()->ClientName(m_ClientID), sizeof(m_aName)); m_Alive = pChr->m_Alive; m_Paused = abs(pChr->m_pPlayer->IsPaused()); m_NeededFaketuning = pChr->m_NeededFaketuning; - m_TeeFinished = pChr->Teams()->TeeFinished(pChr->m_pPlayer->GetCID()); + m_TeeFinished = pChr->Teams()->TeeFinished(m_ClientID); m_IsSolo = pChr->m_Solo; for(int i = 0; i< NUM_WEAPONS; i++) @@ -249,7 +250,7 @@ char* CSaveTee::GetString() return m_aString; } -int CSaveTee::LoadString(char* String) +int CSaveTee::LoadString(const char* String) { int Num; Num = sscanf(String, @@ -425,67 +426,10 @@ bool CSaveTeam::HandleSaveError(int Result, int ClientID, CGameContext *pGameCon return true; } -void CSaveTeam::HandleLoadError(int Result, int ClientID, const CSaveTeam &SavedTeam, CGameContext *pGameContext) +void CSaveTeam::load(int Team) { - if(Result == 1) - { - pGameContext->SendChatTarget(ClientID, "You have to be in a team (from 1-63)"); - } - else if(Result == 2) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "Too many players in this team, should be %d", SavedTeam.GetMembersCount()); - pGameContext->SendChatTarget(ClientID, aBuf); - } - else if(Result >= 10 && Result < 100) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "Unable to find player: '%s'", SavedTeam.m_pSavedTees[Result-10].GetName()); - pGameContext->SendChatTarget(ClientID, aBuf); - } - else if(Result >= 100 && Result < 200) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%s is racing right now, Team can't be loaded if a Tee is racing already", SavedTeam.m_pSavedTees[Result-100].GetName()); - pGameContext->SendChatTarget(ClientID, aBuf); - } - else if(Result >= 200) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "Everyone has to be in a team, %s is in team 0 or the wrong team", SavedTeam.m_pSavedTees[Result-200].GetName()); - pGameContext->SendChatTarget(ClientID, aBuf); - } -} - -int CSaveTeam::load(int Team) -{ - if(Team <= 0 || Team >= MAX_CLIENTS) - return 1; - CGameTeams* pTeams = &(((CGameControllerDDRace*)m_pController)->m_Teams); - if(pTeams->Count(Team) > m_MembersCount) - return 2; - - CCharacter *pChr; - - for (int i = 0; i < m_MembersCount; i++) - { - int ID = MatchPlayer(m_pSavedTees[i].GetName()); - if(ID == -1) // first check if team can be loaded / do not load half teams - { - return i+10; // +10 to leave space for other return-values - } - if(m_pController->GameServer()->m_apPlayers[ID] && m_pController->GameServer()->m_apPlayers[ID]->GetCharacter() && m_pController->GameServer()->m_apPlayers[ID]->GetCharacter()->m_DDRaceState) - { - return i+100; // +100 to leave space for other return-values - } - if(Team != pTeams->m_Core.Team(ID)) - { - return i+200; // +100 to leave space for other return-values - } - } - pTeams->ChangeTeamState(Team, m_TeamState); pTeams->SetTeamLock(Team, m_TeamLocked); if(m_Practice) @@ -493,14 +437,16 @@ int CSaveTeam::load(int Team) for (int i = 0; i < m_MembersCount; i++) { - pChr = MatchCharacter(m_pSavedTees[i].GetName(), i); - if(pChr) + int ClientID = m_pSavedTees[i].GetClientID(); + if(m_pController->GameServer()->m_apPlayers[ClientID] && pTeams->m_Core.Team(ClientID) == Team) { + CCharacter *pChr = MatchCharacter(m_pSavedTees[i].GetClientID(), i); m_pSavedTees[i].load(pChr, Team); } } if(m_pController->GameServer()->Collision()->m_NumSwitchers) + { for(int i=1; i < m_pController->GameServer()->Collision()->m_NumSwitchers+1; i++) { m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Status[Team] = m_pSwitchers[i].m_Status; @@ -508,33 +454,15 @@ int CSaveTeam::load(int Team) m_pController->GameServer()->Collision()->m_pSwitchers[i].m_EndTick[Team] = m_pController->Server()->Tick() - m_pSwitchers[i].m_EndTime; m_pController->GameServer()->Collision()->m_pSwitchers[i].m_Type[Team] = m_pSwitchers[i].m_Type; } - return 0; + } } -int CSaveTeam::MatchPlayer(const char name[16]) +CCharacter* CSaveTeam::MatchCharacter(int ClientID, int SaveID) { - for (int i = 0; i < MAX_CLIENTS; i++) - { - if(str_comp(m_pController->Server()->ClientName(i), name) == 0) - { - return i; - } - } - return -1; -} - -CCharacter* CSaveTeam::MatchCharacter(const char name[16], int SaveID) -{ - int ID = MatchPlayer(name); - if(ID >= 0 && m_pController->GameServer()->m_apPlayers[ID]) - { - if(m_pController->GameServer()->m_apPlayers[ID]->GetCharacter()) - return m_pController->GameServer()->m_apPlayers[ID]->GetCharacter(); - else - return m_pController->GameServer()->m_apPlayers[ID]->ForceSpawn(m_pSavedTees[SaveID].GetPos()); - } - - return 0; + if(m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter()) + return m_pController->GameServer()->m_apPlayers[ClientID]->GetCharacter(); + else + return m_pController->GameServer()->m_apPlayers[ClientID]->ForceSpawn(m_pSavedTees[SaveID].GetPos()); } char* CSaveTeam::GetString() @@ -707,3 +635,48 @@ int CSaveTeam::LoadString(const char* String) return 0; } + +bool CSaveTeam::MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientID, int NumPlayer, char *pMessage, int MessageLen) +{ + if(NumPlayer > m_MembersCount) + { + str_format(pMessage, MessageLen, "Too many players in this team, should be %d", m_MembersCount); + return false; + } + // check for wrong players + for(int i = 0; i < NumPlayer; i++) + { + int Found = false; + for(int j = 0; j < m_MembersCount; j++) + { + if(strcmp(paNames[i], m_pSavedTees[j].GetName()) == 0) + { + Found = true; + } + } + if(!Found) + { + str_format(pMessage, MessageLen, "'%s' don't belong to this team", paNames[i]); + return false; + } + } + // check for missing players + for(int i = 0; i < m_MembersCount; i++) + { + int Found = false; + for(int j = 0; j < NumPlayer; j++) + { + if(strcmp(m_pSavedTees[i].GetName(), paNames[j]) == 0) + { + m_pSavedTees[i].SetClientID(pClientID[j]); + Found = true; + } + } + if(!Found) + { + str_format(pMessage, MessageLen, "'%s' has to be in this team", m_pSavedTees[i].GetName()); + return false; + } + } + return true; +} diff --git a/src/game/server/save.h b/src/game/server/save.h index 65fa7f435..6ea6c0305 100644 --- a/src/game/server/save.h +++ b/src/game/server/save.h @@ -13,11 +13,14 @@ public: void save(CCharacter* pchr); void load(CCharacter* pchr, int Team); char* GetString(); - int LoadString(char* String); + int LoadString(const char* String); vec2 GetPos() const { return m_Pos; } const char* GetName() const { return m_aName; } + int GetClientID() const { return m_ClientID; } + void SetClientID(int ClientID) { m_ClientID = ClientID; }; private: + int m_ClientID; char m_aString [2048]; char m_aName [16]; @@ -96,17 +99,18 @@ public: ~CSaveTeam(); char* GetString(); int GetMembersCount() const { return m_MembersCount; } + // MatchPlayers has to be called afterwards int LoadString(const char* String); + // returns true if a team can load, otherwise writes a nice error Message in pMessage + bool MatchPlayers(const char (*paNames)[MAX_NAME_LENGTH], const int *pClientID, int NumPlayer, char *pMessage, int MessageLen); int save(int Team); - int load(int Team); + void load(int Team); CSaveTee* m_pSavedTees; // returns true if an error occured static bool HandleSaveError(int Result, int ClientID, CGameContext *pGameContext); - static void HandleLoadError(int Result, int ClientID, const CSaveTeam &SavedTeam, CGameContext *pGameContext); private: - int MatchPlayer(const char name[16]); - CCharacter* MatchCharacter(const char name[16], int SaveID); + CCharacter* MatchCharacter(int ClientID, int SaveID); IGameController* m_pController; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index bc6cad695..3dd5fdb91 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1503,7 +1503,7 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData(pGameData); - sqlstr::CSqlString<10> SaveState = pData->m_pResult->m_SavedTeam.GetString(); + sqlstr::CSqlString<65536> SaveState = pData->m_pResult->m_SavedTeam.GetString(); if(HandleFailure) { if (!g_Config.m_SvSqlFailureFile[0]) @@ -1593,139 +1593,128 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlDatam_pController)); + int Team = pController->m_Teams.m_Core.Team(ClientID); + if(pController->m_Teams.GetSaving(Team)) + return; + if(Team <= 0 || Team >= MAX_CLIENTS) + { + GameServer()->SendChatTarget(ClientID, "You have to be in a team (from 1-63)"); + return; + } + if(pController->m_Teams.GetTeamState(Team) != CGameTeams::TEAMSTATE_OPEN) + { + GameServer()->SendChatTarget(ClientID, "Team can't be loaded while racing"); + return; + } + auto SaveResult = std::make_shared(ClientID, pController); + pController->m_Teams.SetSaving(Team, SaveResult); + CSqlTeamLoad *Tmp = new CSqlTeamLoad(SaveResult); Tmp->m_Code = Code; + Tmp->m_Map = g_Config.m_SvMap; Tmp->m_ClientID = ClientID; - str_copy(Tmp->m_ClientName, Server()->ClientName(Tmp->m_ClientID), sizeof(Tmp->m_ClientName)); - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(LoadTeamThread, Tmp), "load team"); - */ + Tmp->m_RequestingPlayer = Server()->ClientName(ClientID); + Tmp->m_NumPlayer = 0; + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(pController->m_Teams.m_Core.Team(i) == Team) + { + // put all names at the beginning of the array + str_copy(Tmp->m_aClientNames[Tmp->m_NumPlayer], Server()->ClientName(i), sizeof(Tmp->m_aClientNames[0])); + Tmp->m_aClientID[Tmp->m_NumPlayer] = i; + Tmp->m_NumPlayer++; + } + } + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), "Loading team initiated by '%s'", this->Server()->ClientName(ClientID)); + GameServer()->SendChatTeam(Team, aBuf); + thread_init_and_detach( + CSqlExecData::ExecSqlFunc, + new CSqlExecData(LoadTeamThread, Tmp, false), + "load team"); } -bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* const CSqlTeamLoad *pData = dynamic_cast(pGameData); + pData->m_pResult->m_Status = CSqlSaveResult::LOAD_FAILED; if (HandleFailure) return true; try { - char aBuf[768]; + char aBuf[512]; str_format(aBuf, sizeof(aBuf), "lock tables %s_saves write;", pSqlServer->GetPrefix()); pSqlServer->executeSql(aBuf); - str_format(aBuf, sizeof(aBuf), "select Savegame, Server, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago from %s_saves where Code = '%s' and Map = '%s' and DDNet7 = false;", pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT Savegame, Server, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) AS Ago " + "FROM %s_saves " + "where Code = '%s' AND Map = '%s' AND DDNet7 = false AND Savegame LIKE '%%\\n%s\\t%%';", + pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr(), pData->m_RequestingPlayer.ClrStr()); pSqlServer->executeSqlQuery(aBuf); - if (pSqlServer->GetResults()->rowsCount() > 0) + if(pSqlServer->GetResults()->rowsCount() == 0) { - pSqlServer->GetResults()->first(); - char ServerName[5]; - str_copy(ServerName, pSqlServer->GetResults()->getString("Server").c_str(), sizeof(ServerName)); - if(str_comp(ServerName, g_Config.m_SvSqlServerName)) - { - str_format(aBuf, sizeof(aBuf), "You have to be on the '%s' server to load this savegame", ServerName); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - goto end; - } - - pSqlServer->GetResults()->getInt("Ago"); - int since = pSqlServer->GetResults()->getInt("Ago"); - - if(since < g_Config.m_SvSaveGamesDelay) - { - str_format(aBuf, sizeof(aBuf), "You have to wait %d seconds until you can load this savegame", g_Config.m_SvSaveGamesDelay - since); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - goto end; - } - - CSaveTeam SavedTeam(pData->GameServer()->m_pController); - - int Num = SavedTeam.LoadString(pSqlServer->GetResults()->getString("Savegame").c_str()); - - if(Num) - pData->GameServer()->SendChatTarget(pData->m_ClientID, "Unable to load savegame: data corrupted"); - else - { - bool Found = false; - for (int i = 0; i < SavedTeam.GetMembersCount(); i++) - { - if(str_comp(SavedTeam.m_pSavedTees[i].GetName(), pData->Server()->ClientName(pData->m_ClientID)) == 0) - { - Found = true; - break; - } - } - if(!Found) - pData->GameServer()->SendChatTarget(pData->m_ClientID, "You don't belong to this team"); - else - { - int Team = ((CGameControllerDDRace*)(pData->GameServer()->m_pController))->m_Teams.m_Core.Team(pData->m_ClientID); - - Num = SavedTeam.load(Team); - - if(Num == 1) - { - pData->GameServer()->SendChatTarget(pData->m_ClientID, "You have to be in a team (from 1-63)"); - } - else if(Num == 2) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "Too many players in this team, should be %d", SavedTeam.GetMembersCount()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - else if(Num >= 10 && Num < 100) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "Unable to find player: '%s'", SavedTeam.m_pSavedTees[Num-10].GetName()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - else if(Num >= 100 && Num < 200) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "%s is racing right now, Team can't be loaded if a Tee is racing already", SavedTeam.m_pSavedTees[Num-100].GetName()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - else if(Num >= 200) - { - char aBuf[256]; - str_format(aBuf, sizeof(aBuf), "Everyone has to be in a team, %s is in team 0 or the wrong team", SavedTeam.m_pSavedTees[Num-200].GetName()); - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - else - { - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "Loading successfully done by %s", pData->m_ClientName); - pData->GameServer()->SendChatTeam(Team, aBuf); - str_format(aBuf, sizeof(aBuf), "DELETE from %s_saves where Code='%s' and Map='%s';", pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr()); - pSqlServer->executeSql(aBuf); - } - } - } + strcpy(pData->m_pResult->m_aMessage, "No such savegame for this map"); + goto end; + } + pSqlServer->GetResults()->first(); + auto ServerName = pSqlServer->GetResults()->getString("Server"); + if(str_comp(ServerName.c_str(), g_Config.m_SvSqlServerName) != 0) + { + str_format(pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage), + "You have to be on the '%s' server to load this savegame", ServerName.c_str()); + goto end; } - else - pData->GameServer()->SendChatTarget(pData->m_ClientID, "No such savegame for this map"); - end: - pSqlServer->executeSql("unlock tables;"); - return true; + int Since = pSqlServer->GetResults()->getInt("Ago"); + if(Since < g_Config.m_SvSaveGamesDelay) + { + str_format(pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage), + "You have to wait %d seconds until you can load this savegame", + g_Config.m_SvSaveGamesDelay - Since); + goto end; + } + auto SaveString = pSqlServer->GetResults()->getString("Savegame"); + int Num = pData->m_pResult->m_SavedTeam.LoadString(SaveString.c_str()); + + if(Num != 0) + { + strcpy(pData->m_pResult->m_aMessage, "Unable to load savegame: data corrupted"); + goto end; + } + + bool CanLoad = pData->m_pResult->m_SavedTeam.MatchPlayers( + pData->m_aClientNames, pData->m_aClientID, pData->m_NumPlayer, + pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage)); + + if(!CanLoad) + goto end; + + str_format(aBuf, sizeof(aBuf), + "DELETE FROM %s_saves " + "WHERE Code='%s' AND Map='%s';", + pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr()); + pSqlServer->executeSql(aBuf); + + pData->m_pResult->m_Status = CSqlSaveResult::LOAD_SUCCESS; + strcpy(pData->m_pResult->m_aMessage, "Loading successfully done"); + } catch (sql::SQLException &e) { dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Could not load the team"); - pData->GameServer()->SendChatTarget(pData->m_ClientID, "MySQL Error: Could not load the team"); + strcpy(pData->m_pResult->m_aMessage, "MySQL Error: Could not load the team"); + pSqlServer->executeSql("unlock tables;"); + + return false; } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted loading team due to reload/change of map."); - return true; - } - return false; - */ - return false; + +end: + pSqlServer->executeSql("unlock tables;"); + return true; } void CSqlScore::GetSaves(int ClientID) @@ -1749,7 +1738,7 @@ bool CSqlScore::GetSavesThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), pData->m_Map.ClrStr(), pData->m_RequestingPlayer.ClrStr() diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index d66d1cc43..a9652e64a 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -170,11 +170,17 @@ struct CSqlTeamSave : CSqlData char m_Server[5]; }; -struct CSqlTeamLoad : CSqlData +struct CSqlTeamLoad : CSqlData { + using CSqlData::CSqlData; sqlstr::CSqlString<128> m_Code; + sqlstr::CSqlString<128> m_Map; + sqlstr::CSqlString<128> m_RequestingPlayer; int m_ClientID; - char m_ClientName[MAX_NAME_LENGTH]; + // struct holding all player names in the team or an empty string + char m_aClientNames[MAX_CLIENTS][MAX_NAME_LENGTH]; + int m_aClientID[MAX_CLIENTS]; + int m_NumPlayer; }; struct CSqlRandomMap : CSqlData @@ -230,7 +236,7 @@ class CSqlScore: public IScore static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + 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); @@ -241,7 +247,6 @@ class CSqlScore: public IScore CGameContext *m_pGameServer; IServer *m_pServer; - char m_aMap[64]; char m_aGameUuid[UUID_MAXSTRSIZE]; diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index 065609e90..b21d4838c 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -702,15 +702,12 @@ void CGameTeams::ProcessSaveTeam() break; } case CSqlSaveResult::SAVE_FAILED: - { - int Result = m_pSaveTeamResult[Team]->m_SavedTeam.load(Team); - if(Result != 0) - CSaveTeam::HandleLoadError(Result, m_pSaveTeamResult[Team]->m_RequestingPlayer, m_pSaveTeamResult[Team]->m_SavedTeam, m_pGameContext); - break; - } case CSqlSaveResult::LOAD_SUCCESS: + m_pSaveTeamResult[Team]->m_SavedTeam.load(Team); + break; case CSqlSaveResult::LOAD_FAILED: - break; // TODO + // just printing the error message to the team above is enough + break; } m_pSaveTeamResult[Team] = nullptr; } From dbf6ac88f6343dcdc5a57ed1d05581951c51958e Mon Sep 17 00:00:00 2001 From: Zwelf Date: Thu, 4 Jun 2020 17:33:10 +0200 Subject: [PATCH 34/44] Thread save loading of player time and birthday --- src/game/server/gamecontext.cpp | 17 +-- src/game/server/player.cpp | 23 +++++ src/game/server/score.h | 5 +- src/game/server/score/file_score.cpp | 7 +- src/game/server/score/file_score.h | 3 +- src/game/server/score/sql_score.cpp | 149 ++++++++++++--------------- src/game/server/score/sql_score.h | 28 ++--- 7 files changed, 113 insertions(+), 119 deletions(-) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index b32e813b7..55b60b171 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -1077,8 +1077,7 @@ void CGameContext::OnClientEnter(int ClientID) // Can't set score here as LoadScore() is threaded, run it in // LoadScoreThreaded() instead - Score()->LoadScore(ClientID); - Score()->CheckBirthday(ClientID); + Score()->LoadPlayerData(ClientID); { int Empty = -1; @@ -1878,18 +1877,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) // reload scores Score()->PlayerData(ClientID)->Reset(); - Score()->LoadScore(ClientID); - Score()->PlayerData(ClientID)->m_CurrentTime = Score()->PlayerData(ClientID)->m_BestTime; - - // -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(!Score()->PlayerData(ClientID)->m_BestTime) - m_apPlayers[ClientID]->m_Score = -9999; - else if((int)Score()->PlayerData(ClientID)->m_BestTime == -9999) - m_apPlayers[ClientID]->m_Score = -10000; - else - m_apPlayers[ClientID]->m_Score = Score()->PlayerData(ClientID)->m_BestTime; + m_apPlayers[ClientID]->m_Score = -9999; + Score()->LoadPlayerData(ClientID); } Server()->SetClientClan(ClientID, pMsg->m_pClan); Server()->SetClientCountry(ClientID, pMsg->m_Country); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index b700eed7e..ac05703a7 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -808,6 +808,29 @@ void CPlayer::ProcessSqlResult() case CSqlPlayerResult::MAP_VOTE: // TODO: start vote break; + case CSqlPlayerResult::PLAYER_INFO: + GameServer()->Score()->PlayerData(m_ClientID)->Set( + m_SqlQueryResult->m_Data.m_Info.m_Time, + m_SqlQueryResult->m_Data.m_Info.m_CpTime + ); + printf("%d", m_SqlQueryResult->m_Data.m_Info.m_Score); + m_Score = m_SqlQueryResult->m_Data.m_Info.m_Score; + m_HasFinishScore = m_SqlQueryResult->m_Data.m_Info.m_HasFinishScore; + Server()->ExpireServerInfo(); + int Birthday = m_SqlQueryResult->m_Data.m_Info.m_Birthday; + if(Birthday != 0) + { + char aBuf[512]; + str_format(aBuf, sizeof(aBuf), + "Happy DDNet birthday to %s for finishing their first map %d year%s ago!", + Server()->ClientName(m_ClientID), Birthday, Birthday > 1 ? "s" : ""); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, m_ClientID); + str_format(aBuf, sizeof(aBuf), + "Happy DDNet birthday, %s!\nYou have finished your first map exactly %d year%s ago!", + Server()->ClientName(m_ClientID), Birthday, Birthday > 1 ? "s" : ""); + GameServer()->SendBroadcast(aBuf, m_ClientID); + } + break; } } m_SqlQueryResult = nullptr; diff --git a/src/game/server/score.h b/src/game/server/score.h index 4a5b606ad..13d645b5b 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -19,6 +19,7 @@ public: { Reset(); } + ~CPlayerData() {} void Reset() { @@ -31,6 +32,7 @@ public: void Set(float Time, float CpTime[NUM_CHECKPOINTS]) { m_BestTime = Time; + m_CurrentTime = Time; for(int i = 0; i < NUM_CHECKPOINTS; i++) m_aBestCpTime[i] = CpTime[i]; } @@ -62,8 +64,7 @@ public: virtual void MapInfo(int ClientID, const char *pMapName) = 0; virtual void MapVote(int ClientID, const char *pMapName) = 0; - virtual void CheckBirthday(int ClientID) = 0; - virtual void LoadScore(int ClientID) = 0; + virtual void LoadPlayerData(int ClientID) = 0; virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, float aCpTime[NUM_CHECKPOINTS], bool NotEligible) = 0; virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 6b330f536..4b4184c6d 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -205,12 +205,7 @@ void CFileScore::UpdatePlayer(int ID, float Score, Save(); } -void CFileScore::CheckBirthday(int ClientID) -{ - // TODO: implement -} - -void CFileScore::LoadScore(int ClientID) +void CFileScore::LoadPlayerData(int ClientID) { CPlayerScore *pPlayer = SearchScore(ClientID, 0); if (pPlayer) diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index 41525a4a2..465d95acb 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -60,8 +60,7 @@ public: CFileScore(CGameContext *pGameServer); ~CFileScore(); - virtual void CheckBirthday(int ClientID); - virtual void LoadScore(int ClientID); + virtual void LoadPlayerData(int ClientID); virtual void MapInfo(int ClientID, const char* MapName); virtual void MapVote(int ClientID, const char* MapName); virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 3dd5fdb91..4b6a8e219 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -20,11 +20,31 @@ std::atomic_int CSqlScore::ms_InstanceCount(0); CSqlPlayerResult::CSqlPlayerResult() : - m_Done(0), - m_MessageKind(DIRECT) + m_Done(0) { - for(int i = 0; i < (int)(sizeof(m_aaMessages)/sizeof(m_aaMessages[0])); i++) - m_aaMessages[i][0] = 0; + SetVariant(Variant::DIRECT); +} + +void CSqlPlayerResult::SetVariant(Variant v) +{ + m_MessageKind = v; + switch(v) + { + case DIRECT: + case ALL: + for(int i = 0; i < (int)(sizeof(m_aaMessages)/sizeof(m_aaMessages[0])); i++) + m_aaMessages[i][0] = 0; + break; + case MAP_VOTE: + 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; + for(int i = 0; i < NUM_CHECKPOINTS; i++) + m_Data.m_Info.m_CpTime[i] = 0; + } } CSqlResult::CSqlResult() : @@ -205,72 +225,16 @@ bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameDa return false; } -void CSqlScore::CheckBirthday(int ClientID) +void CSqlScore::LoadPlayerData(int ClientID) { - /* - CSqlPlayerRequest *Tmp = new CSqlPlayerRequest(); - Tmp->m_ClientID = ClientID; - Tmp->m_Name = Server()->ClientName(ClientID); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(CheckBirthdayThread, Tmp), "birthday check"); - */ -} - -bool CSqlScore::CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) -{ - /* TODO - const CSqlPlayerData *pData = dynamic_cast(pGameData); - - if (HandleFailure) - return true; - - try - { - - char aBuf[512]; - - str_format(aBuf, sizeof(aBuf), "select year(Current) - year(Stamp) as YearsAgo from (select CURRENT_TIMESTAMP as Current, min(Timestamp) as Stamp from %s_race WHERE Name='%s') as l where dayofmonth(Current) = dayofmonth(Stamp) and month(Current) = month(Stamp) and year(Current) > year(Stamp);", pSqlServer->GetPrefix(), pData->m_Name.ClrStr()); - pSqlServer->executeSqlQuery(aBuf); - - if(pSqlServer->GetResults()->next()) - { - int yearsAgo = pSqlServer->GetResults()->getInt("YearsAgo"); - str_format(aBuf, sizeof(aBuf), "Happy DDNet birthday to %s for finishing their first map %d year%s ago!", pData->m_Name.Str(), yearsAgo, yearsAgo > 1 ? "s" : ""); - pData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID); - - str_format(aBuf, sizeof(aBuf), "Happy DDNet birthday, %s!\nYou have finished your first map exactly %d year%s ago!", pData->m_Name.Str(), yearsAgo, yearsAgo > 1 ? "s" : ""); - - pData->GameServer()->SendBroadcast(aBuf, pData->m_ClientID); - } - - dbg_msg("sql", "checking birthday done"); - return true; - } - catch (sql::SQLException &e) - { - dbg_msg("sql", "MySQL ERROR: %s", e.what()); - dbg_msg("sql", "ERROR: could not check birthday"); - } - return false; - */ - return false; -} - -void CSqlScore::LoadScore(int ClientID) -{ - /* - CSqlPlayerData *Tmp = new CSqlPlayerData(); - Tmp->m_ClientID = ClientID; - Tmp->m_Name = Server()->ClientName(ClientID); - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(LoadScoreThread, Tmp), "load score"); - */ + ExecPlayerThread(LoadPlayerDataThread, "load player data", ClientID, "", 0); } // update stuff -bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlPlayerData *pData = dynamic_cast(pGameData); + const CSqlPlayerRequest *pData = dynamic_cast(pGameData); + pData->m_pResult->SetVariant(CSqlPlayerResult::PLAYER_INFO); if (HandleFailure) return true; @@ -278,20 +242,27 @@ bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr()); + // get best race time + str_format(aBuf, sizeof(aBuf), + "SELECT * " + "FROM %s_race " + "WHERE Map='%s' AND Name='%s' " + "ORDER BY Time ASC " + "LIMIT 1;", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_RequestingPlayer.ClrStr()); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->next()) { // get the best time float Time = (float)pSqlServer->GetResults()->getDouble("Time"); - pData->PlayerData(pData->m_ClientID)->m_BestTime = Time; - pData->PlayerData(pData->m_ClientID)->m_CurrentTime = Time; - if(pData->GameServer()->m_apPlayers[pData->m_ClientID]) - { - pData->GameServer()->m_apPlayers[pData->m_ClientID]->m_Score = -Time; - pData->GameServer()->m_apPlayers[pData->m_ClientID]->m_HasFinishScore = true; - } + pData->m_pResult->m_Data.m_Info.m_Time = Time; + pData->m_pResult->m_Data.m_Info.m_Score = -Time; + pData->m_pResult->m_Data.m_Info.m_HasFinishScore = true; + // -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(pData->m_pResult->m_Data.m_Info.m_Score == -9999) + pData->m_pResult->m_Data.m_Info.m_Score = -10000; char aColumn[8]; if(g_Config.m_SvCheckpointSave) @@ -299,12 +270,31 @@ bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlDataPlayerData(pData->m_ClientID)->m_aBestCpTime[i] = (float)pSqlServer->GetResults()->getDouble(aColumn); + pData->m_pResult->m_Data.m_Info.m_CpTime[i] = (float)pSqlServer->GetResults()->getDouble(aColumn); } } } - dbg_msg("sql", "Getting best time done"); + // birthday check + str_format(aBuf, sizeof(aBuf), + "SELECT YEAR(Current) - YEAR(Stamp) AS YearsAgo " + "FROM (" + "SELECT CURRENT_TIMESTAMP AS Current, MIN(Timestamp) AS Stamp " + "FROM %s_race " + "WHERE Name='%s'" + ") AS l " + "WHERE DAYOFMONTH(Current) = DAYOFMONTH(Stamp) AND MONTH(Current) = MONTH(Stamp) " + "AND YEAR(Current) > YEAR(Stamp);", + pSqlServer->GetPrefix(), pData->m_RequestingPlayer.ClrStr()); + pSqlServer->executeSqlQuery(aBuf); + + if(pSqlServer->GetResults()->next()) + { + int YearsAgo = pSqlServer->GetResults()->getInt("YearsAgo"); + pData->m_pResult->m_Data.m_Info.m_Birthday = YearsAgo; + } + pData->m_pResult->m_Done = true; + dbg_msg("sql", "Finished loading player data"); return true; } catch (sql::SQLException &e) @@ -312,13 +302,6 @@ bool CSqlScore::LoadScoreThread(CSqlServer* pSqlServer, const CSqlData -{ - int m_ClientID; - sqlstr::CSqlString m_Name; -}; - // used for mapinfo struct CSqlMapData : CSqlData { @@ -222,9 +228,8 @@ class CSqlScore: public IScore static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool CheckBirthdayThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool LoadScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapInfoThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -272,9 +277,8 @@ public: virtual void RandomMap(int ClientID, int Stars); virtual void RandomUnfinishedMap(int ClientID, int Stars); virtual void MapVote(int ClientID, const char* MapName); - virtual void CheckBirthday(int ClientID); - virtual void LoadScore(int ClientID); + virtual void LoadPlayerData(int ClientID); // Requested by players (fails if another request by this player is active) virtual void MapInfo(int ClientID, const char* MapName); virtual void ShowRank(int ClientID, const char* pName); From c278d8dbc3486c6fb694ce910af6bee8c2e463cd Mon Sep 17 00:00:00 2001 From: Zwelf Date: Fri, 5 Jun 2020 14:16:44 +0200 Subject: [PATCH 35/44] Read SaveID from database on /load --- src/game/server/score/sql_score.cpp | 31 ++++++++++++++++++++++++----- src/game/server/score/sql_score.h | 4 ++-- src/game/server/teams.cpp | 22 ++++++++++++++------ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 4b6a8e219..8091c0492 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1466,11 +1466,10 @@ void CSqlScore::SaveTeam(int ClientID, const char* Code, const char* Server) CSqlTeamSave *Tmp = new CSqlTeamSave(SaveResult); Tmp->m_Code = Code; Tmp->m_Map = g_Config.m_SvMap; - FormatUuid(RandomUuid(), Tmp->m_Uuid, UUID_MAXSTRSIZE); + Tmp->m_pResult->m_SaveID = RandomUuid(); str_copy(Tmp->m_Server, Server, sizeof(Tmp->m_Server)); str_copy(Tmp->m_ClientName, this->Server()->ClientName(ClientID), sizeof(Tmp->m_ClientName)); - // TODO: log event in teehistorian pController->m_Teams.KillSavedTeam(ClientID, Team); char aBuf[512]; // TODO: better message, maybe hint that one should wait until the next message to leave @@ -1486,6 +1485,9 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData(pGameData); + char aSaveID[UUID_MAXSTRSIZE]; + FormatUuid(pData->m_pResult->m_SaveID, aSaveID, UUID_MAXSTRSIZE); + sqlstr::CSqlString<65536> SaveState = pData->m_pResult->m_SavedTeam.GetString(); if(HandleFailure) { @@ -1503,7 +1505,7 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlDatam_Map.ClrStr(), - pData->m_Code.ClrStr(), pData->m_Server, pData->m_Uuid + pData->m_Code.ClrStr(), pData->m_Server, aSaveID ); io_write(File, aBuf, str_length(aBuf)); io_write_newline(File); @@ -1537,11 +1539,12 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->rowsCount() == 0) { char aBuf[65536]; + str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, SaveID, DDNet7) " "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', '%s', false)", pSqlServer->GetPrefix(), SaveState.ClrStr(), pData->m_Map.ClrStr(), - pData->m_Code.ClrStr(), pData->m_Server, pData->m_Uuid + pData->m_Code.ClrStr(), pData->m_Server, aSaveID ); dbg_msg("sql", "%s", aBuf); pSqlServer->executeSql(aBuf); @@ -1631,7 +1634,10 @@ bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix()); pSqlServer->executeSql(aBuf); str_format(aBuf, sizeof(aBuf), - "SELECT Savegame, Server, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) AS Ago " + "SELECT " + "Savegame, Server, " + "UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) AS Ago, " + "(UNHEX(REPLACE(SaveID, '-',''))) AS SaveID " "FROM %s_saves " "where Code = '%s' AND Map = '%s' AND DDNet7 = false AND Savegame LIKE '%%\\n%s\\t%%';", pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr(), pData->m_RequestingPlayer.ClrStr()); @@ -1659,6 +1665,21 @@ bool CSqlScore::LoadTeamThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->isNull("SaveID")) + { + memset(pData->m_pResult->m_SaveID.m_aData, 0, sizeof(pData->m_pResult->m_SaveID.m_aData)); + } + else + { + auto SaveID = pSqlServer->GetResults()->getBlob("SaveID"); + SaveID->read((char *) pData->m_pResult->m_SaveID.m_aData, 16); + if(SaveID->gcount() != 16) + { + strcpy(pData->m_pResult->m_aMessage, "Unable to load savegame: SaveID corrupted"); + goto end; + } + } + auto SaveString = pSqlServer->GetResults()->getString("Savegame"); int Num = pData->m_pResult->m_SavedTeam.LoadString(SaveString.c_str()); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 574519852..1a005cf24 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -58,6 +58,7 @@ public: char m_aBroadcast[512]; CSaveTeam m_SavedTeam; int m_RequestingPlayer; + CUuid m_SaveID; }; class CSqlMapResult { @@ -172,7 +173,6 @@ struct CSqlTeamSave : CSqlData sqlstr::CSqlString<128> m_Map; sqlstr::CSqlString<128> m_Code; - char m_Uuid[UUID_MAXSTRSIZE]; char m_Server[5]; }; @@ -181,7 +181,7 @@ struct CSqlTeamLoad : CSqlData using CSqlData::CSqlData; sqlstr::CSqlString<128> m_Code; sqlstr::CSqlString<128> m_Map; - sqlstr::CSqlString<128> m_RequestingPlayer; + sqlstr::CSqlString m_RequestingPlayer; int m_ClientID; // struct holding all player names in the team or an empty string char m_aClientNames[MAX_CLIENTS][MAX_NAME_LENGTH]; diff --git a/src/game/server/teams.cpp b/src/game/server/teams.cpp index b21d4838c..e5fb052ac 100644 --- a/src/game/server/teams.cpp +++ b/src/game/server/teams.cpp @@ -337,9 +337,7 @@ void CGameTeams::ForceLeaveTeam(int ClientID) SetTeamLock(m_Core.Team(ClientID), false); ResetInvited(m_Core.Team(ClientID)); m_Practice[m_Core.Team(ClientID)] = false; -#if defined(CONF_SQL) - m_pSaveTeamResult[m_Core.Team(ClientID)] = nullptr; -#endif + // do not reset SaveTeamResult, because it should be logged into teehistorian even if the team leaves } } @@ -694,19 +692,31 @@ void CGameTeams::ProcessSaveTeam() GameServer()->SendBroadcast(m_pSaveTeamResult[Team]->m_aBroadcast, -1); if(m_pSaveTeamResult[Team]->m_aMessage[0] != '\0') GameServer()->SendChatTeam(Team, m_pSaveTeamResult[Team]->m_aMessage); + // TODO: log load/save success/fail in teehistorian switch(m_pSaveTeamResult[Team]->m_Status) { case CSqlSaveResult::SAVE_SUCCESS: { ResetSavedTeam(m_pSaveTeamResult[Team]->m_RequestingPlayer, Team); + char aSaveID[UUID_MAXSTRSIZE]; + FormatUuid(m_pSaveTeamResult[Team]->m_SaveID, aSaveID, UUID_MAXSTRSIZE); + dbg_msg("save", "Save successful: %s", aSaveID); break; } case CSqlSaveResult::SAVE_FAILED: - case CSqlSaveResult::LOAD_SUCCESS: - m_pSaveTeamResult[Team]->m_SavedTeam.load(Team); + if(m_MembersCount[Team] > 0) + m_pSaveTeamResult[Team]->m_SavedTeam.load(Team); break; + case CSqlSaveResult::LOAD_SUCCESS: + { + if(m_MembersCount[Team] > 0) + m_pSaveTeamResult[Team]->m_SavedTeam.load(Team); + char aSaveID[UUID_MAXSTRSIZE]; + FormatUuid(m_pSaveTeamResult[Team]->m_SaveID, aSaveID, UUID_MAXSTRSIZE); + dbg_msg("save", "Load successful: %s", aSaveID); + break; + } case CSqlSaveResult::LOAD_FAILED: - // just printing the error message to the team above is enough break; } m_pSaveTeamResult[Team] = nullptr; From 62d56ca8e8f74efe96f8cb1a74c4698c328acc35 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Mon, 8 Jun 2020 15:12:52 +0200 Subject: [PATCH 36/44] Make database init with create tables synchronous Note: database init without creating tables behaves the same --- src/engine/server/server.cpp | 19 ++++++++++--------- src/engine/server/server.h | 2 -- src/engine/server/sql_server.cpp | 7 +++++-- src/engine/server/sql_server.h | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 0baa8ddec..8c7d0967d 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -2828,12 +2828,18 @@ void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData) { apSqlServers[i] = new CSqlServer(pResult->GetString(1), pResult->GetString(2), pResult->GetString(3), pResult->GetString(4), pResult->GetString(5), pResult->GetInteger(6), &pSelf->m_GlobalSqlLock, ReadOnly, SetUpDb); - if(SetUpDb) - thread_init(CreateTablesThread, apSqlServers[i], "CreateTables"); - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "Added new Sql%sServer: %d: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d", ReadOnly ? "Read" : "Write", i, apSqlServers[i]->GetDatabase(), apSqlServers[i]->GetPrefix(), apSqlServers[i]->GetUser(), apSqlServers[i]->GetIP(), apSqlServers[i]->GetPort()); + str_format(aBuf, sizeof(aBuf), + "Added new Sql%sServer: %d: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d", + ReadOnly ? "Read" : "Write", i, apSqlServers[i]->GetDatabase(), + apSqlServers[i]->GetPrefix(), apSqlServers[i]->GetUser(), + apSqlServers[i]->GetIP(), apSqlServers[i]->GetPort()); pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); + if(SetUpDb) + { + if(!apSqlServers[i]->CreateTables()) + pSelf->SetErrorShutdown("database create tables failed"); + } return; } } @@ -2866,11 +2872,6 @@ void CServer::ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData) } } -void CServer::CreateTablesThread(void *pData) -{ - ((CSqlServer *)pData)->CreateTables(); -} - #endif void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 6c8a456cb..1889434d0 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -377,8 +377,6 @@ public: // console commands for sqlmasters static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData); static void ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData); - - static void CreateTablesThread(void *pData); #endif static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData); diff --git a/src/engine/server/sql_server.cpp b/src/engine/server/sql_server.cpp index 642dd32c8..58f7bddb8 100644 --- a/src/engine/server/sql_server.cpp +++ b/src/engine/server/sql_server.cpp @@ -167,11 +167,12 @@ void CSqlServer::Disconnect() m_SqlLock.release(); } -void CSqlServer::CreateTables() +bool CSqlServer::CreateTables() { if (!Connect()) - return; + return false; + bool Success = false; try { char aBuf[1024]; @@ -193,6 +194,7 @@ void CSqlServer::CreateTables() executeSql(aBuf); dbg_msg("sql", "Tables were created successfully"); + Success = true; } catch (sql::SQLException &e) { @@ -200,6 +202,7 @@ void CSqlServer::CreateTables() } Disconnect(); + return Success; } void CSqlServer::executeSql(const char *pCommand) diff --git a/src/engine/server/sql_server.h b/src/engine/server/sql_server.h index ee85d5ace..d789f4491 100644 --- a/src/engine/server/sql_server.h +++ b/src/engine/server/sql_server.h @@ -17,7 +17,7 @@ public: bool Connect(); void Disconnect(); - void CreateTables(); + bool CreateTables(); void executeSql(const char *pCommand); void executeSqlQuery(const char *pQuery); From 5dcb6f3b0d57d7e89e9d48752d815323b3face31 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Mon, 8 Jun 2020 13:59:18 +0200 Subject: [PATCH 37/44] Thread save Init --- src/game/server/gamemodes/DDRace.cpp | 15 ++++- src/game/server/gamemodes/DDRace.h | 3 + src/game/server/score/sql_score.cpp | 87 +++++++++++----------------- src/game/server/score/sql_score.h | 82 +++++++------------------- 4 files changed, 72 insertions(+), 115 deletions(-) diff --git a/src/game/server/gamemodes/DDRace.cpp b/src/game/server/gamemodes/DDRace.cpp index b183b9f0d..dd9c76c99 100644 --- a/src/game/server/gamemodes/DDRace.cpp +++ b/src/game/server/gamemodes/DDRace.cpp @@ -9,8 +9,12 @@ #include "DDRace.h" #include "gamemode.h" +#if defined(CONF_SQL) +#include +#endif + CGameControllerDDRace::CGameControllerDDRace(class CGameContext *pGameServer) : - IGameController(pGameServer), m_Teams(pGameServer) + IGameController(pGameServer), m_Teams(pGameServer), m_pInitResult(nullptr) { m_pGameType = g_Config.m_SvTestingCommands ? TEST_NAME : GAME_NAME; @@ -27,6 +31,15 @@ void CGameControllerDDRace::Tick() IGameController::Tick(); #if defined(CONF_SQL) m_Teams.ProcessSaveTeam(); + + if(m_pInitResult != nullptr && m_pInitResult.use_count() == 1) + { + if(m_pInitResult->m_Done) + { + m_CurrentRecord = m_pInitResult->m_CurrentRecord; + } + m_pInitResult = nullptr; + } #endif } diff --git a/src/game/server/gamemodes/DDRace.h b/src/game/server/gamemodes/DDRace.h index 20be25cb5..0db166138 100644 --- a/src/game/server/gamemodes/DDRace.h +++ b/src/game/server/gamemodes/DDRace.h @@ -8,6 +8,7 @@ #include #include +struct CSqlInitResult; class CGameControllerDDRace: public IGameController { public: @@ -22,5 +23,7 @@ public: void InitTeleporter(); virtual void Tick(); + + std::shared_ptr m_pInitResult; }; #endif // GAME_SERVER_GAMEMODES_DDRACE_H diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 8091c0492..bb96053b7 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -47,32 +47,6 @@ void CSqlPlayerResult::SetVariant(Variant v) } } -CSqlResult::CSqlResult() : - m_Done(false), - m_MessageTarget(DIRECT), - m_TeamMessageTo(-1), - m_Tag(NONE) -{ - m_Message[0] = 0; -} - -CSqlResult::~CSqlResult() { - switch(m_Tag) - { - case NONE: - break; - case LOAD: - //m_Variant.m_LoadTeam.~CSaveTeam(); - break; - case RANDOM_MAP: - m_Variant.m_RandomMap.~CRandomMapResult(); - break; - case MAP_VOTE: - m_Variant.m_MapVote.~CMapVoteResult(); - break; - } -} - template < typename TResult > CSqlExecData::CSqlExecData( bool (*pFuncPtr) (CSqlServer*, const CSqlData *, bool), @@ -124,15 +98,6 @@ void CSqlScore::ExecPlayerThread( LOCK CSqlScore::ms_FailureFileLock = lock_create(); -CSqlScore::CSqlScore(CGameContext *pGameServer) : - m_pGameServer(pGameServer), - m_pServer(pGameServer->Server()) -{ - CSqlConnector::ResetReachable(); - - //thread_init_and_detach(ExecSqlFunc, new CSqlExecData(Init, new CSqlData(nullptr)), "SqlScore constructor"); -} - void CSqlScore::OnShutdown() { int i = 0; @@ -189,10 +154,25 @@ void CSqlExecData::ExecSqlFunc(void *pUser) delete pData; } -bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +CSqlScore::CSqlScore(CGameContext *pGameServer) : + m_pGameServer(pGameServer), + m_pServer(pGameServer->Server()) { - /* - const CSqlData* pData = pGameData; + CSqlConnector::ResetReachable(); + + auto InitResult = std::make_shared(); + CSqlInitData *Tmp = new CSqlInitData(InitResult); + ((CGameControllerDDRace*)(pGameServer->m_pController))->m_pInitResult = InitResult; + Tmp->m_Map = g_Config.m_SvMap; + + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(Init, Tmp), + "SqlScore constructor"); +} + +bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +{ + const CSqlInitData *pData = dynamic_cast(pGameData); if (HandleFailure) { @@ -202,26 +182,24 @@ bool CSqlScore::Init(CSqlServer* pSqlServer, const CSqlData *pGameDa try { - char aBuf[1024]; + char aBuf[512]; // get the best time - str_format(aBuf, sizeof(aBuf), "SELECT Time FROM %s_race WHERE Map='%s' ORDER BY `Time` ASC LIMIT 0, 1;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT Time FROM %s_race WHERE Map='%s' ORDER BY `Time` ASC LIMIT 1;", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr()); pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->next()) - { - ((CGameControllerDDRace*)pData->GameServer()->m_pController)->m_CurrentRecord = (float)pSqlServer->GetResults()->getDouble("Time"); + pData->m_pResult->m_CurrentRecord = (float)pSqlServer->GetResults()->getDouble("Time"); - dbg_msg("sql", "Getting best time on server done"); - } + pData->m_pResult->m_Done = true; + dbg_msg("sql", "Getting best time on server done"); return true; } catch (sql::SQLException &e) { dbg_msg("sql", "MySQL Error: %s", e.what()); - dbg_msg("sql", "ERROR: Tables were NOT created"); } - */ - return false; } @@ -497,11 +475,10 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlDataConsole(); if(pCon->m_Cheated) return; - CSqlScoreData *Tmp = new CSqlScoreData(); + CSqlScoreData *Tmp = new CSqlScoreData(nullptr); Tmp->m_ClientID = ClientID; Tmp->m_Name = Server()->ClientName(ClientID); Tmp->m_Time = Time; @@ -510,8 +487,9 @@ void CSqlScore::SaveScore(int ClientID, float Time, const char *pTimestamp, floa for(int i = 0; i < NUM_CHECKPOINTS; i++) Tmp->m_aCpCurrent[i] = CpTime[i]; - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(SaveScoreThread, Tmp, false), "save score"); - */ + thread_init_and_detach(CSqlExecData::ExecSqlFunc, + new CSqlExecData(SaveScoreThread, Tmp), + "save score"); } bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) @@ -595,7 +573,6 @@ bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { + /* const CSqlRandomMap *pData = dynamic_cast(pGameData); if (HandleFailure) @@ -1384,6 +1362,7 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlRandomMap *pData = dynamic_cast(pGameData); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 1a005cf24..05f258431 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -10,8 +10,8 @@ class CSqlServer; -class CSqlPlayerResult { -public: +struct CSqlPlayerResult +{ std::atomic_bool m_Done; CSqlPlayerResult(); @@ -36,8 +36,7 @@ public: void SetVariant(Variant v); }; -class CSqlSaveResult { -public: +struct CSqlSaveResult { CSqlSaveResult(int PlayerID, IGameController* Controller) : m_Status(SAVE_SUCCESS), m_SavedTeam(CSaveTeam(Controller)), @@ -61,43 +60,14 @@ public: CUuid m_SaveID; }; -class CSqlMapResult { - CSqlMapResult(); - std::atomic_bool m_Done; -}; - -// result only valid if m_Done is set to true -class CSqlResult +struct CSqlInitResult { -public: + CSqlInitResult() : + m_Done(false), + m_CurrentRecord(0) + { } std::atomic_bool m_Done; - // specify where chat messages should be returned - enum - { - DIRECT, - TEAM, - ALL, - } m_MessageTarget; - int m_TeamMessageTo; // store team id, if player changes team after /save - char m_Message[512]; - // TODO: replace this with a type-safe std::variant (C++17) - enum - { - NONE, - LOAD, - RANDOM_MAP, - MAP_VOTE, - } m_Tag; - - union - { - //CSaveTeam m_LoadTeam; - CRandomMapResult m_RandomMap; - CMapVoteResult m_MapVote; - } m_Variant; - - CSqlResult(); - ~CSqlResult(); + float m_CurrentRecord; }; // holding relevant data for one thread, and function pointer for return values @@ -111,14 +81,11 @@ struct CSqlData virtual ~CSqlData() = default; }; -// used for mapinfo -struct CSqlMapData : CSqlData +struct CSqlInitData : CSqlData { - using CSqlData::CSqlData; - int m_ClientID; - - sqlstr::CSqlString<128> m_RequestedMap; - sqlstr::CSqlString m_Name; + using CSqlData::CSqlData; + // current map + sqlstr::CSqlString<128> m_Map; }; struct CSqlPlayerRequest : CSqlData @@ -133,18 +100,13 @@ struct CSqlPlayerRequest : CSqlData int m_Offset; }; -// used for mapvote -struct CSqlMapVoteData : CSqlMapData +struct CSqlScoreData : CSqlData { - std::shared_ptr m_pResult; -}; - -struct CSqlScoreData : CSqlData -{ - int m_ClientID; + using CSqlData::CSqlData; sqlstr::CSqlString m_Name; + int m_ClientID; bool m_NotEligible; float m_Time; char m_aTimestamp[TIMESTAMP_STR_LENGTH]; @@ -154,7 +116,7 @@ struct CSqlScoreData : CSqlData char m_aRequestingPlayer[MAX_NAME_LENGTH]; }; -struct CSqlTeamScoreData : CSqlData +struct CSqlTeamScoreData : CSqlData { bool m_NotEligible; float m_Time; @@ -189,7 +151,7 @@ struct CSqlTeamLoad : CSqlData int m_NumPlayer; }; -struct CSqlRandomMap : CSqlData +struct CSqlRandomMap : CSqlData { using CSqlData::CSqlData; int m_Stars; @@ -223,10 +185,10 @@ class CSqlScore: public IScore static LOCK ms_FailureFileLock; - static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); + static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); - static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -295,7 +257,7 @@ public: virtual void SaveTeam(int ClientID, const char* Code, const char* Server); virtual void LoadTeam(const char* Code, int ClientID); - // Game relevant not allowed to fail + // Game relevant not allowed to fail due to an ongoing SQL request. virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, float CpTime[NUM_CHECKPOINTS], bool NotEligible); virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, const char *pTimestamp); From 13c80fdc562c7952743b8b29c59c943334fd5ba9 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Mon, 8 Jun 2020 17:11:41 +0200 Subject: [PATCH 38/44] Thread safe saving of score --- src/game/server/player.cpp | 48 ++++++---- src/game/server/player.h | 3 +- src/game/server/score/sql_score.cpp | 143 +++++++++++++++++++--------- src/game/server/score/sql_score.h | 5 +- 4 files changed, 132 insertions(+), 67 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index ac05703a7..2b7cc62a1 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -123,6 +123,7 @@ void CPlayer::Reset() #if defined(CONF_SQL) m_LastSQLQuery = 0; m_SqlQueryResult = nullptr; + m_SqlFinishResult = nullptr; #endif int64 Now = Server()->Tick(); @@ -148,8 +149,18 @@ void CPlayer::Tick() if(!g_Config.m_DbgDummies || m_ClientID < MAX_CLIENTS-g_Config.m_DbgDummies) #endif #if defined(CONF_SQL) - ProcessSqlResult(); + if(m_SqlQueryResult != nullptr && m_SqlQueryResult.use_count() == 1) + { + ProcessSqlResult(*m_SqlQueryResult); + m_SqlQueryResult = nullptr; + } + if(m_SqlFinishResult != nullptr && m_SqlFinishResult.use_count() == 1) + { + ProcessSqlResult(*m_SqlFinishResult); + m_SqlFinishResult = nullptr; + } #endif + if(!Server()->ClientIngame(m_ClientID)) return; @@ -779,45 +790,45 @@ void CPlayer::SpectatePlayerName(const char *pName) } #if defined(CONF_SQL) -void CPlayer::ProcessSqlResult() +void CPlayer::ProcessSqlResult(CSqlPlayerResult &Result) { - if(m_SqlQueryResult == nullptr || m_SqlQueryResult.use_count() != 1) - return; - - if(m_SqlQueryResult->m_Done) // SQL request was successful + if(Result.m_Done) // SQL request was successful { - int NumMessages = (int)(sizeof(m_SqlQueryResult->m_aaMessages)/sizeof(m_SqlQueryResult->m_aaMessages[0])); - switch(m_SqlQueryResult->m_MessageKind) + int NumMessages = (int)(sizeof(Result.m_aaMessages)/sizeof(Result.m_aaMessages[0])); + switch(Result.m_MessageKind) { case CSqlPlayerResult::DIRECT: for(int i = 0; i < NumMessages; i++) { - if(m_SqlQueryResult->m_aaMessages[i][0] == 0) + if(Result.m_aaMessages[i][0] == 0) break; - GameServer()->SendChatTarget(m_ClientID, m_SqlQueryResult->m_aaMessages[i]); + GameServer()->SendChatTarget(m_ClientID, Result.m_aaMessages[i]); } break; case CSqlPlayerResult::ALL: for(int i = 0; i < NumMessages; i++) { - if(m_SqlQueryResult->m_aaMessages[i][0] == 0) + if(Result.m_aaMessages[i][0] == 0) break; - GameServer()->SendChat(-1, CGameContext::CHAT_ALL, m_SqlQueryResult->m_aaMessages[i]); + GameServer()->SendChat(-1, CGameContext::CHAT_ALL, Result.m_aaMessages[i]); } break; + case CSqlPlayerResult::BROADCAST: + if(Result.m_Data.m_Broadcast[0] != 0) + GameServer()->SendBroadcast(Result.m_Data.m_Broadcast, -1); + break; case CSqlPlayerResult::MAP_VOTE: // TODO: start vote break; case CSqlPlayerResult::PLAYER_INFO: GameServer()->Score()->PlayerData(m_ClientID)->Set( - m_SqlQueryResult->m_Data.m_Info.m_Time, - m_SqlQueryResult->m_Data.m_Info.m_CpTime + Result.m_Data.m_Info.m_Time, + Result.m_Data.m_Info.m_CpTime ); - printf("%d", m_SqlQueryResult->m_Data.m_Info.m_Score); - m_Score = m_SqlQueryResult->m_Data.m_Info.m_Score; - m_HasFinishScore = m_SqlQueryResult->m_Data.m_Info.m_HasFinishScore; + m_Score = Result.m_Data.m_Info.m_Score; + m_HasFinishScore = Result.m_Data.m_Info.m_HasFinishScore; Server()->ExpireServerInfo(); - int Birthday = m_SqlQueryResult->m_Data.m_Info.m_Birthday; + int Birthday = Result.m_Data.m_Info.m_Birthday; if(Birthday != 0) { char aBuf[512]; @@ -833,6 +844,5 @@ void CPlayer::ProcessSqlResult() break; } } - m_SqlQueryResult = nullptr; } #endif diff --git a/src/game/server/player.h b/src/game/server/player.h index f6be28f4a..d43bc6016 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -198,9 +198,10 @@ public: bool m_Halloween; bool m_FirstPacket; #if defined(CONF_SQL) - void ProcessSqlResult(); + void ProcessSqlResult(CSqlPlayerResult &Result); int64 m_LastSQLQuery; std::shared_ptr m_SqlQueryResult; + std::shared_ptr m_SqlFinishResult; #endif bool m_NotEligibleForFinish; int64 m_EligibleForFinishCheck; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index bb96053b7..327e2c6a3 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -20,7 +20,7 @@ std::atomic_int CSqlScore::ms_InstanceCount(0); CSqlPlayerResult::CSqlPlayerResult() : - m_Done(0) + m_Done(false) { SetVariant(Variant::DIRECT); } @@ -35,6 +35,9 @@ void CSqlPlayerResult::SetVariant(Variant v) for(int i = 0; i < (int)(sizeof(m_aaMessages)/sizeof(m_aaMessages[0])); i++) m_aaMessages[i][0] = 0; break; + case BROADCAST: + m_Data.m_Broadcast[0] = 0; + break; case MAP_VOTE: break; case PLAYER_INFO: @@ -476,14 +479,20 @@ bool CSqlScore::MapInfoThread(CSqlServer* pSqlServer, const CSqlDataConsole(); - if(pCon->m_Cheated) + if(pCon->m_Cheated || NotEligible) return; - CSqlScoreData *Tmp = new CSqlScoreData(nullptr); + + CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientID]; + if(pCurPlayer->m_SqlFinishResult != nullptr) + dbg_msg("sql", "WARNING: previous save score result didn't complete, overwriting it now"); + pCurPlayer->m_SqlFinishResult = std::make_shared(); + CSqlScoreData *Tmp = new CSqlScoreData(pCurPlayer->m_SqlFinishResult); + Tmp->m_Map = g_Config.m_SvMap; + FormatUuid(GameServer()->GameUuid(), Tmp->m_GameUuid, sizeof(Tmp->m_GameUuid)); Tmp->m_ClientID = ClientID; Tmp->m_Name = Server()->ClientName(ClientID); Tmp->m_Time = Time; str_copy(Tmp->m_aTimestamp, pTimestamp, sizeof(Tmp->m_aTimestamp)); - Tmp->m_NotEligible = NotEligible; for(int i = 0; i < NUM_CHECKPOINTS; i++) Tmp->m_aCpCurrent[i] = CpTime[i]; @@ -494,48 +503,70 @@ void CSqlScore::SaveScore(int ClientID, float Time, const char *pTimestamp, floa bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* const CSqlScoreData *pData = dynamic_cast(pGameData); + auto paMessages = pData->m_pResult->m_aaMessages; - if (HandleFailure) + if(HandleFailure) { - if (!g_Config.m_SvSqlFailureFile[0]) + if(!g_Config.m_SvSqlFailureFile[0]) return true; lock_wait(ms_FailureFileLock); IOHANDLE File = io_open(g_Config.m_SvSqlFailureFile, IOFLAG_APPEND); - if(File) + if(File == 0) { - dbg_msg("sql", "ERROR: Could not save Score, writing insert to a file now..."); - - char aBuf[768]; - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %%s_race(Map, Name, Timestamp, Time, Server, 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, GameID, DDNet7) VALUES ('%s', '%s', '%s', '%.2f', '%s', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%s', false);", pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), pData->m_aTimestamp, pData->m_Time, g_Config.m_SvSqlServerName, pData->m_aCpCurrent[0], pData->m_aCpCurrent[1], pData->m_aCpCurrent[2], pData->m_aCpCurrent[3], pData->m_aCpCurrent[4], pData->m_aCpCurrent[5], pData->m_aCpCurrent[6], pData->m_aCpCurrent[7], pData->m_aCpCurrent[8], pData->m_aCpCurrent[9], pData->m_aCpCurrent[10], pData->m_aCpCurrent[11], pData->m_aCpCurrent[12], pData->m_aCpCurrent[13], pData->m_aCpCurrent[14], pData->m_aCpCurrent[15], pData->m_aCpCurrent[16], pData->m_aCpCurrent[17], pData->m_aCpCurrent[18], pData->m_aCpCurrent[19], pData->m_aCpCurrent[20], pData->m_aCpCurrent[21], pData->m_aCpCurrent[22], pData->m_aCpCurrent[23], pData->m_aCpCurrent[24], pData->m_GameUuid.ClrStr()); - io_write(File, aBuf, str_length(aBuf)); - io_write_newline(File); - io_close(File); - lock_unlock(ms_FailureFileLock); - - pData->GameServer()->SendBroadcast("Database connection failed, score written to a file instead. Admins will add it manually in a few days.", -1); - - return true; + lock_unlock(ms_FailureFileLock); + dbg_msg("sql", "ERROR: Could not save Score, NOT even to a file"); + return false; } - lock_unlock(ms_FailureFileLock); - dbg_msg("sql", "ERROR: Could not save Score, NOT even to a file"); - return false; - } + dbg_msg("sql", "ERROR: Could not save Score, writing insert to a file now..."); - if(pData->m_NotEligible) - { - return false; + char aBuf[1024]; + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %%s_race(Map, Name, Timestamp, Time, Server, " + "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, " + "GameID, DDNet7) " + "VALUES ('%s', '%s', '%s', '%.2f', '%s'," + "'%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', " + "'%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', " + "'%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', " + "'%.2f', '%s', false);", + pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), + pData->m_aTimestamp, pData->m_Time, g_Config.m_SvSqlServerName, + pData->m_aCpCurrent[0], pData->m_aCpCurrent[1], pData->m_aCpCurrent[2], + pData->m_aCpCurrent[3], pData->m_aCpCurrent[4], pData->m_aCpCurrent[5], + pData->m_aCpCurrent[6], pData->m_aCpCurrent[7], pData->m_aCpCurrent[8], + pData->m_aCpCurrent[9], pData->m_aCpCurrent[10], pData->m_aCpCurrent[11], + pData->m_aCpCurrent[12], pData->m_aCpCurrent[13], pData->m_aCpCurrent[14], + pData->m_aCpCurrent[15], pData->m_aCpCurrent[16], pData->m_aCpCurrent[17], + pData->m_aCpCurrent[18], pData->m_aCpCurrent[19], pData->m_aCpCurrent[20], + pData->m_aCpCurrent[21], pData->m_aCpCurrent[22], pData->m_aCpCurrent[23], + pData->m_aCpCurrent[24], + pData->m_GameUuid); + io_write(File, aBuf, str_length(aBuf)); + io_write_newline(File); + io_close(File); + lock_unlock(ms_FailureFileLock); + + pData->m_pResult->SetVariant(CSqlPlayerResult::BROADCAST); + strcpy(pData->m_pResult->m_Data.m_Broadcast, + "Database connection failed, score written to a file instead. Admins will add it manually in a few days."); + pData->m_pResult->m_Done = true; + return true; } try { - char aBuf[768]; + char aBuf[1024]; - str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_race WHERE Map='%s' AND Name='%s' ORDER BY time ASC LIMIT 1;", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr()); + str_format(aBuf, sizeof(aBuf), + "SELECT COUNT(*) AS NumFinished FROM %s_race WHERE Map='%s' AND Name='%s' ORDER BY time ASC LIMIT 1;", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr()); pSqlServer->executeSqlQuery(aBuf); - if(!pSqlServer->GetResults()->next()) + pSqlServer->GetResults()->first(); + int NumFinished = pSqlServer->GetResults()->getInt("NumFinished"); + if(NumFinished == 0) { str_format(aBuf, sizeof(aBuf), "SELECT Points FROM %s_maps WHERE Map ='%s'", pSqlServer->GetPrefix(), pData->m_Map.ClrStr()); pSqlServer->executeSqlQuery(aBuf); @@ -543,37 +574,57 @@ bool CSqlScore::SaveScoreThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->rowsCount() == 1) { pSqlServer->GetResults()->next(); - int points = pSqlServer->GetResults()->getInt("Points"); - if (points == 1) - str_format(aBuf, sizeof(aBuf), "You earned %d point for finishing this map!", points); + int Points = pSqlServer->GetResults()->getInt("Points"); + if(Points == 1) + str_format(paMessages[0], sizeof(paMessages[0]), "You earned %d point for finishing this map!", Points); else - str_format(aBuf, sizeof(aBuf), "You earned %d points for finishing this map!", points); + str_format(paMessages[0], sizeof(paMessages[0]), "You earned %d points for finishing this map!", Points); - try - { - pData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); - } - catch (CGameContextError &e) {} // just do nothing, it is not much of a problem if the player is not informed about points during mapchange - - str_format(aBuf, sizeof(aBuf), "INSERT INTO %s_points(Name, Points) VALUES ('%s', '%d') ON duplicate key UPDATE Name=VALUES(Name), Points=Points+VALUES(Points);", pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), points); + str_format(aBuf, sizeof(aBuf), + "INSERT INTO %s_points(Name, Points) " + "VALUES ('%s', '%d') " + "ON duplicate key " + "UPDATE Name=VALUES(Name), Points=Points+VALUES(Points);", + pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), Points); pSqlServer->executeSql(aBuf); } } - // if no entry found... create a new one - str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_race(Map, Name, Timestamp, Time, Server, 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, GameID, DDNet7) VALUES ('%s', '%s', '%s', '%.2f', '%s', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%s', false);", pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), pData->m_aTimestamp, pData->m_Time, g_Config.m_SvSqlServerName, pData->m_aCpCurrent[0], pData->m_aCpCurrent[1], pData->m_aCpCurrent[2], pData->m_aCpCurrent[3], pData->m_aCpCurrent[4], pData->m_aCpCurrent[5], pData->m_aCpCurrent[6], pData->m_aCpCurrent[7], pData->m_aCpCurrent[8], pData->m_aCpCurrent[9], pData->m_aCpCurrent[10], pData->m_aCpCurrent[11], pData->m_aCpCurrent[12], pData->m_aCpCurrent[13], pData->m_aCpCurrent[14], pData->m_aCpCurrent[15], pData->m_aCpCurrent[16], pData->m_aCpCurrent[17], pData->m_aCpCurrent[18], pData->m_aCpCurrent[19], pData->m_aCpCurrent[20], pData->m_aCpCurrent[21], pData->m_aCpCurrent[22], pData->m_aCpCurrent[23], pData->m_aCpCurrent[24], pData->m_GameUuid.ClrStr()); + // save score + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %s_race(" + "Map, Name, Timestamp, Time, Server, " + "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, " + "GameID, DDNet7) " + "VALUES ('%s', '%s', '%s', '%.2f', '%s', " + "'%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', " + "'%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', " + "'%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', '%.2f', " + "'%s', false);", + pSqlServer->GetPrefix(), pData->m_Map.ClrStr(), pData->m_Name.ClrStr(), + pData->m_aTimestamp, pData->m_Time, g_Config.m_SvSqlServerName, + pData->m_aCpCurrent[0], pData->m_aCpCurrent[1], pData->m_aCpCurrent[2], + pData->m_aCpCurrent[3], pData->m_aCpCurrent[4], pData->m_aCpCurrent[5], + pData->m_aCpCurrent[6], pData->m_aCpCurrent[7], pData->m_aCpCurrent[8], + pData->m_aCpCurrent[9], pData->m_aCpCurrent[10], pData->m_aCpCurrent[11], + pData->m_aCpCurrent[12], pData->m_aCpCurrent[13], pData->m_aCpCurrent[14], + pData->m_aCpCurrent[15], pData->m_aCpCurrent[16], pData->m_aCpCurrent[17], + pData->m_aCpCurrent[18], pData->m_aCpCurrent[19], pData->m_aCpCurrent[20], + pData->m_aCpCurrent[21], pData->m_aCpCurrent[22], pData->m_aCpCurrent[23], + pData->m_aCpCurrent[24], pData->m_GameUuid); dbg_msg("sql", "%s", aBuf); pSqlServer->executeSql(aBuf); - dbg_msg("sql", "Updating time done"); + pData->m_pResult->m_Done = true; + dbg_msg("sql", "Saving score done"); return true; } catch (sql::SQLException &e) { dbg_msg("sql", "MySQL Error: %s", e.what()); - dbg_msg("sql", "ERROR: Could not update time"); + dbg_msg("sql", "ERROR: Could not insert time"); } - */ return false; } diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 05f258431..dede0c242 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -19,11 +19,13 @@ struct CSqlPlayerResult { DIRECT, ALL, + BROADCAST, MAP_VOTE, // 3 Messages: Reason, Server, Map PLAYER_INFO, } m_MessageKind; char m_aaMessages[7][512]; union { + char m_Broadcast[1024]; struct { float m_Time; float m_CpTime[NUM_CHECKPOINTS]; @@ -104,10 +106,11 @@ struct CSqlScoreData : CSqlData { using CSqlData::CSqlData; + sqlstr::CSqlString m_Map; + char m_GameUuid[UUID_MAXSTRSIZE]; sqlstr::CSqlString m_Name; int m_ClientID; - bool m_NotEligible; float m_Time; char m_aTimestamp[TIMESTAMP_STR_LENGTH]; float m_aCpCurrent[NUM_CHECKPOINTS]; From 528ca377ed2c2fb4f7dad28bd2c446490519af15 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 10 Jun 2020 11:26:40 +0200 Subject: [PATCH 39/44] 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; } From ea31171873a324772e7b89942dea6badb0b39864 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 10 Jun 2020 22:58:49 +0200 Subject: [PATCH 40/44] Generate passphrase for save-code * when none is given * if the given save-code already exist * if the database connection fails therefore it can't be checked if the save-code already exists Using the short word list from eff: https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases --- data/wordlist.txt | 1296 +++++++++++++++++++++++++++ src/engine/server/sql_server.h | 1 + src/game/server/ddracechat.cpp | 11 +- src/game/server/ddracechat.h | 2 +- src/game/server/score/sql_score.cpp | 162 +++- src/game/server/score/sql_score.h | 13 +- 6 files changed, 1436 insertions(+), 49 deletions(-) create mode 100644 data/wordlist.txt diff --git a/data/wordlist.txt b/data/wordlist.txt new file mode 100644 index 000000000..fb0a9e35e --- /dev/null +++ b/data/wordlist.txt @@ -0,0 +1,1296 @@ +1111 acid +1112 acorn +1113 acre +1114 acts +1115 afar +1116 affix +1121 aged +1122 agent +1123 agile +1124 aging +1125 agony +1126 ahead +1131 aide +1132 aids +1133 aim +1134 ajar +1135 alarm +1136 alias +1141 alibi +1142 alien +1143 alike +1144 alive +1145 aloe +1146 aloft +1151 aloha +1152 alone +1153 amend +1154 amino +1155 ample +1156 amuse +1161 angel +1162 anger +1163 angle +1164 ankle +1165 apple +1166 april +1211 apron +1212 aqua +1213 area +1214 arena +1215 argue +1216 arise +1221 armed +1222 armor +1223 army +1224 aroma +1225 array +1226 arson +1231 art +1232 ashen +1233 ashes +1234 atlas +1235 atom +1236 attic +1241 audio +1242 avert +1243 avoid +1244 awake +1245 award +1246 awoke +1251 axis +1252 bacon +1253 badge +1254 bagel +1255 baggy +1256 baked +1261 baker +1262 balmy +1263 banjo +1264 barge +1265 barn +1266 bash +1311 basil +1312 bask +1313 batch +1314 bath +1315 baton +1316 bats +1321 blade +1322 blank +1323 blast +1324 blaze +1325 bleak +1326 blend +1331 bless +1332 blimp +1333 blink +1334 bloat +1335 blob +1336 blog +1341 blot +1342 blunt +1343 blurt +1344 blush +1345 boast +1346 boat +1351 body +1352 boil +1353 bok +1354 bolt +1355 boned +1356 boney +1361 bonus +1362 bony +1363 book +1364 booth +1365 boots +1366 boss +1411 botch +1412 both +1413 boxer +1414 breed +1415 bribe +1416 brick +1421 bride +1422 brim +1423 bring +1424 brink +1425 brisk +1426 broad +1431 broil +1432 broke +1433 brook +1434 broom +1435 brush +1436 buck +1441 bud +1442 buggy +1443 bulge +1444 bulk +1445 bully +1446 bunch +1451 bunny +1452 bunt +1453 bush +1454 bust +1455 busy +1456 buzz +1461 cable +1462 cache +1463 cadet +1464 cage +1465 cake +1466 calm +1511 cameo +1512 canal +1513 candy +1514 cane +1515 canon +1516 cape +1521 card +1522 cargo +1523 carol +1524 carry +1525 carve +1526 case +1531 cash +1532 cause +1533 cedar +1534 chain +1535 chair +1536 chant +1541 chaos +1542 charm +1543 chase +1544 cheek +1545 cheer +1546 chef +1551 chess +1552 chest +1553 chew +1554 chief +1555 chili +1556 chill +1561 chip +1562 chomp +1563 chop +1564 chow +1565 chuck +1566 chump +1611 chunk +1612 churn +1613 chute +1614 cider +1615 cinch +1616 city +1621 civic +1622 civil +1623 clad +1624 claim +1625 clamp +1626 clap +1631 clash +1632 clasp +1633 class +1634 claw +1635 clay +1636 clean +1641 clear +1642 cleat +1643 cleft +1644 clerk +1645 click +1646 cling +1651 clink +1652 clip +1653 cloak +1654 clock +1655 clone +1656 cloth +1661 cloud +1662 clump +1663 coach +1664 coast +1665 coat +1666 cod +2111 coil +2112 coke +2113 cola +2114 cold +2115 colt +2116 coma +2121 come +2122 comic +2123 comma +2124 cone +2125 cope +2126 copy +2131 coral +2132 cork +2133 cost +2134 cot +2135 couch +2136 cough +2141 cover +2142 cozy +2143 craft +2144 cramp +2145 crane +2146 crank +2151 crate +2152 crave +2153 crawl +2154 crazy +2155 creme +2156 crepe +2161 crept +2162 crib +2163 cried +2164 crisp +2165 crook +2166 crop +2211 cross +2212 crowd +2213 crown +2214 crumb +2215 crush +2216 crust +2221 cub +2222 cult +2223 cupid +2224 cure +2225 curl +2226 curry +2231 curse +2232 curve +2233 curvy +2234 cushy +2235 cut +2236 cycle +2241 dab +2242 dad +2243 daily +2244 dairy +2245 daisy +2246 dance +2251 dandy +2252 darn +2253 dart +2254 dash +2255 data +2256 date +2261 dawn +2262 deaf +2263 deal +2264 dean +2265 debit +2266 debt +2311 debug +2312 decaf +2313 decal +2314 decay +2315 deck +2316 decor +2321 decoy +2322 deed +2323 delay +2324 denim +2325 dense +2326 dent +2331 depth +2332 derby +2333 desk +2334 dial +2335 diary +2336 dice +2341 dig +2342 dill +2343 dime +2344 dimly +2345 diner +2346 dingy +2351 disco +2352 dish +2353 disk +2354 ditch +2355 ditzy +2356 dizzy +2361 dock +2362 dodge +2363 doing +2364 doll +2365 dome +2366 donor +2411 donut +2412 dose +2413 dot +2414 dove +2415 down +2416 dowry +2421 doze +2422 drab +2423 drama +2424 drank +2425 draw +2426 dress +2431 dried +2432 drift +2433 drill +2434 drive +2435 drone +2436 droop +2441 drove +2442 drown +2443 drum +2444 dry +2445 duck +2446 duct +2451 dude +2452 dug +2453 duke +2454 duo +2455 dusk +2456 dust +2461 duty +2462 dwarf +2463 dwell +2464 eagle +2465 early +2466 earth +2511 easel +2512 east +2513 eaten +2514 eats +2515 ebay +2516 ebony +2521 ebook +2522 echo +2523 edge +2524 eel +2525 eject +2526 elbow +2531 elder +2532 elf +2533 elk +2534 elm +2535 elope +2536 elude +2541 elves +2542 email +2543 emit +2544 empty +2545 emu +2546 enter +2551 entry +2552 envoy +2553 equal +2554 erase +2555 error +2556 erupt +2561 essay +2562 etch +2563 evade +2564 even +2565 evict +2566 evil +2611 evoke +2612 exact +2613 exit +2614 fable +2615 faced +2616 fact +2621 fade +2622 fall +2623 false +2624 fancy +2625 fang +2626 fax +2631 feast +2632 feed +2633 femur +2634 fence +2635 fend +2636 ferry +2641 fetal +2642 fetch +2643 fever +2644 fiber +2645 fifth +2646 fifty +2651 film +2652 filth +2653 final +2654 finch +2655 fit +2656 five +2661 flag +2662 flaky +2663 flame +2664 flap +2665 flask +2666 fled +3111 flick +3112 fling +3113 flint +3114 flip +3115 flirt +3116 float +3121 flock +3122 flop +3123 floss +3124 flyer +3125 foam +3126 foe +3131 fog +3132 foil +3133 folic +3134 folk +3135 food +3136 fool +3141 found +3142 fox +3143 foyer +3144 frail +3145 frame +3146 fray +3151 fresh +3152 fried +3153 frill +3154 frisk +3155 from +3156 front +3161 frost +3162 froth +3163 frown +3164 froze +3165 fruit +3166 gag +3211 gains +3212 gala +3213 game +3214 gap +3215 gas +3216 gave +3221 gear +3222 gecko +3223 geek +3224 gem +3225 genre +3226 gift +3231 gig +3232 gills +3233 given +3234 giver +3235 glad +3236 glass +3241 glide +3242 gloss +3243 glove +3244 glow +3245 glue +3246 goal +3251 going +3252 golf +3253 gong +3254 good +3255 gooey +3256 goofy +3261 gore +3262 gown +3263 grab +3264 grain +3265 grant +3266 grape +3311 graph +3312 grasp +3313 grass +3314 grave +3315 gravy +3316 gray +3321 green +3322 greet +3323 grew +3324 grid +3325 grief +3326 grill +3331 grip +3332 grit +3333 groom +3334 grope +3335 growl +3336 grub +3341 grunt +3342 guide +3343 gulf +3344 gulp +3345 gummy +3346 guru +3351 gush +3352 gut +3353 guy +3354 habit +3355 half +3356 halo +3361 halt +3362 happy +3363 harm +3364 hash +3365 hasty +3366 hatch +3411 hate +3412 haven +3413 hazel +3414 hazy +3415 heap +3416 heat +3421 heave +3422 hedge +3423 hefty +3424 help +3425 herbs +3426 hers +3431 hub +3432 hug +3433 hula +3434 hull +3435 human +3436 humid +3441 hump +3442 hung +3443 hunk +3444 hunt +3445 hurry +3446 hurt +3451 hush +3452 hut +3453 ice +3454 icing +3455 icon +3456 icy +3461 igloo +3462 image +3463 ion +3464 iron +3465 islam +3466 issue +3511 item +3512 ivory +3513 ivy +3514 jab +3515 jam +3516 jaws +3521 jazz +3522 jeep +3523 jelly +3524 jet +3525 jiffy +3526 job +3531 jog +3532 jolly +3533 jolt +3534 jot +3535 joy +3536 judge +3541 juice +3542 juicy +3543 july +3544 jumbo +3545 jump +3546 junky +3551 juror +3552 jury +3553 keep +3554 keg +3555 kept +3556 kick +3561 kilt +3562 king +3563 kite +3564 kitty +3565 kiwi +3566 knee +3611 knelt +3612 koala +3613 kung +3614 ladle +3615 lady +3616 lair +3621 lake +3622 lance +3623 land +3624 lapel +3625 large +3626 lash +3631 lasso +3632 last +3633 latch +3634 late +3635 lazy +3636 left +3641 legal +3642 lemon +3643 lend +3644 lens +3645 lent +3646 level +3651 lever +3652 lid +3653 life +3654 lift +3655 lilac +3656 lily +3661 limb +3662 limes +3663 line +3664 lint +3665 lion +3666 lip +4111 list +4112 lived +4113 liver +4114 lunar +4115 lunch +4116 lung +4121 lurch +4122 lure +4123 lurk +4124 lying +4125 lyric +4126 mace +4131 maker +4132 malt +4133 mama +4134 mango +4135 manor +4136 many +4141 map +4142 march +4143 mardi +4144 marry +4145 mash +4146 match +4151 mate +4152 math +4153 moan +4154 mocha +4155 moist +4156 mold +4161 mom +4162 moody +4163 mop +4164 morse +4165 most +4166 motor +4211 motto +4212 mount +4213 mouse +4214 mousy +4215 mouth +4216 move +4221 movie +4222 mower +4223 mud +4224 mug +4225 mulch +4226 mule +4231 mull +4232 mumbo +4233 mummy +4234 mural +4235 muse +4236 music +4241 musky +4242 mute +4243 nacho +4244 nag +4245 nail +4246 name +4251 nanny +4252 nap +4253 navy +4254 near +4255 neat +4256 neon +4261 nerd +4262 nest +4263 net +4264 next +4265 niece +4266 ninth +4311 nutty +4312 oak +4313 oasis +4314 oat +4315 ocean +4316 oil +4321 old +4322 olive +4323 omen +4324 onion +4325 only +4326 ooze +4331 opal +4332 open +4333 opera +4334 opt +4335 otter +4336 ouch +4341 ounce +4342 outer +4343 oval +4344 oven +4345 owl +4346 ozone +4351 pace +4352 pagan +4353 pager +4354 palm +4355 panda +4356 panic +4361 pants +4362 panty +4363 paper +4364 park +4365 party +4366 pasta +4411 patch +4412 path +4413 patio +4414 payer +4415 pecan +4416 penny +4421 pep +4422 perch +4423 perky +4424 perm +4425 pest +4426 petal +4431 petri +4432 petty +4433 photo +4434 plank +4435 plant +4436 plaza +4441 plead +4442 plot +4443 plow +4444 pluck +4445 plug +4446 plus +4451 poach +4452 pod +4453 poem +4454 poet +4455 pogo +4456 point +4461 poise +4462 poker +4463 polar +4464 polio +4465 polka +4466 polo +4511 pond +4512 pony +4513 poppy +4514 pork +4515 poser +4516 pouch +4521 pound +4522 pout +4523 power +4524 prank +4525 press +4526 print +4531 prior +4532 prism +4533 prize +4534 probe +4535 prong +4536 proof +4541 props +4542 prude +4543 prune +4544 pry +4545 pug +4546 pull +4551 pulp +4552 pulse +4553 puma +4554 punch +4555 punk +4556 pupil +4561 puppy +4562 purr +4563 purse +4564 push +4565 putt +4566 quack +4611 quake +4612 query +4613 quiet +4614 quill +4615 quilt +4616 quit +4621 quota +4622 quote +4623 rabid +4624 race +4625 rack +4626 radar +4631 radio +4632 raft +4633 rage +4634 raid +4635 rail +4636 rake +4641 rally +4642 ramp +4643 ranch +4644 range +4645 rank +4646 rant +4651 rash +4652 raven +4653 reach +4654 react +4655 ream +4656 rebel +4661 recap +4662 relax +4663 relay +4664 relic +4665 remix +4666 repay +5111 repel +5112 reply +5113 rerun +5114 reset +5115 rhyme +5116 rice +5121 rich +5122 ride +5123 rigid +5124 rigor +5125 rinse +5126 riot +5131 ripen +5132 rise +5133 risk +5134 ritzy +5135 rival +5136 river +5141 roast +5142 robe +5143 robin +5144 rock +5145 rogue +5146 roman +5151 romp +5152 rope +5153 rover +5154 royal +5155 ruby +5156 rug +5161 ruin +5162 rule +5163 runny +5164 rush +5165 rust +5166 rut +5211 sadly +5212 sage +5213 said +5214 saint +5215 salad +5216 salon +5221 salsa +5222 salt +5223 same +5224 sandy +5225 santa +5226 satin +5231 sauna +5232 saved +5233 savor +5234 sax +5235 say +5236 scale +5241 scam +5242 scan +5243 scare +5244 scarf +5245 scary +5246 scoff +5251 scold +5252 scoop +5253 scoot +5254 scope +5255 score +5256 scorn +5261 scout +5262 scowl +5263 scrap +5264 scrub +5265 scuba +5266 scuff +5311 sect +5312 sedan +5313 self +5314 send +5315 sepia +5316 serve +5321 set +5322 seven +5323 shack +5324 shade +5325 shady +5326 shaft +5331 shaky +5332 sham +5333 shape +5334 share +5335 sharp +5336 shed +5341 sheep +5342 sheet +5343 shelf +5344 shell +5345 shine +5346 shiny +5351 ship +5352 shirt +5353 shock +5354 shop +5355 shore +5356 shout +5361 shove +5362 shown +5363 showy +5364 shred +5365 shrug +5366 shun +5411 shush +5412 shut +5413 shy +5414 sift +5415 silk +5416 silly +5421 silo +5422 sip +5423 siren +5424 sixth +5425 size +5426 skate +5431 skew +5432 skid +5433 skier +5434 skies +5435 skip +5436 skirt +5441 skit +5442 sky +5443 slab +5444 slack +5445 slain +5446 slam +5451 slang +5452 slash +5453 slate +5454 slaw +5455 sled +5456 sleek +5461 sleep +5462 sleet +5463 slept +5464 slice +5465 slick +5466 slimy +5511 sling +5512 slip +5513 slit +5514 slob +5515 slot +5516 slug +5521 slum +5522 slurp +5523 slush +5524 small +5525 smash +5526 smell +5531 smile +5532 smirk +5533 smog +5534 snack +5535 snap +5536 snare +5541 snarl +5542 sneak +5543 sneer +5544 sniff +5545 snore +5546 snort +5551 snout +5552 snowy +5553 snub +5554 snuff +5555 speak +5556 speed +5561 spend +5562 spent +5563 spew +5564 spied +5565 spill +5566 spiny +5611 spoil +5612 spoke +5613 spoof +5614 spool +5615 spoon +5616 sport +5621 spot +5622 spout +5623 spray +5624 spree +5625 spur +5626 squad +5631 squat +5632 squid +5633 stack +5634 staff +5635 stage +5636 stain +5641 stall +5642 stamp +5643 stand +5644 stank +5645 stark +5646 start +5651 stash +5652 state +5653 stays +5654 steam +5655 steep +5656 stem +5661 step +5662 stew +5663 stick +5664 sting +5665 stir +5666 stock +6111 stole +6112 stomp +6113 stony +6114 stood +6115 stool +6116 stoop +6121 stop +6122 storm +6123 stout +6124 stove +6125 straw +6126 stray +6131 strut +6132 stuck +6133 stud +6134 stuff +6135 stump +6136 stung +6141 stunt +6142 suds +6143 sugar +6144 sulk +6145 surf +6146 sushi +6151 swab +6152 swan +6153 swarm +6154 sway +6155 swear +6156 sweat +6161 sweep +6162 swell +6163 swept +6164 swim +6165 swing +6166 swipe +6211 swirl +6212 swoop +6213 swore +6214 syrup +6215 tacky +6216 taco +6221 tag +6222 take +6223 tall +6224 talon +6225 tamer +6226 tank +6231 taper +6232 taps +6233 tarot +6234 tart +6235 task +6236 taste +6241 tasty +6242 taunt +6243 thank +6244 thaw +6245 theft +6246 theme +6251 thigh +6252 thing +6253 think +6254 thong +6255 thorn +6256 those +6261 throb +6262 thud +6263 thumb +6264 thump +6265 thus +6266 tiara +6311 tidal +6312 tidy +6313 tiger +6314 tile +6315 tilt +6316 tint +6321 tiny +6322 trace +6323 track +6324 trade +6325 train +6326 trait +6331 trap +6332 trash +6333 tray +6334 treat +6335 tree +6336 trek +6341 trend +6342 trial +6343 tribe +6344 trick +6345 trio +6346 trout +6351 truce +6352 truck +6353 trump +6354 trunk +6355 try +6356 tug +6361 tulip +6362 tummy +6363 turf +6364 tusk +6365 tutor +6366 tutu +6411 tux +6412 tweak +6413 tweet +6414 twice +6415 twine +6416 twins +6421 twirl +6422 twist +6423 uncle +6424 uncut +6425 undo +6426 unify +6431 union +6432 unit +6433 untie +6434 upon +6435 upper +6436 urban +6441 used +6442 user +6443 usher +6444 utter +6445 value +6446 vapor +6451 vegan +6452 venue +6453 verse +6454 vest +6455 veto +6456 vice +6461 video +6462 view +6463 viral +6464 virus +6465 visa +6466 visor +6511 vixen +6512 vocal +6513 voice +6514 void +6515 volt +6516 voter +6521 vowel +6522 wad +6523 wafer +6524 wager +6525 wages +6526 wagon +6531 wake +6532 walk +6533 wand +6534 wasp +6535 watch +6536 water +6541 wavy +6542 wheat +6543 whiff +6544 whole +6545 whoop +6546 wick +6551 widen +6552 widow +6553 width +6554 wife +6555 wifi +6556 wilt +6561 wimp +6562 wind +6563 wing +6564 wink +6565 wipe +6566 wired +6611 wiry +6612 wise +6613 wish +6614 wispy +6615 wok +6616 wolf +6621 womb +6622 wool +6623 woozy +6624 word +6625 work +6626 worry +6631 wound +6632 woven +6633 wrath +6634 wreck +6635 wrist +6636 xerox +6641 yahoo +6642 yam +6643 yard +6644 year +6645 yeast +6646 yelp +6651 yield +6652 yo-yo +6653 yodel +6654 yoga +6655 yoyo +6656 yummy +6661 zebra +6662 zero +6663 zesty +6664 zippy +6665 zone +6666 zoom diff --git a/src/engine/server/sql_server.h b/src/engine/server/sql_server.h index d789f4491..3b6a39a8e 100644 --- a/src/engine/server/sql_server.h +++ b/src/engine/server/sql_server.h @@ -30,6 +30,7 @@ public: const char* GetPass() { return m_aPass; } const char* GetIP() { return m_aIp; } int GetPort() { return m_Port; } + sql::Connection *Connection() const { return m_pConnection; } static int ms_NumReadServer; static int ms_NumWriteServer; diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index d4781775e..57191b9a6 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -693,17 +693,20 @@ void CGameContext::ConSave(IConsole::IResult *pResult, void *pUserData) if(pPlayer->m_LastSQLQuery + g_Config.m_SvSqlQueriesDelay * pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) return; - const char* pCode = pResult->GetString(0); + const char* pCode = ""; + if(pResult->NumArguments() > 0) + pCode = pResult->GetString(0); + char aCountry[5]; - if(str_length(pCode) > 3 && pCode[0] >= 'A' && pCode[0] <= 'Z' && pCode[1] >= 'A' + if(str_length(pCode) >= 3 && pCode[0] >= 'A' && pCode[0] <= 'Z' && pCode[1] >= 'A' && pCode[1] <= 'Z' && pCode[2] >= 'A' && pCode[2] <= 'Z') { - if(pCode[3] == ' ') + if(str_length(pCode) == 3 || pCode[3] == ' ') { str_copy(aCountry, pCode, 4); pCode = str_skip_whitespaces_const(pCode + 4); } - else if(str_length(pCode) > 4 && pCode[4] == ' ') + else if(str_length(pCode) == 4 || (str_length(pCode) > 4 && pCode[4] == ' ')) { str_copy(aCountry, pCode, 5); pCode = str_skip_whitespaces_const(pCode + 5); diff --git a/src/game/server/ddracechat.h b/src/game/server/ddracechat.h index e0d3ec894..48787a00c 100644 --- a/src/game/server/ddracechat.h +++ b/src/game/server/ddracechat.h @@ -27,7 +27,7 @@ CHAT_COMMAND("dnd", "", CFGFLAG_CHAT|CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConD CHAT_COMMAND("mapinfo", "?r[map]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConMapInfo, this, "Show info about the map with name r gives (current map by default)") CHAT_COMMAND("timeout", "?s[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTimeout, this, "Set timeout protection code s") CHAT_COMMAND("practice", "?i['0'|'1']", CFGFLAG_CHAT|CFGFLAG_SERVER, ConPractice, this, "Enable cheats (currently only /rescue) for your current team's run, but you can't earn a rank") -CHAT_COMMAND("save", "r[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSave, this, "Save team with code r to current server. To save to another server, use '/save s r' where s = server (case-sensitive: GER, RUS, etc) and r = code.") +CHAT_COMMAND("save", "?r[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConSave, this, "Save team with code r to current server. To save to another server, use '/save s r' where s = server (case-sensitive: GER, RUS, etc) and r = code.") CHAT_COMMAND("load", "?r[code]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConLoad, this, "Load with code r. /load to check your existing saves") CHAT_COMMAND("map", "?r[map]", CFGFLAG_CHAT|CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConMap, this, "Vote a map by name") CHAT_COMMAND("rankteam", "?r[player name]", CFGFLAG_CHAT|CFGFLAG_SERVER, ConTeamRank, this, "Shows the team rank of player with name r (your team rank by default)") diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 1bafc8aa4..58278433f 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -6,11 +6,14 @@ #include #include +#include +#include #include #include #include #include +#include #include #include @@ -100,6 +103,18 @@ void CSqlScore::ExecPlayerThread( pThreadName); } +void CSqlScore::GeneratePassphrase(char *pBuf, int BufSize) +{ + for(int i = 0; i < 3; i++) + { + if(i != 0) + str_append(pBuf, " ", BufSize); + // TODO: decide if the slight bias towards lower numbers is ok + int Rand = m_Prng.RandomBits() % m_aWordlist.size(); + str_append(pBuf, m_aWordlist[Rand].c_str(), BufSize); + } +} + LOCK CSqlScore::ms_FailureFileLock = lock_create(); void CSqlScore::OnShutdown() @@ -169,6 +184,33 @@ CSqlScore::CSqlScore(CGameContext *pGameServer) : ((CGameControllerDDRace*)(pGameServer->m_pController))->m_pInitResult = InitResult; Tmp->m_Map = g_Config.m_SvMap; + IOHANDLE File = GameServer()->Storage()->OpenFile("wordlist.txt", IOFLAG_READ, IStorage::TYPE_ALL); + if(!File) + { + dbg_msg("sql", "failed to open wordlist"); + Server()->SetErrorShutdown("sql open wordlist error"); + return; + } + + uint64 aSeed[2]; + secure_random_fill(aSeed, sizeof(aSeed)); + m_Prng.Seed(aSeed); + CLineReader LineReader; + LineReader.Init(File); + char *pLine; + while((pLine = LineReader.Get())) + { + char Word[32] = {0}; + sscanf(pLine, "%*s %31s", Word); + Word[31] = 0; + m_aWordlist.push_back(Word); + } + if(m_aWordlist.size() < 1000) + { + dbg_msg("sql", "too few words in wordlist"); + Server()->SetErrorShutdown("sql too few words in wordlist"); + return; + } thread_init_and_detach(CSqlExecData::ExecSqlFunc, new CSqlExecData(Init, Tmp), "SqlScore constructor"); @@ -1463,11 +1505,13 @@ void CSqlScore::SaveTeam(int ClientID, const char* Code, const char* Server) pController->m_Teams.SetSaving(Team, SaveResult); CSqlTeamSave *Tmp = new CSqlTeamSave(SaveResult); - Tmp->m_Code = Code; - Tmp->m_Map = g_Config.m_SvMap; + str_copy(Tmp->m_Code, Code, sizeof(Tmp->m_Code)); + str_copy(Tmp->m_Map, g_Config.m_SvMap, sizeof(Tmp->m_Map)); Tmp->m_pResult->m_SaveID = RandomUuid(); str_copy(Tmp->m_Server, Server, sizeof(Tmp->m_Server)); str_copy(Tmp->m_ClientName, this->Server()->ClientName(ClientID), sizeof(Tmp->m_ClientName)); + Tmp->m_aGeneratedCode[0] = '\0'; + GeneratePassphrase(Tmp->m_aGeneratedCode, sizeof(Tmp->m_aGeneratedCode)); pController->m_Teams.KillSavedTeam(ClientID, Team); char aBuf[512]; @@ -1487,7 +1531,7 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_SaveID, aSaveID, UUID_MAXSTRSIZE); - sqlstr::CSqlString<65536> SaveState = pData->m_pResult->m_SavedTeam.GetString(); + char *pSaveState = pData->m_pResult->m_SavedTeam.GetString(); if(HandleFailure) { if (!g_Config.m_SvSqlFailureFile[0]) @@ -1498,13 +1542,16 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlData SaveState = pSaveState; + sqlstr::CSqlString<128> Code = pData->m_aGeneratedCode; + sqlstr::CSqlString<128> Map = pData->m_Map; char aBuf[65536]; str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %%s_saves(Savegame, Map, Code, Timestamp, Server, SaveID, DDNet7) " "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', '%s', false)", - SaveState.ClrStr(), pData->m_Map.ClrStr(), - pData->m_Code.ClrStr(), pData->m_Server, aSaveID + SaveState.ClrStr(), Map.ClrStr(), + Code.ClrStr(), pData->m_Server, aSaveID ); io_write(File, aBuf, str_length(aBuf)); io_write_newline(File); @@ -1514,63 +1561,98 @@ bool CSqlScore::SaveTeamThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_Status = CSqlSaveResult::SAVE_SUCCESS; strcpy(pData->m_pResult->m_aBroadcast, "Database connection failed, teamsave written to a file instead. Admins will add it manually in a few days."); - + str_format(pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage), + "Team successfully saved by %s. Use '/load %s' to continue", + pData->m_ClientName, Code.Str()); return true; } lock_unlock(ms_FailureFileLock); dbg_msg("sql", "ERROR: Could not save Teamsave, NOT even to a file"); return false; } - else + + try { - try + char aBuf[65536]; + str_format(aBuf, sizeof(aBuf), "lock tables %s_saves write;", pSqlServer->GetPrefix()); + pSqlServer->executeSql(aBuf); + + char Code[128] = {0}; + str_format(aBuf, sizeof(aBuf), "SELECT Savegame FROM %s_saves WHERE Code = ? AND Map = ?", pSqlServer->GetPrefix()); + std::unique_ptr pPrepStmt; + std::unique_ptr pResult; + pPrepStmt.reset(pSqlServer->Connection()->prepareStatement(aBuf)); + bool UseCode = false; + if(pData->m_Code[0] != '\0') { - char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "lock tables %s_saves write;", pSqlServer->GetPrefix()); - pSqlServer->executeSql(aBuf); - str_format(aBuf, sizeof(aBuf), - "SELECT Savegame " - "FROM %s_saves " - "WHERE Code = '%s' AND Map = '%s';", - pSqlServer->GetPrefix(), pData->m_Code.ClrStr(), pData->m_Map.ClrStr()); - pSqlServer->executeSqlQuery(aBuf); - - if (pSqlServer->GetResults()->rowsCount() == 0) + pPrepStmt->setString(1, pData->m_Code); + pPrepStmt->setString(2, pData->m_Map); + pResult.reset(pPrepStmt->executeQuery()); + if(pResult->rowsCount() == 0) { - char aBuf[65536]; + UseCode = true; + str_copy(Code, pData->m_Code, sizeof(Code)); + } + } + if(!UseCode) + { + // use random generated passphrase if save code exists or no save code given + pPrepStmt->setString(1, pData->m_aGeneratedCode); + pPrepStmt->setString(2, pData->m_Map); + pResult.reset(pPrepStmt->executeQuery()); + if(pResult->rowsCount() == 0) + { + UseCode = true; + str_copy(Code, pData->m_aGeneratedCode, sizeof(Code)); + } + } - str_format(aBuf, sizeof(aBuf), - "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, SaveID, DDNet7) " - "VALUES ('%s', '%s', '%s', CURRENT_TIMESTAMP(), '%s', '%s', false)", - pSqlServer->GetPrefix(), SaveState.ClrStr(), pData->m_Map.ClrStr(), - pData->m_Code.ClrStr(), pData->m_Server, aSaveID - ); - dbg_msg("sql", "%s", aBuf); - pSqlServer->executeSql(aBuf); + if(UseCode) + { + str_format(aBuf, sizeof(aBuf), + "INSERT IGNORE INTO %s_saves(Savegame, Map, Code, Timestamp, Server, SaveID, DDNet7) " + "VALUES (?, ?, ?, CURRENT_TIMESTAMP(), ?, ?, false)", + pSqlServer->GetPrefix()); + pPrepStmt.reset(pSqlServer->Connection()->prepareStatement(aBuf)); + pPrepStmt->setString(1, pSaveState); + pPrepStmt->setString(2, pData->m_Map); + pPrepStmt->setString(3, Code); + pPrepStmt->setString(4, pData->m_Server); + pPrepStmt->setString(5, aSaveID); + dbg_msg("sql", "%s", aBuf); + pPrepStmt->execute(); + if(str_comp(pData->m_Server, g_Config.m_SvSqlServerName) == 0) + { str_format(pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage), "Team successfully saved by %s. Use '/load %s' to continue", - pData->m_ClientName, pData->m_Code.Str()); - pData->m_pResult->m_Status = CSqlSaveResult::SAVE_SUCCESS; + pData->m_ClientName, Code); } else { - dbg_msg("sql", "ERROR: This save-code already exists"); - pData->m_pResult->m_Status = CSqlSaveResult::SAVE_FAILED; - strcpy(pData->m_pResult->m_aMessage, "This save-code already exists"); + str_format(pData->m_pResult->m_aMessage, sizeof(pData->m_pResult->m_aMessage), + "Team successfully saved by %s. Use '/load %s' on %s to continue", + pData->m_ClientName, Code, pData->m_Server); } + pData->m_pResult->m_Status = CSqlSaveResult::SAVE_SUCCESS; } - catch (sql::SQLException &e) + else { + dbg_msg("sql", "ERROR: This save-code already exists"); pData->m_pResult->m_Status = CSqlSaveResult::SAVE_FAILED; - dbg_msg("sql", "MySQL Error: %s", e.what()); - dbg_msg("sql", "ERROR: Could not save the team"); - - strcpy(pData->m_pResult->m_aMessage, "MySQL Error: Could not save the team"); - pSqlServer->executeSql("unlock tables;"); - return false; + strcpy(pData->m_pResult->m_aMessage, "This save-code already exists"); } } + catch (sql::SQLException &e) + { + pData->m_pResult->m_Status = CSqlSaveResult::SAVE_FAILED; + dbg_msg("sql", "MySQL Error: %s", e.what()); + dbg_msg("sql", "ERROR: Could not save the team"); + + strcpy(pData->m_pResult->m_aMessage, "MySQL Error: Could not save the team"); + pSqlServer->executeSql("unlock tables;"); + return false; + } pSqlServer->executeSql("unlock tables;"); return true; diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 00d281bde..e5e16fa4f 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -5,6 +5,7 @@ #define GAME_SERVER_SCORE_SQL_H #include +#include #include "../score.h" @@ -40,7 +41,7 @@ struct CSqlPlayerResult struct CSqlSaveResult { CSqlSaveResult(int PlayerID, IGameController* Controller) : - m_Status(SAVE_SUCCESS), + m_Status(SAVE_FAILED), m_SavedTeam(CSaveTeam(Controller)), m_RequestingPlayer(PlayerID) { @@ -137,8 +138,9 @@ struct CSqlTeamSave : CSqlData char m_ClientName[MAX_NAME_LENGTH]; - sqlstr::CSqlString<128> m_Map; - sqlstr::CSqlString<128> m_Code; + char m_Map[128]; + char m_Code[128]; + char m_aGeneratedCode[128]; char m_Server[5]; }; @@ -188,7 +190,6 @@ class CSqlScore: public IScore { static LOCK ms_FailureFileLock; - static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -218,6 +219,10 @@ class CSqlScore: public IScore CGameContext *m_pGameServer; IServer *m_pServer; + std::vector m_aWordlist; + CPrng m_Prng; + void GeneratePassphrase(char *pBuf, int BufSize); + char m_aMap[64]; char m_aGameUuid[UUID_MAXSTRSIZE]; From 7f4dc2bb4e35cba78aa8e5a3aa606d10217d15b0 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sun, 14 Jun 2020 10:44:14 +0200 Subject: [PATCH 41/44] Thread safe /map --- src/game/server/ddracechat.cpp | 3 + src/game/server/gamecontext.cpp | 160 +++++++++++++++------------- src/game/server/gamecontext.h | 4 + src/game/server/player.cpp | 15 ++- src/game/server/score/sql_score.cpp | 14 ++- src/game/server/score/sql_score.h | 7 ++ 6 files changed, 125 insertions(+), 78 deletions(-) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 57191b9a6..4f4035fa3 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -518,6 +518,9 @@ void CGameContext::ConMap(IConsole::IResult *pResult, void *pUserData) if (!pPlayer) return; + if(pSelf->RateLimitPlayerVote(pResult->m_ClientID) || pSelf->RateLimitPlayerMapVote(pResult->m_ClientID)) + return; + #if defined(CONF_SQL) if(g_Config.m_SvUseSQL) if(pPlayer->m_LastSQLQuery + g_Config.m_SvSqlQueriesDelay * pSelf->Server()->TickSpeed() >= pSelf->Server()->Tick()) diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 55b60b171..1cdeb8ca9 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -920,12 +920,6 @@ void CGameContext::OnTick() } /* - if(m_pRandomMapResult && m_pRandomMapResult->m_Done) - { - str_copy(g_Config.m_SvMap, m_pRandomMapResult->m_aMap, sizeof(g_Config.m_SvMap)); - m_pRandomMapResult = NULL; - } - if(m_pMapVoteResult && m_pMapVoteResult->m_Done) { m_VoteKick = false; @@ -939,8 +933,6 @@ void CGameContext::OnTick() str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(m_pMapVoteResult->m_ClientID), m_pMapVoteResult->m_aMap, "/map"); CallVote(m_pMapVoteResult->m_ClientID, m_pMapVoteResult->m_aMap, aCmd, "/map", aChatmsg); - - m_pMapVoteResult = NULL; } */ @@ -1424,68 +1416,8 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) } else if(MsgID == NETMSGTYPE_CL_CALLVOTE) { - int64 Now = Server()->Tick(); - int64 TickSpeed = Server()->TickSpeed(); - - if(g_Config.m_SvRconVote && !Server()->GetAuthedState(ClientID)) - { - SendChatTarget(ClientID, "You can only vote after logging in."); + if(RateLimitPlayerVote(ClientID)) return; - } - - if (g_Config.m_SvDnsblVote && !m_pServer->DnsblWhite(ClientID) && Server()->DistinctClientCount() > 1) - { - // blacklisted by dnsbl - SendChatTarget(ClientID, "You are not allowed to vote due to DNSBL."); - return; - } - - if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + TickSpeed * 3 > Now) - return; - - pPlayer->m_LastVoteTry = Now; - if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS) - { - SendChatTarget(ClientID, "Spectators aren't allowed to start a vote."); - return; - } - - if(m_VoteCloseTime) - { - SendChatTarget(ClientID, "Wait for current vote to end before calling a new one."); - return; - } - - if(Now < pPlayer->m_FirstVoteTick) - { - char aBuf[64]; - str_format(aBuf, sizeof(aBuf), "You must wait %d seconds before making your first vote.", (int)((pPlayer->m_FirstVoteTick - Now) / TickSpeed) + 1); - SendChatTarget(ClientID, aBuf); - return; - } - - int TimeLeft = pPlayer->m_LastVoteCall + TickSpeed * g_Config.m_SvVoteDelay - Now; - if(pPlayer->m_LastVoteCall && TimeLeft > 0) - { - char aChatmsg[64]; - str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote.", (int)(TimeLeft / TickSpeed) + 1); - SendChatTarget(ClientID, aChatmsg); - return; - } - - NETADDR Addr; - Server()->GetClientAddr(ClientID, &Addr); - int VoteMuted = 0; - for(int i = 0; i < m_NumVoteMutes && !VoteMuted; i++) - if(!net_addr_comp_noport(&Addr, &m_aVoteMutes[i].m_Addr)) - VoteMuted = (m_aVoteMutes[i].m_Expire - Server()->Tick()) / Server()->TickSpeed(); - if(VoteMuted > 0) - { - char aChatmsg[64]; - str_format(aChatmsg, sizeof(aChatmsg), "You are not permitted to vote for the next %d seconds.", VoteMuted); - SendChatTarget(ClientID, aChatmsg); - return; - } char aChatmsg[512] = {0}; char aDesc[VOTE_DESC_LENGTH] = {0}; @@ -1516,11 +1448,12 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) SendChatTarget(ClientID, "Invalid option"); return; } - if(!Authed && (str_startswith(pOption->m_aCommand, "sv_map ") || str_startswith(pOption->m_aCommand, "change_map ") || str_startswith(pOption->m_aCommand, "random_map") || str_startswith(pOption->m_aCommand, "random_unfinished_map")) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay)) + if((str_startswith(pOption->m_aCommand, "sv_map ") + || str_startswith(pOption->m_aCommand, "change_map ") + || str_startswith(pOption->m_aCommand, "random_map") + || str_startswith(pOption->m_aCommand, "random_unfinished_map")) + && RateLimitPlayerMapVote(ClientID)) { - str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", g_Config.m_SvVoteMapTimeDelay, (int)(((m_LastMapVote+(g_Config.m_SvVoteMapTimeDelay * time_freq()))/time_freq())-(time_get()/time_freq()))); - SendChatTarget(ClientID, aChatmsg); - return; } @@ -3581,3 +3514,84 @@ void CGameContext::ForceVote(int EnforcerID, bool Success) str_format(aBuf, sizeof(aBuf), "forcing vote %s", pOption); Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf); } + +bool CGameContext::RateLimitPlayerVote(int ClientID) +{ + int64 Now = Server()->Tick(); + int64 TickSpeed = Server()->TickSpeed(); + CPlayer *pPlayer = m_apPlayers[ClientID]; + + if(g_Config.m_SvRconVote && !Server()->GetAuthedState(ClientID)) + { + SendChatTarget(ClientID, "You can only vote after logging in."); + return true; + } + + if (g_Config.m_SvDnsblVote && !m_pServer->DnsblWhite(ClientID) && Server()->DistinctClientCount() > 1) + { + // blacklisted by dnsbl + SendChatTarget(ClientID, "You are not allowed to vote due to DNSBL."); + return true; + } + + if(g_Config.m_SvSpamprotection && pPlayer->m_LastVoteTry && pPlayer->m_LastVoteTry + TickSpeed * 3 > Now) + return true; + + pPlayer->m_LastVoteTry = Now; + if(g_Config.m_SvSpectatorVotes == 0 && pPlayer->GetTeam() == TEAM_SPECTATORS) + { + SendChatTarget(ClientID, "Spectators aren't allowed to start a vote."); + return true; + } + + if(m_VoteCloseTime) + { + SendChatTarget(ClientID, "Wait for current vote to end before calling a new one."); + return true; + } + + if(Now < pPlayer->m_FirstVoteTick) + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "You must wait %d seconds before making your first vote.", (int)((pPlayer->m_FirstVoteTick - Now) / TickSpeed) + 1); + SendChatTarget(ClientID, aBuf); + return true; + } + + int TimeLeft = pPlayer->m_LastVoteCall + TickSpeed * g_Config.m_SvVoteDelay - Now; + if(pPlayer->m_LastVoteCall && TimeLeft > 0) + { + char aChatmsg[64]; + str_format(aChatmsg, sizeof(aChatmsg), "You must wait %d seconds before making another vote.", (int)(TimeLeft / TickSpeed) + 1); + SendChatTarget(ClientID, aChatmsg); + return true; + } + + NETADDR Addr; + Server()->GetClientAddr(ClientID, &Addr); + int VoteMuted = 0; + for(int i = 0; i < m_NumVoteMutes && !VoteMuted; i++) + if(!net_addr_comp_noport(&Addr, &m_aVoteMutes[i].m_Addr)) + VoteMuted = (m_aVoteMutes[i].m_Expire - Server()->Tick()) / Server()->TickSpeed(); + if(VoteMuted > 0) + { + char aChatmsg[64]; + str_format(aChatmsg, sizeof(aChatmsg), "You are not permitted to vote for the next %d seconds.", VoteMuted); + SendChatTarget(ClientID, aChatmsg); + return true; + } + return false; +} + +bool CGameContext::RateLimitPlayerMapVote(int ClientID) +{ + if(!Server()->GetAuthedState(ClientID) && time_get() < m_LastMapVote + (time_freq() * g_Config.m_SvVoteMapTimeDelay)) + { + char aChatmsg[512] = {0}; + str_format(aChatmsg, sizeof(aChatmsg), "There's a %d second delay between map-votes, please wait %d seconds.", + g_Config.m_SvVoteMapTimeDelay, (int)((m_LastMapVote + g_Config.m_SvVoteMapTimeDelay * time_freq() - time_get())/time_freq())); + SendChatTarget(ClientID, aChatmsg); + return true; + } + return false; +} diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 4a6d04299..6efb400de 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -259,6 +259,10 @@ public: bool PlayerModerating(); void ForceVote(int EnforcerID, bool Success); + // Checks if player can vote and notify them about the reason + bool RateLimitPlayerVote(int ClientID); + bool RateLimitPlayerMapVote(int ClientID); + private: bool m_VoteWillPass; diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 2b7cc62a1..36c55f116 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -818,7 +818,20 @@ void CPlayer::ProcessSqlResult(CSqlPlayerResult &Result) GameServer()->SendBroadcast(Result.m_Data.m_Broadcast, -1); break; case CSqlPlayerResult::MAP_VOTE: - // TODO: start vote + GameServer()->m_VoteKick = false; + GameServer()->m_VoteSpec = false; + GameServer()->m_LastMapVote = time_get(); + + char aCmd[256]; + str_format(aCmd, sizeof(aCmd), + "sv_reset_file types/%s/flexreset.cfg; change_map \"%s\"", + Result.m_Data.m_MapVote.m_Server, Result.m_Data.m_MapVote.m_Map); + + char aChatmsg[512]; + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", + Server()->ClientName(m_ClientID), Result.m_Data.m_MapVote.m_Map, "/map"); + + GameServer()->CallVote(m_ClientID, Result.m_Data.m_MapVote.m_Map, aCmd, "/map", aChatmsg); break; case CSqlPlayerResult::PLAYER_INFO: GameServer()->Score()->PlayerData(m_ClientID)->Set( diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 58278433f..694117dc3 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -43,6 +43,9 @@ void CSqlPlayerResult::SetVariant(Variant v) m_Data.m_Broadcast[0] = 0; break; case MAP_VOTE: + m_Data.m_MapVote.m_Map[0] = '\0'; + m_Data.m_MapVote.m_Reason[0] = '\0'; + m_Data.m_MapVote.m_Server[0] = '\0'; break; case PLAYER_INFO: m_Data.m_Info.m_Score = -9999; @@ -373,13 +376,16 @@ bool CSqlScore::MapVoteThread(CSqlServer* pSqlServer, const CSqlDataGetResults()->first(); auto Server = pSqlServer->GetResults()->getString("Server"); auto Map = pSqlServer->GetResults()->getString("Map"); - strcpy(paMessages[0], "/map"); // reason - str_copy(paMessages[1], Server.c_str(), sizeof(paMessages[0])); - str_copy(paMessages[2], Map.c_str(), sizeof(paMessages[0])); + pData->m_pResult->SetVariant(CSqlPlayerResult::MAP_VOTE); + auto MapVote = &pData->m_pResult->m_Data.m_MapVote; + strcpy(MapVote->m_Reason, "/map"); + str_copy(MapVote->m_Server, Server.c_str(), sizeof(MapVote->m_Server)); + str_copy(MapVote->m_Map, Map.c_str(), sizeof(MapVote->m_Map)); - for(char *p = paMessages[1]; *p; p++) // lower case server + for(char *p = MapVote->m_Server; *p; p++) // lower case server *p = tolower(*p); } pData->m_pResult->m_Done = true; diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index e5e16fa4f..167c5f5d1 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -6,6 +6,7 @@ #include #include +#include #include "../score.h" @@ -34,6 +35,12 @@ struct CSqlPlayerResult int m_HasFinishScore; int m_Birthday; // 0 indicates no birthday } m_Info; + struct + { + char m_Reason[VOTE_REASON_LENGTH]; + char m_Server[32+1]; + char m_Map[128+1]; + } m_MapVote; } m_Data; // PLAYER_INFO void SetVariant(Variant v); From afe96ed69c6fe010416ad7a01dd29c4ec0f4cfa6 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sun, 14 Jun 2020 11:24:33 +0200 Subject: [PATCH 42/44] Thread safe random_map --- src/game/server/player.cpp | 11 +++++++ src/game/server/player.h | 2 ++ src/game/server/score.h | 11 ------- src/game/server/score/sql_score.cpp | 50 +++++++++++++---------------- src/game/server/score/sql_score.h | 31 ++++++++++++++---- 5 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 36c55f116..75c1fe804 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -159,6 +159,17 @@ void CPlayer::Tick() ProcessSqlResult(*m_SqlFinishResult); m_SqlFinishResult = nullptr; } + if(m_SqlRandomMapResult!= nullptr && m_SqlRandomMapResult.use_count() == 1) + { + if(m_SqlRandomMapResult->m_Done) + { + if(m_SqlRandomMapResult->m_aMessage[0] != '\0') + GameServer()->SendChatTarget(m_ClientID, m_SqlRandomMapResult->m_aMessage); + if(m_SqlRandomMapResult->m_Map[0] != '\0') + str_copy(g_Config.m_SvMap, m_SqlRandomMapResult->m_Map, sizeof(g_Config.m_SvMap)); + } + m_SqlRandomMapResult = nullptr; + } #endif if(!Server()->ClientIngame(m_ClientID)) diff --git a/src/game/server/player.h b/src/game/server/player.h index d43bc6016..e2aeac559 100644 --- a/src/game/server/player.h +++ b/src/game/server/player.h @@ -11,6 +11,7 @@ #if defined(CONF_SQL) class CSqlPlayerResult; +class CSqlRandomMapResult; #endif // player object @@ -202,6 +203,7 @@ public: int64 m_LastSQLQuery; std::shared_ptr m_SqlQueryResult; std::shared_ptr m_SqlFinishResult; + std::shared_ptr m_SqlRandomMapResult; #endif bool m_NotEligibleForFinish; int64 m_EligibleForFinishCheck; diff --git a/src/game/server/score.h b/src/game/server/score.h index 13d645b5b..63598fe73 100644 --- a/src/game/server/score.h +++ b/src/game/server/score.h @@ -42,17 +42,6 @@ public: float m_aBestCpTime[NUM_CHECKPOINTS]; }; -struct CRandomMapResult -{ - char m_aMap[64]; -}; - -struct CMapVoteResult -{ - char m_aMap[64]; - char m_aServer[32]; -}; - class IScore { CPlayerData m_aPlayerData[MAX_CLIENTS]; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 694117dc3..bbd341a05 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1356,25 +1356,25 @@ bool CSqlScore::ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlDatam_apPlayers[ClientID]; + auto pResult = std::make_shared(); + pCurPlayer->m_SqlRandomMapResult = pResult; + + auto *Tmp = new CSqlRandomMapRequest(pResult); Tmp->m_Stars = Stars; + Tmp->m_CurrentMap = g_Config.m_SvMap; + Tmp->m_ServerType = g_Config.m_SvServerType; + Tmp->m_RequestingPlayer = GameServer()->Server()->ClientName(ClientID); - // TODO: Set Client result Tmp->m_ClientID = ClientID; - str_copy(Tmp->m_aCurrentMap, m_aMap, sizeof(Tmp->m_aCurrentMap)); - str_copy(Tmp->m_aServerType, g_Config.m_SvServerType, sizeof(Tmp->m_aServerType)); - - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(RandomMapThread, Tmp), "random map"); - */ + thread_init_and_detach( + CSqlExecData::ExecSqlFunc, + new CSqlExecData(RandomMapThread, Tmp), + "random map"); } -bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlRandomMap *pData = dynamic_cast(pGameData); + const CSqlRandomMapRequest *pData = dynamic_cast(pGameData); if (HandleFailure) return true; @@ -1389,8 +1389,8 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), - pData->m_aServerType, - pData->m_aCurrentMap, + pData->m_ServerType.ClrStr(), + pData->m_CurrentMap.ClrStr(), pData->m_Stars ); } @@ -1401,24 +1401,21 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlDataGetPrefix(), - pData->m_aServerType, - pData->m_aCurrentMap + pData->m_ServerType.ClrStr(), + pData->m_CurrentMap.ClrStr() ); } pSqlServer->executeSqlQuery(aBuf); - pData->m_pResult->m_Tag = CSqlResult::RANDOM_MAP; if(pSqlServer->GetResults()->rowsCount() != 1) { - pData->m_pResult->m_MessageTarget = CSqlResult::DIRECT; - str_copy(pData->m_pResult->m_Message, "No maps found on this server!", sizeof(pData->m_pResult->m_Message)); - pData->m_pResult->m_Variant.m_RandomMap.m_aMap[0] = 0; + str_copy(pData->m_pResult->m_aMessage, "No maps found on this server!", sizeof(pData->m_pResult->m_aMessage)); } else { pSqlServer->GetResults()->next(); - std::string Map = pSqlServer->GetResults()->getString("Map"); - str_copy(pData->m_pResult->m_Variant.m_RandomMap.m_aMap, Map.c_str(), sizeof(pData->m_pResult->m_Variant.m_RandomMap.m_aMap)); + auto Map = pSqlServer->GetResults()->getString("Map"); + str_copy(pData->m_pResult->m_Map, Map.c_str(), sizeof(pData->m_pResult->m_Map)); } dbg_msg("sql", "voting random map done"); @@ -1430,14 +1427,13 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData(); + *ppResult = std::make_shared(); CSqlRandomMap *Tmp = new CSqlRandomMap(); Tmp->m_Num = Stars; @@ -1449,7 +1445,7 @@ void CSqlScore::RandomUnfinishedMap(int ClientID, int Stars) */ } -bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) +bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { /* const CSqlRandomMap *pData = dynamic_cast(pGameData); diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 167c5f5d1..8d293a38a 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -22,7 +22,7 @@ struct CSqlPlayerResult DIRECT, ALL, BROADCAST, - MAP_VOTE, // 3 Messages: Reason, Server, Map + MAP_VOTE, PLAYER_INFO, } m_MessageKind; char m_aaMessages[7][512]; @@ -46,6 +46,19 @@ struct CSqlPlayerResult void SetVariant(Variant v); }; +struct CSqlRandomMapResult +{ + std::atomic_bool m_Done; + CSqlRandomMapResult() : + m_Done(false) + { + m_Map[0] = '\0'; + m_aMessage[0] = '\0'; + } + char m_Map[128]; + char m_aMessage[512]; +}; + struct CSqlSaveResult { CSqlSaveResult(int PlayerID, IGameController* Controller) : m_Status(SAVE_FAILED), @@ -110,6 +123,15 @@ struct CSqlPlayerRequest : CSqlData int m_Offset; }; +struct CSqlRandomMapRequest : CSqlData +{ + using CSqlData::CSqlData; + sqlstr::CSqlString<32> m_ServerType; + sqlstr::CSqlString<32> m_CurrentMap; + sqlstr::CSqlString m_RequestingPlayer; + int m_Stars; +}; + struct CSqlScoreData : CSqlData { using CSqlData::CSqlData; @@ -199,8 +221,8 @@ class CSqlScore: public IScore static bool Init(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure); - static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); - static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); + static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); static bool LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure = false); @@ -230,9 +252,6 @@ class CSqlScore: public IScore CPrng m_Prng; void GeneratePassphrase(char *pBuf, int BufSize); - char m_aMap[64]; - char m_aGameUuid[UUID_MAXSTRSIZE]; - // returns new SqlResult bound to the player, if no current Thread is active for this player std::shared_ptr NewSqlPlayerResult(int ClientID); // Creates for player database requests From 9f15acdbee9e73a2e460a89efbe58590a27c096d Mon Sep 17 00:00:00 2001 From: Zwelf Date: Sun, 14 Jun 2020 23:51:58 +0200 Subject: [PATCH 43/44] Thread safe random_unfinished_map and modified sql statement --- src/game/server/player.cpp | 2 + src/game/server/score/sql_score.cpp | 70 ++++++++++++++++++----------- src/game/server/score/sql_score.h | 8 ---- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 75c1fe804..75c66f223 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -167,6 +167,8 @@ void CPlayer::Tick() GameServer()->SendChatTarget(m_ClientID, m_SqlRandomMapResult->m_aMessage); if(m_SqlRandomMapResult->m_Map[0] != '\0') str_copy(g_Config.m_SvMap, m_SqlRandomMapResult->m_Map, sizeof(g_Config.m_SvMap)); + else + GameServer()->m_LastMapVote = 0; } m_SqlRandomMapResult = nullptr; } diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index bbd341a05..bc0239501 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -1432,23 +1432,25 @@ bool CSqlScore::RandomMapThread(CSqlServer* pSqlServer, const CSqlData(); + CPlayer *pCurPlayer = GameServer()->m_apPlayers[ClientID]; + auto pResult = std::make_shared(); + pCurPlayer->m_SqlRandomMapResult = pResult; - CSqlRandomMap *Tmp = new CSqlRandomMap(); - Tmp->m_Num = Stars; - Tmp->m_ClientID = ClientID; - Tmp->m_Name = GameServer()->Server()->ClientName(ClientID); - Tmp->m_pResult = *ppResult; + auto *Tmp = new CSqlRandomMapRequest(pResult); + Tmp->m_Stars = Stars; + Tmp->m_CurrentMap = g_Config.m_SvMap; + Tmp->m_ServerType = g_Config.m_SvServerType; + Tmp->m_RequestingPlayer = GameServer()->Server()->ClientName(ClientID); - thread_init_and_detach(ExecSqlFunc, new CSqlExecData(RandomUnfinishedMapThread, Tmp), "random unfinished map"); - */ + thread_init_and_detach( + CSqlExecData::ExecSqlFunc, + new CSqlExecData(RandomUnfinishedMapThread, Tmp), + "random unfinished map"); } bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData *pGameData, bool HandleFailure) { - /* - const CSqlRandomMap *pData = dynamic_cast(pGameData); + const CSqlRandomMapRequest *pData = dynamic_cast(pGameData); if (HandleFailure) return true; @@ -1456,25 +1458,48 @@ bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData try { char aBuf[512]; - if(pData->m_Num >= 0) - str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" and Stars = \"%d\" and not exists (select * from %s_race where Name = \"%s\" and %s_race.Map = %s_maps.Map) order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap, pData->m_Num, pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix()); + if(pData->m_Stars >= 0) + { + str_format(aBuf, sizeof(aBuf), + "SELECT Map " + "FROM %s_maps " + "WHERE Server = \"%s\" AND Map != \"%s\" AND Stars = \"%d\" AND Map NOT IN (" + "SELECT Map " + "FROM %s_race " + "WHERE Name = \"%s\"" + ") ORDER BY RAND() " + "LIMIT 1;", + pSqlServer->GetPrefix(), pData->m_ServerType.ClrStr(), pData->m_CurrentMap.ClrStr(), + pData->m_Stars, pSqlServer->GetPrefix(), pData->m_RequestingPlayer.ClrStr()); + } else - str_format(aBuf, sizeof(aBuf), "select * from %s_maps where Server = \"%s\" and Map != \"%s\" and not exists (select * from %s_race where Name = \"%s\" and %s_race.Map = %s_maps.Map) order by RAND() limit 1;", pSqlServer->GetPrefix(), g_Config.m_SvServerType, g_Config.m_SvMap, pSqlServer->GetPrefix(), pData->m_Name.ClrStr(), pSqlServer->GetPrefix(), pSqlServer->GetPrefix()); + { + str_format(aBuf, sizeof(aBuf), + "SELECT Map " + "FROM %s_maps AS maps " + "WHERE Server = \"%s\" AND Map != \"%s\" AND Map NOT IN (" + "SELECT Map " + "FROM %s_race as race " + "WHERE Name = \"%s\"" + ") ORDER BY RAND() " + "LIMIT 1;", + pSqlServer->GetPrefix(), pData->m_ServerType.ClrStr(), pData->m_CurrentMap.ClrStr(), + pSqlServer->GetPrefix(), pData->m_RequestingPlayer.ClrStr()); + } pSqlServer->executeSqlQuery(aBuf); if(pSqlServer->GetResults()->rowsCount() != 1) { - pData->GameServer()->SendChatTarget(pData->m_ClientID, "You have no more unfinished maps on this server!"); - pData->GameServer()->m_LastMapVote = 0; + str_copy(pData->m_pResult->m_aMessage, "You have no more unfinished maps on this server!", sizeof(pData->m_pResult->m_aMessage)); } else { pSqlServer->GetResults()->next(); - std::string Map = pSqlServer->GetResults()->getString("Map"); - str_copy(pData->m_pResult->m_aMap, Map.c_str(), sizeof(pData->m_pResult->m_aMap)); - pData->m_pResult->m_Done = true; + auto Map = pSqlServer->GetResults()->getString("Map"); + str_copy(pData->m_pResult->m_Map, Map.c_str(), sizeof(pData->m_pResult->m_Map)); } + pData->m_pResult->m_Done = true; dbg_msg("sql", "voting random unfinished map done"); return true; } @@ -1483,13 +1508,6 @@ bool CSqlScore::RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData dbg_msg("sql", "MySQL Error: %s", e.what()); dbg_msg("sql", "ERROR: Could not vote random unfinished map"); } - catch (CGameContextError &e) - { - dbg_msg("sql", "WARNING: Aborted unfinished-map-thread due to reload/change of map."); - return true; - } - return false; - */ return false; } diff --git a/src/game/server/score/sql_score.h b/src/game/server/score/sql_score.h index 8d293a38a..183d3e2be 100644 --- a/src/game/server/score/sql_score.h +++ b/src/game/server/score/sql_score.h @@ -186,14 +186,6 @@ struct CSqlTeamLoad : CSqlData int m_NumPlayer; }; -struct CSqlRandomMap : CSqlData -{ - using CSqlData::CSqlData; - int m_Stars; - char m_aCurrentMap[64]; - char m_aServerType[64]; -}; - // controls one thread template < typename TResult > struct CSqlExecData From f1b2ff086c7b8e96a2a19c53daba6a518f645e20 Mon Sep 17 00:00:00 2001 From: Zwelf Date: Wed, 17 Jun 2020 21:15:07 +0200 Subject: [PATCH 44/44] Clean up thread safe SQL refactor --- src/engine/server/sql_string_helpers.h | 4 ++-- src/game/server/gamecontext.cpp | 17 ----------------- src/game/server/player.cpp | 5 +++++ src/game/server/score/sql_score.cpp | 6 ------ 4 files changed, 7 insertions(+), 25 deletions(-) diff --git a/src/engine/server/sql_string_helpers.h b/src/engine/server/sql_string_helpers.h index a5cad6871..342be3f98 100644 --- a/src/engine/server/sql_string_helpers.h +++ b/src/engine/server/sql_string_helpers.h @@ -32,7 +32,7 @@ public: const char* Str() const { return m_aString; } const char* ClrStr() const { return m_aClearString; } - CSqlString& operator = (const char *pStr) + CSqlString& operator=(const char *pStr) { str_copy(m_aString, pStr, size); str_copy(m_aClearString, pStr, size); @@ -40,7 +40,7 @@ public: return *this; } - bool operator < (const CSqlString& other) const + bool operator<(const CSqlString& other) const { return strcmp(m_aString, other.m_aString) < 0; } diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 1cdeb8ca9..56369415c 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -919,23 +919,6 @@ void CGameContext::OnTick() } } - /* - if(m_pMapVoteResult && m_pMapVoteResult->m_Done) - { - m_VoteKick = false; - m_VoteSpec = false; - m_LastMapVote = time_get(); - - char aCmd[256]; - str_format(aCmd, sizeof(aCmd), "sv_reset_file types/%s/flexreset.cfg; change_map \"%s\"", m_pMapVoteResult->m_aServer, m_pMapVoteResult->m_aMap); - - char aChatmsg[512]; - str_format(aChatmsg, sizeof(aChatmsg), "'%s' called vote to change server option '%s' (%s)", Server()->ClientName(m_pMapVoteResult->m_ClientID), m_pMapVoteResult->m_aMap, "/map"); - - CallVote(m_pMapVoteResult->m_ClientID, m_pMapVoteResult->m_aMap, aCmd, "/map", aChatmsg); - } - */ - #ifdef CONF_DEBUG if(g_Config.m_DbgDummies) { diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 75c66f223..7249f6a9d 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -853,6 +853,11 @@ void CPlayer::ProcessSqlResult(CSqlPlayerResult &Result) ); 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; Server()->ExpireServerInfo(); int Birthday = Result.m_Data.m_Info.m_Birthday; if(Birthday != 0) diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index bc0239501..8c97f9f53 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -285,11 +285,6 @@ bool CSqlScore::LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlDatam_pResult->m_Data.m_Info.m_Time = Time; pData->m_pResult->m_Data.m_Info.m_Score = -Time; pData->m_pResult->m_Data.m_Info.m_HasFinishScore = true; - // -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(pData->m_pResult->m_Data.m_Info.m_Score == -9999) - pData->m_pResult->m_Data.m_Info.m_Score = -10000; char aColumn[8]; if(g_Config.m_SvCheckpointSave) @@ -1067,7 +1062,6 @@ bool CSqlScore::ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlDataexecuteSql("SET @pos := 0;"); str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, Rank, TeamSize " "FROM (" // limit to 5