From 60de0da1c33e38c785ac8b02159106c24078d0af Mon Sep 17 00:00:00 2001 From: Sebastian Wojtowicz Date: Sun, 13 Feb 2011 05:45:17 +0100 Subject: [PATCH] Added timestamp into MySQL ranking, and function to have ago time web2.0 like printed on asking for /rank --- src/game/server/ddracecommands.cpp | 56 ++-- src/game/server/entities/character.cpp | 6 +- src/game/server/gamemodes/DDRace.cpp | 4 +- src/game/server/gamemodes/DDRace.h | 6 +- src/game/server/score/file_score.cpp | 32 +-- src/game/server/score/file_score.h | 22 +- src/game/server/score/sql_score.cpp | 344 +++++++++++++++---------- src/game/server/score/sql_score.h | 23 +- 8 files changed, 279 insertions(+), 214 deletions(-) diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index 828911eda..eb852c349 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -45,7 +45,7 @@ void CGameContext::ConMoveRaw(IConsole::IResult *pResult, void *pUserData, int C void CGameContext::MoveCharacter(int ClientID, int Victim, int X, int Y, bool Raw) { CCharacter* pChr = GetPlayerChar(ClientID); - + if(!pChr) return; @@ -135,10 +135,10 @@ void CGameContext::ConHammer(IConsole::IResult *pResult, void *pUserData, int Cl int Type = pResult->GetInteger(0); CCharacter* pChr = pSelf->GetPlayerChar(Victim); - + if(!pChr) return; - + CServer* pServ = (CServer*)pSelf->Server(); if(Type>3 || Type<0) { @@ -249,16 +249,16 @@ void CGameContext::ModifyWeapons(int ClientID, int Victim, int Weapon, bool Remo Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "invalid weapon id"); return; } - + CCharacter* pChr = GetPlayerChar(Victim); if(!pChr) return; - + if(Weapon == -1) { if(Remove && (pChr->GetActiveWeapon() == WEAPON_SHOTGUN || pChr->GetActiveWeapon() == WEAPON_GRENADE || pChr->GetActiveWeapon() == WEAPON_RIFLE)) pChr->SetActiveWeapon(WEAPON_GUN); - + if(Remove) { pChr->SetWeaponGot(WEAPON_SHOTGUN, false); @@ -266,13 +266,13 @@ void CGameContext::ModifyWeapons(int ClientID, int Victim, int Weapon, bool Remo pChr->SetWeaponGot(WEAPON_RIFLE, false); } else - pChr->GiveAllWeapons(); + pChr->GiveAllWeapons(); } else if(Weapon != WEAPON_NINJA) { if(Remove && pChr->GetActiveWeapon() == Weapon) pChr->SetActiveWeapon(WEAPON_GUN); - + if(Remove) pChr->SetWeaponGot(Weapon, false); else @@ -285,7 +285,7 @@ void CGameContext::ModifyWeapons(int ClientID, int Victim, int Weapon, bool Remo Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "you can't remove ninja"); return; } - + pChr->GiveNinja(); } @@ -399,11 +399,11 @@ void CGameContext::ConFreeze(IConsole::IResult *pResult, void *pUserData, int Cl if(pResult->NumArguments()) Seconds = clamp(pResult->GetInteger(0), -2, 9999); - + CCharacter* pChr = pSelf->GetPlayerChar(Victim); if(!pChr) return; - + if(pSelf->m_apPlayers[Victim]) { pChr->Freeze(Seconds); @@ -757,7 +757,7 @@ void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData, int Clie if(/*g_Config.m_SvSpamprotection && */pPlayer->m_Last_Chat && pPlayer->m_Last_Chat + pSelf->Server()->TickSpeed() + g_Config.m_SvChatDelay > pSelf->Server()->Tick()) return; - + pPlayer->m_Last_Chat = pSelf->Server()->Tick(); if(pResult->NumArguments() > 0) @@ -771,7 +771,7 @@ void CGameContext::ConRank(IConsole::IResult *pResult, void *pUserData, int Clie void CGameContext::ConJoinTeam(IConsole::IResult *pResult, void *pUserData, int ClientID) { - + CGameContext *pSelf = (CGameContext *)pUserData; CGameControllerDDRace* Controller = (CGameControllerDDRace*)pSelf->m_pController; if(g_Config.m_SvTeam == 0) @@ -868,7 +868,7 @@ void CGameContext::ConMe(IConsole::IResult *pResult, void *pUserData, int Client { CGameContext *pSelf = (CGameContext *)pUserData; char aBuf[256 + 24]; - + str_format(aBuf, 256 + 24, "'%s' %s", pSelf->Server()->ClientName(ClientID), pResult->GetString(0)); if(g_Config.m_SvSlashMe) pSelf->SendChat(-2, CGameContext::CHAT_ALL, aBuf, ClientID); @@ -879,7 +879,7 @@ void CGameContext::ConMe(IConsole::IResult *pResult, void *pUserData, int Client void CGameContext::ConToggleEyeEmote(IConsole::IResult *pResult, void *pUserData, int ClientID) { CGameContext *pSelf = (CGameContext *)pUserData; - + CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter(); if(pChr) @@ -892,7 +892,7 @@ void CGameContext::ConToggleEyeEmote(IConsole::IResult *pResult, void *pUserData void CGameContext::ConToggleBroadcast(IConsole::IResult *pResult, void *pUserData, int ClientID) { CGameContext *pSelf = (CGameContext *)pUserData; - + CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter(); if(pChr) @@ -902,39 +902,39 @@ void CGameContext::ConToggleBroadcast(IConsole::IResult *pResult, void *pUserDat void CGameContext::ConEyeEmote(IConsole::IResult *pResult, void *pUserData, int ClientID) { CGameContext *pSelf = (CGameContext *)pUserData; - + CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter(); - + if (pResult->NumArguments() == 0) { pSelf->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "Emote commands are: /emote surprise /emote blink /emote close /emote angry /emote happy /emote pain"); pSelf->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "Example: /emote surprise 10 for 10 seconds or /emote surprise (default 1 second)"); } - else + else { - if (pChr) + if (pChr) { if (!str_comp(pResult->GetString(0), "angry")) - pChr->m_DefEmote = EMOTE_ANGRY; + pChr->m_DefEmote = EMOTE_ANGRY; else if (!str_comp(pResult->GetString(0), "blink")) - pChr->m_DefEmote = EMOTE_BLINK; + pChr->m_DefEmote = EMOTE_BLINK; else if (!str_comp(pResult->GetString(0), "close")) - pChr->m_DefEmote = EMOTE_BLINK; + pChr->m_DefEmote = EMOTE_BLINK; else if (!str_comp(pResult->GetString(0), "happy")) - pChr->m_DefEmote = EMOTE_HAPPY; + pChr->m_DefEmote = EMOTE_HAPPY; else if (!str_comp(pResult->GetString(0), "pain")) - pChr->m_DefEmote = EMOTE_PAIN; + pChr->m_DefEmote = EMOTE_PAIN; else if (!str_comp(pResult->GetString(0), "surprise")) - pChr->m_DefEmote = EMOTE_SURPRISE; + pChr->m_DefEmote = EMOTE_SURPRISE; else { pSelf->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "Unkown emote... Say /emote"); } - + int Duration = 1; if (pResult->NumArguments() > 1) Duration = pResult->GetInteger(1); - + pChr->m_DefEmoteReset = pSelf->Server()->Tick() + Duration * pSelf->Server()->TickSpeed(); } } diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 45b002f5f..fc323e84e 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -987,11 +987,13 @@ void CCharacter::OnFinish() } } - if(!pData->m_BestTime || time < pData->m_BestTime) + if(!pData->m_BestTime || time < pData->m_BestTime && !g_Config.m_SvUseSQL) { // update the score pData->Set(time, m_CpCurrent); - + } + else + { if(str_comp_num(Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", 12) != 0) GameServer()->Score()->SaveScore(m_pPlayer->GetCID(), time, this); } diff --git a/src/game/server/gamemodes/DDRace.cpp b/src/game/server/gamemodes/DDRace.cpp index 494fe8b32..47bda5d3a 100644 --- a/src/game/server/gamemodes/DDRace.cpp +++ b/src/game/server/gamemodes/DDRace.cpp @@ -33,10 +33,10 @@ void CGameControllerDDRace::InitTeleporter() for(int i = 0; i < Width*Height; i++) { - if(GameServer()->Collision()->TeleLayer()[i].m_Number > 0 + if(GameServer()->Collision()->TeleLayer()[i].m_Number > 0 && GameServer()->Collision()->TeleLayer()[i].m_Type == TILE_TELEOUT) { - m_TeleOuts[GameServer()->Collision()->TeleLayer()[i].m_Number-1].push_back(vec2(i % GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16, + m_TeleOuts[GameServer()->Collision()->TeleLayer()[i].m_Number-1].push_back(vec2(i % GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16, i/GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16)); } } diff --git a/src/game/server/gamemodes/DDRace.h b/src/game/server/gamemodes/DDRace.h index 9aa37f20e..ef181193f 100644 --- a/src/game/server/gamemodes/DDRace.h +++ b/src/game/server/gamemodes/DDRace.h @@ -12,14 +12,14 @@ class CGameControllerDDRace : public IGameController { public: - + CGameControllerDDRace(class CGameContext *pGameServer); ~CGameControllerDDRace(); - + CGameTeams m_Teams; std::map < int , std::vector < vec2 > > m_TeleOuts; - + void InitTeleporter(); virtual void Tick(); }; diff --git a/src/game/server/score/file_score.cpp b/src/game/server/score/file_score.cpp index 0a7b28188..100776e4d 100644 --- a/src/game/server/score/file_score.cpp +++ b/src/game/server/score/file_score.cpp @@ -23,17 +23,17 @@ CFileScore::CFileScore(CGameContext *pGameServer) : m_pGameServer(pGameServer), { if(gs_ScoreLock == 0) gs_ScoreLock = lock_create(); - + Init(); } CFileScore::~CFileScore() { lock_wait(gs_ScoreLock); - + // clear list m_Top.clear(); - + lock_release(gs_ScoreLock); } @@ -85,14 +85,14 @@ void CFileScore::Save() void CFileScore::Init() { lock_wait(gs_ScoreLock); - + // create folder if not exist if(g_Config.m_SvScoreFolder[0]) fs_makedir(g_Config.m_SvScoreFolder); - + std::fstream f; f.open(SaveFile().c_str(), std::ios::in); - + while(!f.eof() && !f.fail()) { std::string TmpName, TmpScore, TmpCpLine; @@ -118,7 +118,7 @@ void CFileScore::Init() } f.close(); lock_release(gs_ScoreLock); - + // save the current best score if(m_Top.size()) ((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = m_Top[0].m_Score; @@ -157,23 +157,23 @@ CFileScore::CPlayerScore *CFileScore::SearchName(const char *pName, int *pPositi void CFileScore::UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS]) { const char *pName = Server()->ClientName(ID); - + lock_wait(gs_ScoreLock); CPlayerScore *pPlayer = SearchScore(ID, 0); - + if(pPlayer) { for(int c = 0; c < NUM_CHECKPOINTS; c++) pPlayer->m_aCpTime[c] = aCpTime[c]; - + pPlayer->m_Score = Score; str_copy(pPlayer->m_aName, pName, sizeof(pPlayer->m_aName)); - + sort(m_Top.all()); } else m_Top.add(*new CPlayerScore(pName, Score, aCpTime)); - + lock_release(gs_ScoreLock); Save(); } @@ -187,7 +187,7 @@ void CFileScore::LoadScore(int ClientID) lock_release(gs_ScoreLock); Save(); } - + // set score if(pPlayer) PlayerData(ClientID)->Set(pPlayer->m_Score, pPlayer->m_aCpTime); @@ -219,12 +219,12 @@ void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) CPlayerScore *pScore; int Pos; char aBuf[512]; - + if(!Search) pScore = SearchScore(ClientID, &Pos); else pScore = SearchName(pName, &Pos, 1); - + if(pScore && Pos > -1) { float Time = pScore->m_Score; @@ -242,6 +242,6 @@ void CFileScore::ShowRank(int ClientID, const char* pName, bool Search) str_format(aBuf, sizeof(aBuf), "Several players were found."); else str_format(aBuf, sizeof(aBuf), "%s is not ranked", Search?pName:Server()->ClientName(ClientID)); - + GameServer()->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "rank", aBuf); } diff --git a/src/game/server/score/file_score.h b/src/game/server/score/file_score.h index c7ea1d85c..e565fead6 100644 --- a/src/game/server/score/file_score.h +++ b/src/game/server/score/file_score.h @@ -11,42 +11,42 @@ class CFileScore : public IScore { CGameContext *m_pGameServer; IServer *m_pServer; - + class CPlayerScore { public: char m_aName[MAX_NAME_LENGTH]; float m_Score; float m_aCpTime[NUM_CHECKPOINTS]; - + CPlayerScore() {}; CPlayerScore(const char *pName, float Score, float aCpTime[NUM_CHECKPOINTS]); - + bool operator<(const CPlayerScore& other) { return (this->m_Score < other.m_Score); } }; - + sorted_array m_Top; - + CGameContext *GameServer() { return m_pGameServer; } IServer *Server() { return m_pServer; } - + CPlayerScore *SearchScore(int ID, int *pPosition){ 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]); - + void Init(); void Save(); static void SaveScoreThread(void *pUser); - + public: - + CFileScore(CGameContext *pGameServer); ~CFileScore(); - + virtual void LoadScore(int ClientID); virtual void SaveScore(int ClientID, float Time, CCharacter *pChar); - + virtual void ShowTop5(int ClientID, int Debut=1); virtual void ShowRank(int ClientID, const char* pName, bool Search=false); }; diff --git a/src/game/server/score/sql_score.cpp b/src/game/server/score/sql_score.cpp index 9e964b94b..c6798e248 100644 --- a/src/game/server/score/sql_score.cpp +++ b/src/game/server/score/sql_score.cpp @@ -9,22 +9,21 @@ static LOCK gs_SqlLock = 0; -CSqlScore::CSqlScore(CGameContext *pGameServer) -: m_pGameServer(pGameServer), - m_pServer(pGameServer->Server()), - m_pDatabase(g_Config.m_SvSqlDatabase), - m_pPrefix(g_Config.m_SvSqlPrefix), - m_pUser(g_Config.m_SvSqlUser), - m_pPass(g_Config.m_SvSqlPw), - m_pIp(g_Config.m_SvSqlIp), - m_Port(g_Config.m_SvSqlPort) +CSqlScore::CSqlScore(CGameContext *pGameServer) : m_pGameServer(pGameServer), + m_pServer(pGameServer->Server()), + m_pDatabase(g_Config.m_SvSqlDatabase), + m_pPrefix(g_Config.m_SvSqlPrefix), + m_pUser(g_Config.m_SvSqlUser), + m_pPass(g_Config.m_SvSqlPw), + m_pIp(g_Config.m_SvSqlIp), + m_Port(g_Config.m_SvSqlPort) { str_copy(m_aMap, g_Config.m_SvMap, sizeof(m_aMap)); NormalizeMapname(m_aMap); - + if(gs_SqlLock == 0) gs_SqlLock = lock_create(); - + Init(); } @@ -36,65 +35,65 @@ CSqlScore::~CSqlScore() bool CSqlScore::Connect() { - try + try { // Create connection m_pDriver = get_driver_instance(); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "tcp://%s:%d", m_pIp, m_Port); m_pConnection = m_pDriver->connect(aBuf, m_pUser, m_pPass); - + // Create Statement m_pStatement = m_pConnection->createStatement(); - + // Create database if not exists str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s", m_pDatabase); m_pStatement->execute(aBuf); - + // Connect to specific database m_pConnection->setSchema(m_pDatabase); dbg_msg("SQL", "SQL connection established"); return true; - } + } catch (sql::SQLException &e) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); dbg_msg("SQL", aBuf); - + dbg_msg("SQL", "ERROR: SQL connection failed"); return false; } catch (const std::exception& ex) { - // ... + // ... dbg_msg("SQL", "1 %s",ex.what()); } catch (const std::string& ex) { - // ... + // ... dbg_msg("SQL", "2 %s",ex.c_str()); } - catch( int ) - { - dbg_msg("SQL", "3 %s"); - } - catch( float ) - { - dbg_msg("SQL", "4 %s"); - } - - catch( char[] ) - { - dbg_msg("SQL", "5 %s"); - } - - catch( char ) - { - dbg_msg("SQL", "6 %s"); - } + catch( int ) + { + dbg_msg("SQL", "3 %s"); + } + catch( float ) + { + dbg_msg("SQL", "4 %s"); + } + + catch( char[] ) + { + dbg_msg("SQL", "5 %s"); + } + + catch( char ) + { + dbg_msg("SQL", "6 %s"); + } catch (...) { dbg_msg("SQL", "Unknown Error cause by the MySQL/C++ Connector, my advice compile server_debug and use it"); - + dbg_msg("SQL", "ERROR: SQL connection failed"); return false; } @@ -124,32 +123,46 @@ void CSqlScore::Init() { // create tables char aBuf[768]; - str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_%s_race (Name VARCHAR(%d) NOT NULL, Time FLOAT DEFAULT 0, cp1 FLOAT DEFAULT 0, cp2 FLOAT DEFAULT 0, cp3 FLOAT DEFAULT 0, cp4 FLOAT DEFAULT 0, cp5 FLOAT DEFAULT 0, cp6 FLOAT DEFAULT 0, cp7 FLOAT DEFAULT 0, cp8 FLOAT DEFAULT 0, cp9 FLOAT DEFAULT 0, cp10 FLOAT DEFAULT 0, cp11 FLOAT DEFAULT 0, cp12 FLOAT DEFAULT 0, cp13 FLOAT DEFAULT 0, cp14 FLOAT DEFAULT 0, cp15 FLOAT DEFAULT 0, cp16 FLOAT DEFAULT 0, cp17 FLOAT DEFAULT 0, cp18 FLOAT DEFAULT 0, cp19 FLOAT DEFAULT 0, cp20 FLOAT DEFAULT 0, cp21 FLOAT DEFAULT 0, cp22 FLOAT DEFAULT 0, cp23 FLOAT DEFAULT 0, cp24 FLOAT DEFAULT 0, cp25 FLOAT DEFAULT 0) CHARACTER SET utf8 ;", m_pPrefix, m_aMap, MAX_NAME_LENGTH); + str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_%s_race (Name VARCHAR(%d) NOT NULL, Timestamp TIMESTAMP, Time FLOAT DEFAULT 0, cp1 FLOAT DEFAULT 0, cp2 FLOAT DEFAULT 0, cp3 FLOAT DEFAULT 0, cp4 FLOAT DEFAULT 0, cp5 FLOAT DEFAULT 0, cp6 FLOAT DEFAULT 0, cp7 FLOAT DEFAULT 0, cp8 FLOAT DEFAULT 0, cp9 FLOAT DEFAULT 0, cp10 FLOAT DEFAULT 0, cp11 FLOAT DEFAULT 0, cp12 FLOAT DEFAULT 0, cp13 FLOAT DEFAULT 0, cp14 FLOAT DEFAULT 0, cp15 FLOAT DEFAULT 0, cp16 FLOAT DEFAULT 0, cp17 FLOAT DEFAULT 0, cp18 FLOAT DEFAULT 0, cp19 FLOAT DEFAULT 0, cp20 FLOAT DEFAULT 0, cp21 FLOAT DEFAULT 0, cp22 FLOAT DEFAULT 0, cp23 FLOAT DEFAULT 0, cp24 FLOAT DEFAULT 0, cp25 FLOAT DEFAULT 0) CHARACTER SET utf8 ;", m_pPrefix, m_aMap, MAX_NAME_LENGTH); m_pStatement->execute(aBuf); + + // Check if table has new column with timestamp + str_format(aBuf, sizeof(aBuf), "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '%s_%s_race' AND column_name = 'Timestamp'",m_pPrefix, m_aMap); + m_pResults = m_pStatement->executeQuery(aBuf); + + if(m_pResults->rowsCount() < 1){ + // If not... add the column + str_format(aBuf, sizeof(aBuf), "%s_%s_race has no column Timestamp, I will add it now",m_pPrefix, m_aMap); + dbg_msg("SQL",aBuf); + str_format(aBuf, sizeof(aBuf), "ALTER TABLE %s_%s_race ADD Timestamp TIMESTAMP AFTER Name",m_pPrefix, m_aMap); + dbg_msg("SQL",aBuf); + m_pStatement->execute(aBuf); + } + dbg_msg("SQL", "Tables were created successfully"); - + // get the best time str_format(aBuf, sizeof(aBuf), "SELECT Time FROM %s_%s_race ORDER BY `Time` ASC LIMIT 0, 1;", m_pPrefix, m_aMap); m_pResults = m_pStatement->executeQuery(aBuf); - + if(m_pResults->next()) { ((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = (float)m_pResults->getDouble("Time"); - + dbg_msg("SQL", "Getting best time on server done"); - + // delete results delete m_pResults; } - + // delete statement delete m_pStatement; } catch (sql::SQLException &e) { - char aBuf[256]; + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); - dbg_msg("SQL", aBuf); + dbg_msg("SQL", aBuf); dbg_msg("SQL", "ERROR: Tables were NOT created"); } @@ -162,9 +175,9 @@ void CSqlScore::Init() void CSqlScore::LoadScoreThread(void *pUser) { lock_wait(gs_SqlLock); - + CSqlScoreData *pData = (CSqlScoreData *)pUser; - + // Connect to database if(pData->m_pSqlData->Connect()) { @@ -172,11 +185,11 @@ void CSqlScore::LoadScoreThread(void *pUser) { // check strings pData->m_pSqlData->ClearString(pData->m_aName); - + char aBuf[512]; - - str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_%s_race WHERE Name='%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName); + + str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_%s_race WHERE Name='%s' ORDER BY time ASC LIMIT 1;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); if(pData->m_pSqlData->m_pResults->next()) { @@ -192,25 +205,25 @@ void CSqlScore::LoadScoreThread(void *pUser) } } } - + dbg_msg("SQL", "Getting best time done"); - + // delete statement and results delete pData->m_pSqlData->m_pStatement; delete pData->m_pSqlData->m_pResults; } catch (sql::SQLException &e) - { - char aBuf[256]; + { + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); dbg_msg("SQL", aBuf); dbg_msg("SQL", "ERROR: Could not update account"); } - + // disconnect from database pData->m_pSqlData->Disconnect(); } - + delete pData; lock_release(gs_SqlLock); @@ -222,7 +235,7 @@ void CSqlScore::LoadScore(int ClientID) Tmp->m_ClientID = ClientID; str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName)); Tmp->m_pSqlData = this; - + void *LoadThread = thread_create(LoadScoreThread, Tmp); #if defined(CONF_FAMILY_UNIX) pthread_detach((pthread_t)LoadThread); @@ -232,9 +245,9 @@ void CSqlScore::LoadScore(int ClientID) void CSqlScore::SaveScoreThread(void *pUser) { lock_wait(gs_SqlLock); - + CSqlScoreData *pData = (CSqlScoreData *)pUser; - + // Connect to database if(pData->m_pSqlData->Connect()) { @@ -242,61 +255,30 @@ void CSqlScore::SaveScoreThread(void *pUser) { // check strings pData->m_pSqlData->ClearString(pData->m_aName); - + char aBuf[768]; - - // fisrt check for the name - str_format(aBuf, sizeof(aBuf), "SELECT * FROM %s_%s_race WHERE Name='%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName); - pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - - // if the name is found... - if(pData->m_pSqlData->m_pResults->next()) - { - // update time - if(g_Config.m_SvCheckpointSave) - str_format(aBuf, sizeof(aBuf), "UPDATE %s_%s_race SET Time='%.2f', cp1='%.2f', cp2='%.2f', cp3='%.2f', cp4='%.2f', cp5='%.2f', cp6='%.2f', cp7='%.2f', cp8='%.2f', cp9='%.2f', cp10='%.2f', cp11='%.2f', cp12='%.2f', cp13='%.2f', cp14='%.2f', cp15='%.2f', cp16='%.2f', cp17='%.2f', cp18='%.2f', cp19='%.2f', cp20='%.2f', cp21='%.2f', cp22='%.2f', cp23='%.2f', cp24='%.2f', cp25='%.2f' WHERE Name='%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Time, 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_aName); - else - str_format(aBuf, sizeof(aBuf), "UPDATE %s_%s_race SET Time='%.2f' WHERE Name='%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Time, pData->m_aName); - pData->m_pSqlData->m_pStatement->execute(aBuf); - - dbg_msg("SQL", "Updating time done"); - - // delete results statement - delete pData->m_pSqlData->m_pResults; - delete pData->m_pSqlData->m_pStatement; - - // disconnect from database - pData->m_pSqlData->Disconnect(); - - delete pData; - - lock_release(gs_SqlLock); - - return; - } - + // if no entry found... create a new one str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_%s_race(Name, Time, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8, cp9, cp10, cp11, cp12, cp13, cp14, cp15, cp16, cp17, cp18, cp19, cp20, cp21, cp22, cp23, cp24, cp25) VALUES ('%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', '%.2f');", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_Time, 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_pSqlData->m_pStatement->execute(aBuf); - + dbg_msg("SQL", "Updating time done"); - + // delete results statement - delete pData->m_pSqlData->m_pResults; delete pData->m_pSqlData->m_pStatement; } catch (sql::SQLException &e) { - char aBuf[256]; + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); - dbg_msg("SQL", aBuf); + dbg_msg("SQL", aBuf); dbg_msg("SQL", "ERROR: Could not update time"); } - + // disconnect from database pData->m_pSqlData->Disconnect(); //TODO:Check if an exception is caught will this still execute ? } - + delete pData; lock_release(gs_SqlLock); @@ -311,7 +293,7 @@ void CSqlScore::SaveScore(int ClientID, float Time, CCharacter *pChar) for(int i = 0; i < NUM_CHECKPOINTS; i++) Tmp->m_aCpCurrent[i] = pChar->m_CpCurrent[i]; Tmp->m_pSqlData = this; - + void *SaveThread = thread_create(SaveScoreThread, Tmp); #if defined(CONF_FAMILY_UNIX) pthread_detach((pthread_t)SaveThread); @@ -321,9 +303,9 @@ void CSqlScore::SaveScore(int ClientID, float Time, CCharacter *pChar) void CSqlScore::ShowRankThread(void *pUser) { lock_wait(gs_SqlLock); - + CSqlScoreData *pData = (CSqlScoreData *)pUser; - + // Connect to database if(pData->m_pSqlData->Connect()) { @@ -333,19 +315,27 @@ void CSqlScore::ShowRankThread(void *pUser) char originalName[MAX_NAME_LENGTH]; strcpy(originalName,pData->m_aName); pData->m_pSqlData->ClearString(pData->m_aName); - + // check sort methode char aBuf[512]; - + pData->m_pSqlData->m_pStatement->execute("SET @rownum := 0;"); - str_format(aBuf, sizeof(aBuf), "SELECT Rank, Name, Time " + str_format(aBuf, sizeof(aBuf), "SELECT Rank, one_rank.Name, one_rank.Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(r.Timestamp) as Ago, UNIX_TIMESTAMP(r.Timestamp) as stamp " "FROM (" - "SELECT @rownum := @rownum + 1 AS RANK, Name, Time FROM %s_%s_race ORDER BY Time ASC" - ") as Query " - "WHERE Name = '%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap,pData->m_aName); - + "SELECT * FROM (" + "SELECT @rownum := @rownum + 1 AS RANK, Name, Time " + "FROM (" + "SELECT Name, min(Time) as Time " + "FROM %s_%s_race " + "Group By Name) as all_top_times " + "ORDER BY Time ASC) as all_ranks " + "WHERE all_ranks.Name = '%s') as one_rank " + "LEFT JOIN %s_%s_race as r " + "ON one_rank.Name = r.Name && one_rank.Time = r.Time " + ";", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap,pData->m_aName, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap); + pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - + if(pData->m_pSqlData->m_pResults->rowsCount() != 1) { str_format(aBuf, sizeof(aBuf), "%s is not ranked", originalName); @@ -353,43 +343,115 @@ void CSqlScore::ShowRankThread(void *pUser) } else { - pData->m_pSqlData->m_pResults->next(); - float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); - int Rank = (float)pData->m_pSqlData->m_pResults->getInt("Rank"); + pData->m_pSqlData->m_pResults->next(); + int since = (int)pData->m_pSqlData->m_pResults->getInt("Ago"); + char agoString[40]; + agoTimeToString(since,agoString); + + float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); + int Rank = (int)pData->m_pSqlData->m_pResults->getInt("Rank"); if(g_Config.m_SvHideScore) str_format(aBuf, sizeof(aBuf), "Your time: %d minute(s) %5.2f second(s)", (int)(Time/60), Time-((int)Time/60*60)); else - str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %5.2f second(s)", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60)); - + str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %5.2f second(s)", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60), agoString); + + if(pData->m_pSqlData->m_pResults->getInt("stamp") != 0){ + pData->m_pSqlData->GameServer()->SendChatTarget(-1, aBuf); + str_format(aBuf, sizeof(aBuf), "Finished: %s ago", agoString); + } if(pData->m_Search) strcat(aBuf, pData->m_aRequestingPlayer); - pData->m_pSqlData->GameServer()->SendChatTarget(-1, aBuf); + } - + dbg_msg("SQL", "Showing rank done"); - + // delete results and statement - delete pData->m_pSqlData->m_pResults; + delete pData->m_pSqlData->m_pResults; delete pData->m_pSqlData->m_pStatement; } catch (sql::SQLException &e) { - char aBuf[256]; + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); - dbg_msg("SQL", aBuf); + dbg_msg("SQL", aBuf); dbg_msg("SQL", "ERROR: Could not show rank"); } - + // disconnect from database pData->m_pSqlData->Disconnect();//TODO:Check if an exception is caught will this still execute ? } - + delete pData; lock_release(gs_SqlLock); } +void CSqlScore::agoTimeToString(int agoTime, char agoString[]){ + char aBuf[20]; + int times[7] = { + 60 * 60 * 24 * 365 , + 60 * 60 * 24 * 30 , + 60 * 60 * 24 * 7, + 60 * 60 * 24 , + 60 * 60 , + 60 , + 1 + }; + char names[7][6] = { + "year", + "month", + "week", + "day", + "hour", + "min", + "sec" + }; + + int seconds = 0; + char name[6]; + int count = 0; + int i = 0; + + // finding biggest match + for(i = 0; i<7; i++){ + seconds = times[i]; + strcpy(name,names[i]); + + count = floor(agoTime/seconds); + if(count != 0){ + break; + } + } + + if(count == 1){ + str_format(aBuf, sizeof(aBuf), "%d %s", 1 , name); + }else{ + str_format(aBuf, sizeof(aBuf), "%d %ss", count , name); + } + strcat(agoString,aBuf); + + if (i + 1 < 7) { + // getting second piece now + int seconds2 = times[i+1]; + char name2[6]; + strcpy(name2,names[i+1]); + + // add second piece if it's greater than 0 + int count2 = floor((agoTime - (seconds * count)) / seconds2); + + if (count2 != 0) { + if(count2 == 1){ + str_format(aBuf, sizeof(aBuf), " and %d %s", 1 , name2); + }else{ + str_format(aBuf, sizeof(aBuf), " and %d %ss", count2 , name2); + } + strcat(agoString,aBuf); + } + } +} + void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) { CSqlScoreData *Tmp = new CSqlScoreData(); @@ -398,7 +460,7 @@ void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) Tmp->m_Search = Search; str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), " (%s)", Server()->ClientName(ClientID)); Tmp->m_pSqlData = this; - + void *RankThread = thread_create(ShowRankThread, Tmp); #if defined(CONF_FAMILY_UNIX) pthread_detach((pthread_t)RankThread); @@ -408,9 +470,9 @@ void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search) void CSqlScore::ShowTop5Thread(void *pUser) { lock_wait(gs_SqlLock); - + CSqlScoreData *pData = (CSqlScoreData *)pUser; - + // Connect to database if(pData->m_pSqlData->Connect()) { @@ -418,41 +480,41 @@ void CSqlScore::ShowTop5Thread(void *pUser) { // check sort methode char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "SELECT Name, Time FROM %s_%s_race ORDER BY `Time` ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1); + str_format(aBuf, sizeof(aBuf), "SELECT Name, min(Time) as Time FROM %s_%s_race Group By Name ORDER BY `Time` ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf); - + // show top5 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------"); - + int Rank = pData->m_Num; float Time = 0; while(pData->m_pSqlData->m_pResults->next()) { Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); - str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %.2f second(s)", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60)); + str_format(aBuf, sizeof(aBuf), "%d. %s Time: %d minute(s) %.2f second(s)", Rank, pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(Time/60), Time-((int)Time/60*60)); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf); Rank++; } pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "------------------------------"); - + dbg_msg("SQL", "Showing top5 done"); - + // delete results and statement delete pData->m_pSqlData->m_pResults; delete pData->m_pSqlData->m_pStatement; } catch (sql::SQLException &e) { - char aBuf[256]; + char aBuf[256]; str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); - dbg_msg("SQL", aBuf); + dbg_msg("SQL", aBuf); dbg_msg("SQL", "ERROR: Could not show top5"); } - + // disconnect from database pData->m_pSqlData->Disconnect(); } - + delete pData; lock_release(gs_SqlLock); @@ -464,7 +526,7 @@ void CSqlScore::ShowTop5(int ClientID, int Debut) Tmp->m_Num = Debut; Tmp->m_ClientID = ClientID; Tmp->m_pSqlData = this; - + void *Top5Thread = thread_create(ShowTop5Thread, Tmp); #if defined(CONF_FAMILY_UNIX) pthread_detach((pthread_t)Top5Thread); @@ -477,7 +539,7 @@ void CSqlScore::ClearString(char *pString) { char newString[MAX_NAME_LENGTH*2-1]; int pos = 0; - + for(int i=0;i