Added timestamp into MySQL ranking, and function to have ago time web2.0 like printed on asking for /rank

This commit is contained in:
Sebastian Wojtowicz 2011-02-13 05:45:17 +01:00 committed by GreYFoXGTi
parent 604ae5286f
commit 60de0da1c3
8 changed files with 279 additions and 214 deletions

View file

@ -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) void CGameContext::MoveCharacter(int ClientID, int Victim, int X, int Y, bool Raw)
{ {
CCharacter* pChr = GetPlayerChar(ClientID); CCharacter* pChr = GetPlayerChar(ClientID);
if(!pChr) if(!pChr)
return; return;
@ -135,10 +135,10 @@ void CGameContext::ConHammer(IConsole::IResult *pResult, void *pUserData, int Cl
int Type = pResult->GetInteger(0); int Type = pResult->GetInteger(0);
CCharacter* pChr = pSelf->GetPlayerChar(Victim); CCharacter* pChr = pSelf->GetPlayerChar(Victim);
if(!pChr) if(!pChr)
return; return;
CServer* pServ = (CServer*)pSelf->Server(); CServer* pServ = (CServer*)pSelf->Server();
if(Type>3 || Type<0) 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"); Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "invalid weapon id");
return; return;
} }
CCharacter* pChr = GetPlayerChar(Victim); CCharacter* pChr = GetPlayerChar(Victim);
if(!pChr) if(!pChr)
return; return;
if(Weapon == -1) if(Weapon == -1)
{ {
if(Remove && (pChr->GetActiveWeapon() == WEAPON_SHOTGUN || pChr->GetActiveWeapon() == WEAPON_GRENADE || pChr->GetActiveWeapon() == WEAPON_RIFLE)) if(Remove && (pChr->GetActiveWeapon() == WEAPON_SHOTGUN || pChr->GetActiveWeapon() == WEAPON_GRENADE || pChr->GetActiveWeapon() == WEAPON_RIFLE))
pChr->SetActiveWeapon(WEAPON_GUN); pChr->SetActiveWeapon(WEAPON_GUN);
if(Remove) if(Remove)
{ {
pChr->SetWeaponGot(WEAPON_SHOTGUN, false); 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); pChr->SetWeaponGot(WEAPON_RIFLE, false);
} }
else else
pChr->GiveAllWeapons(); pChr->GiveAllWeapons();
} }
else if(Weapon != WEAPON_NINJA) else if(Weapon != WEAPON_NINJA)
{ {
if(Remove && pChr->GetActiveWeapon() == Weapon) if(Remove && pChr->GetActiveWeapon() == Weapon)
pChr->SetActiveWeapon(WEAPON_GUN); pChr->SetActiveWeapon(WEAPON_GUN);
if(Remove) if(Remove)
pChr->SetWeaponGot(Weapon, false); pChr->SetWeaponGot(Weapon, false);
else 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"); Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "you can't remove ninja");
return; return;
} }
pChr->GiveNinja(); pChr->GiveNinja();
} }
@ -399,11 +399,11 @@ void CGameContext::ConFreeze(IConsole::IResult *pResult, void *pUserData, int Cl
if(pResult->NumArguments()) if(pResult->NumArguments())
Seconds = clamp(pResult->GetInteger(0), -2, 9999); Seconds = clamp(pResult->GetInteger(0), -2, 9999);
CCharacter* pChr = pSelf->GetPlayerChar(Victim); CCharacter* pChr = pSelf->GetPlayerChar(Victim);
if(!pChr) if(!pChr)
return; return;
if(pSelf->m_apPlayers[Victim]) if(pSelf->m_apPlayers[Victim])
{ {
pChr->Freeze(Seconds); 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()) if(/*g_Config.m_SvSpamprotection && */pPlayer->m_Last_Chat && pPlayer->m_Last_Chat + pSelf->Server()->TickSpeed() + g_Config.m_SvChatDelay > pSelf->Server()->Tick())
return; return;
pPlayer->m_Last_Chat = pSelf->Server()->Tick(); pPlayer->m_Last_Chat = pSelf->Server()->Tick();
if(pResult->NumArguments() > 0) 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) void CGameContext::ConJoinTeam(IConsole::IResult *pResult, void *pUserData, int ClientID)
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
CGameControllerDDRace* Controller = (CGameControllerDDRace*)pSelf->m_pController; CGameControllerDDRace* Controller = (CGameControllerDDRace*)pSelf->m_pController;
if(g_Config.m_SvTeam == 0) if(g_Config.m_SvTeam == 0)
@ -868,7 +868,7 @@ void CGameContext::ConMe(IConsole::IResult *pResult, void *pUserData, int Client
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
char aBuf[256 + 24]; char aBuf[256 + 24];
str_format(aBuf, 256 + 24, "'%s' %s", pSelf->Server()->ClientName(ClientID), pResult->GetString(0)); str_format(aBuf, 256 + 24, "'%s' %s", pSelf->Server()->ClientName(ClientID), pResult->GetString(0));
if(g_Config.m_SvSlashMe) if(g_Config.m_SvSlashMe)
pSelf->SendChat(-2, CGameContext::CHAT_ALL, aBuf, ClientID); 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) void CGameContext::ConToggleEyeEmote(IConsole::IResult *pResult, void *pUserData, int ClientID)
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter(); CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter();
if(pChr) if(pChr)
@ -892,7 +892,7 @@ void CGameContext::ConToggleEyeEmote(IConsole::IResult *pResult, void *pUserData
void CGameContext::ConToggleBroadcast(IConsole::IResult *pResult, void *pUserData, int ClientID) void CGameContext::ConToggleBroadcast(IConsole::IResult *pResult, void *pUserData, int ClientID)
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter(); CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter();
if(pChr) if(pChr)
@ -902,39 +902,39 @@ void CGameContext::ConToggleBroadcast(IConsole::IResult *pResult, void *pUserDat
void CGameContext::ConEyeEmote(IConsole::IResult *pResult, void *pUserData, int ClientID) void CGameContext::ConEyeEmote(IConsole::IResult *pResult, void *pUserData, int ClientID)
{ {
CGameContext *pSelf = (CGameContext *)pUserData; CGameContext *pSelf = (CGameContext *)pUserData;
CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter(); CCharacter *pChr = pSelf->m_apPlayers[ClientID]->GetCharacter();
if (pResult->NumArguments() == 0) 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", "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)"); 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")) 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")) 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")) 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")) 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")) 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")) else if (!str_comp(pResult->GetString(0), "surprise"))
pChr->m_DefEmote = EMOTE_SURPRISE; pChr->m_DefEmote = EMOTE_SURPRISE;
else else
{ {
pSelf->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "Unkown emote... Say /emote"); pSelf->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "info", "Unkown emote... Say /emote");
} }
int Duration = 1; int Duration = 1;
if (pResult->NumArguments() > 1) if (pResult->NumArguments() > 1)
Duration = pResult->GetInteger(1); Duration = pResult->GetInteger(1);
pChr->m_DefEmoteReset = pSelf->Server()->Tick() + Duration * pSelf->Server()->TickSpeed(); pChr->m_DefEmoteReset = pSelf->Server()->Tick() + Duration * pSelf->Server()->TickSpeed();
} }
} }

View file

@ -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 // update the score
pData->Set(time, m_CpCurrent); pData->Set(time, m_CpCurrent);
}
else
{
if(str_comp_num(Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", 12) != 0) if(str_comp_num(Server()->ClientName(m_pPlayer->GetCID()), "nameless tee", 12) != 0)
GameServer()->Score()->SaveScore(m_pPlayer->GetCID(), time, this); GameServer()->Score()->SaveScore(m_pPlayer->GetCID(), time, this);
} }

View file

@ -33,10 +33,10 @@ void CGameControllerDDRace::InitTeleporter()
for(int i = 0; i < Width*Height; i++) 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) && 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)); i/GameServer()->Collision()->Layers()->TeleLayer()->m_Width*32+16));
} }
} }

View file

@ -12,14 +12,14 @@
class CGameControllerDDRace : public IGameController class CGameControllerDDRace : public IGameController
{ {
public: public:
CGameControllerDDRace(class CGameContext *pGameServer); CGameControllerDDRace(class CGameContext *pGameServer);
~CGameControllerDDRace(); ~CGameControllerDDRace();
CGameTeams m_Teams; CGameTeams m_Teams;
std::map < int , std::vector < vec2 > > m_TeleOuts; std::map < int , std::vector < vec2 > > m_TeleOuts;
void InitTeleporter(); void InitTeleporter();
virtual void Tick(); virtual void Tick();
}; };

View file

@ -23,17 +23,17 @@ CFileScore::CFileScore(CGameContext *pGameServer) : m_pGameServer(pGameServer),
{ {
if(gs_ScoreLock == 0) if(gs_ScoreLock == 0)
gs_ScoreLock = lock_create(); gs_ScoreLock = lock_create();
Init(); Init();
} }
CFileScore::~CFileScore() CFileScore::~CFileScore()
{ {
lock_wait(gs_ScoreLock); lock_wait(gs_ScoreLock);
// clear list // clear list
m_Top.clear(); m_Top.clear();
lock_release(gs_ScoreLock); lock_release(gs_ScoreLock);
} }
@ -85,14 +85,14 @@ void CFileScore::Save()
void CFileScore::Init() void CFileScore::Init()
{ {
lock_wait(gs_ScoreLock); lock_wait(gs_ScoreLock);
// create folder if not exist // create folder if not exist
if(g_Config.m_SvScoreFolder[0]) if(g_Config.m_SvScoreFolder[0])
fs_makedir(g_Config.m_SvScoreFolder); fs_makedir(g_Config.m_SvScoreFolder);
std::fstream f; std::fstream f;
f.open(SaveFile().c_str(), std::ios::in); f.open(SaveFile().c_str(), std::ios::in);
while(!f.eof() && !f.fail()) while(!f.eof() && !f.fail())
{ {
std::string TmpName, TmpScore, TmpCpLine; std::string TmpName, TmpScore, TmpCpLine;
@ -118,7 +118,7 @@ void CFileScore::Init()
} }
f.close(); f.close();
lock_release(gs_ScoreLock); lock_release(gs_ScoreLock);
// save the current best score // save the current best score
if(m_Top.size()) if(m_Top.size())
((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = m_Top[0].m_Score; ((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]) void CFileScore::UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS])
{ {
const char *pName = Server()->ClientName(ID); const char *pName = Server()->ClientName(ID);
lock_wait(gs_ScoreLock); lock_wait(gs_ScoreLock);
CPlayerScore *pPlayer = SearchScore(ID, 0); CPlayerScore *pPlayer = SearchScore(ID, 0);
if(pPlayer) if(pPlayer)
{ {
for(int c = 0; c < NUM_CHECKPOINTS; c++) for(int c = 0; c < NUM_CHECKPOINTS; c++)
pPlayer->m_aCpTime[c] = aCpTime[c]; pPlayer->m_aCpTime[c] = aCpTime[c];
pPlayer->m_Score = Score; pPlayer->m_Score = Score;
str_copy(pPlayer->m_aName, pName, sizeof(pPlayer->m_aName)); str_copy(pPlayer->m_aName, pName, sizeof(pPlayer->m_aName));
sort(m_Top.all()); sort(m_Top.all());
} }
else else
m_Top.add(*new CPlayerScore(pName, Score, aCpTime)); m_Top.add(*new CPlayerScore(pName, Score, aCpTime));
lock_release(gs_ScoreLock); lock_release(gs_ScoreLock);
Save(); Save();
} }
@ -187,7 +187,7 @@ void CFileScore::LoadScore(int ClientID)
lock_release(gs_ScoreLock); lock_release(gs_ScoreLock);
Save(); Save();
} }
// set score // set score
if(pPlayer) if(pPlayer)
PlayerData(ClientID)->Set(pPlayer->m_Score, pPlayer->m_aCpTime); 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; CPlayerScore *pScore;
int Pos; int Pos;
char aBuf[512]; char aBuf[512];
if(!Search) if(!Search)
pScore = SearchScore(ClientID, &Pos); pScore = SearchScore(ClientID, &Pos);
else else
pScore = SearchName(pName, &Pos, 1); pScore = SearchName(pName, &Pos, 1);
if(pScore && Pos > -1) if(pScore && Pos > -1)
{ {
float Time = pScore->m_Score; 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."); str_format(aBuf, sizeof(aBuf), "Several players were found.");
else else
str_format(aBuf, sizeof(aBuf), "%s is not ranked", Search?pName:Server()->ClientName(ClientID)); str_format(aBuf, sizeof(aBuf), "%s is not ranked", Search?pName:Server()->ClientName(ClientID));
GameServer()->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "rank", aBuf); GameServer()->Console()->PrintResponse(IConsole::OUTPUT_LEVEL_STANDARD, "rank", aBuf);
} }

View file

@ -11,42 +11,42 @@ class CFileScore : public IScore
{ {
CGameContext *m_pGameServer; CGameContext *m_pGameServer;
IServer *m_pServer; IServer *m_pServer;
class CPlayerScore class CPlayerScore
{ {
public: public:
char m_aName[MAX_NAME_LENGTH]; char m_aName[MAX_NAME_LENGTH];
float m_Score; float m_Score;
float m_aCpTime[NUM_CHECKPOINTS]; float m_aCpTime[NUM_CHECKPOINTS];
CPlayerScore() {}; CPlayerScore() {};
CPlayerScore(const char *pName, float Score, float aCpTime[NUM_CHECKPOINTS]); CPlayerScore(const char *pName, float Score, float aCpTime[NUM_CHECKPOINTS]);
bool operator<(const CPlayerScore& other) { return (this->m_Score < other.m_Score); } bool operator<(const CPlayerScore& other) { return (this->m_Score < other.m_Score); }
}; };
sorted_array<CPlayerScore> m_Top; sorted_array<CPlayerScore> m_Top;
CGameContext *GameServer() { return m_pGameServer; } CGameContext *GameServer() { return m_pGameServer; }
IServer *Server() { return m_pServer; } IServer *Server() { return m_pServer; }
CPlayerScore *SearchScore(int ID, int *pPosition){ return SearchName(Server()->ClientName(ID), pPosition, 0 );}; CPlayerScore *SearchScore(int ID, int *pPosition){ return SearchName(Server()->ClientName(ID), pPosition, 0 );};
; ;
CPlayerScore *SearchName(const char *pName, int *pPosition, bool MatchCase); CPlayerScore *SearchName(const char *pName, int *pPosition, bool MatchCase);
void UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS]); void UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS]);
void Init(); void Init();
void Save(); void Save();
static void SaveScoreThread(void *pUser); static void SaveScoreThread(void *pUser);
public: public:
CFileScore(CGameContext *pGameServer); CFileScore(CGameContext *pGameServer);
~CFileScore(); ~CFileScore();
virtual void LoadScore(int ClientID); virtual void LoadScore(int ClientID);
virtual void SaveScore(int ClientID, float Time, CCharacter *pChar); virtual void SaveScore(int ClientID, float Time, CCharacter *pChar);
virtual void ShowTop5(int ClientID, int Debut=1); virtual void ShowTop5(int ClientID, int Debut=1);
virtual void ShowRank(int ClientID, const char* pName, bool Search=false); virtual void ShowRank(int ClientID, const char* pName, bool Search=false);
}; };

View file

@ -9,22 +9,21 @@
static LOCK gs_SqlLock = 0; static LOCK gs_SqlLock = 0;
CSqlScore::CSqlScore(CGameContext *pGameServer) CSqlScore::CSqlScore(CGameContext *pGameServer) : m_pGameServer(pGameServer),
: m_pGameServer(pGameServer), m_pServer(pGameServer->Server()),
m_pServer(pGameServer->Server()), m_pDatabase(g_Config.m_SvSqlDatabase),
m_pDatabase(g_Config.m_SvSqlDatabase), m_pPrefix(g_Config.m_SvSqlPrefix),
m_pPrefix(g_Config.m_SvSqlPrefix), m_pUser(g_Config.m_SvSqlUser),
m_pUser(g_Config.m_SvSqlUser), m_pPass(g_Config.m_SvSqlPw),
m_pPass(g_Config.m_SvSqlPw), m_pIp(g_Config.m_SvSqlIp),
m_pIp(g_Config.m_SvSqlIp), m_Port(g_Config.m_SvSqlPort)
m_Port(g_Config.m_SvSqlPort)
{ {
str_copy(m_aMap, g_Config.m_SvMap, sizeof(m_aMap)); str_copy(m_aMap, g_Config.m_SvMap, sizeof(m_aMap));
NormalizeMapname(m_aMap); NormalizeMapname(m_aMap);
if(gs_SqlLock == 0) if(gs_SqlLock == 0)
gs_SqlLock = lock_create(); gs_SqlLock = lock_create();
Init(); Init();
} }
@ -36,65 +35,65 @@ CSqlScore::~CSqlScore()
bool CSqlScore::Connect() bool CSqlScore::Connect()
{ {
try try
{ {
// Create connection // Create connection
m_pDriver = get_driver_instance(); m_pDriver = get_driver_instance();
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "tcp://%s:%d", m_pIp, m_Port); str_format(aBuf, sizeof(aBuf), "tcp://%s:%d", m_pIp, m_Port);
m_pConnection = m_pDriver->connect(aBuf, m_pUser, m_pPass); m_pConnection = m_pDriver->connect(aBuf, m_pUser, m_pPass);
// Create Statement // Create Statement
m_pStatement = m_pConnection->createStatement(); m_pStatement = m_pConnection->createStatement();
// Create database if not exists // Create database if not exists
str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s", m_pDatabase); str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s", m_pDatabase);
m_pStatement->execute(aBuf); m_pStatement->execute(aBuf);
// Connect to specific database // Connect to specific database
m_pConnection->setSchema(m_pDatabase); m_pConnection->setSchema(m_pDatabase);
dbg_msg("SQL", "SQL connection established"); dbg_msg("SQL", "SQL connection established");
return true; return true;
} }
catch (sql::SQLException &e) catch (sql::SQLException &e)
{ {
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
dbg_msg("SQL", aBuf); dbg_msg("SQL", aBuf);
dbg_msg("SQL", "ERROR: SQL connection failed"); dbg_msg("SQL", "ERROR: SQL connection failed");
return false; return false;
} }
catch (const std::exception& ex) { catch (const std::exception& ex) {
// ... // ...
dbg_msg("SQL", "1 %s",ex.what()); dbg_msg("SQL", "1 %s",ex.what());
} catch (const std::string& ex) { } catch (const std::string& ex) {
// ... // ...
dbg_msg("SQL", "2 %s",ex.c_str()); dbg_msg("SQL", "2 %s",ex.c_str());
} }
catch( int ) catch( int )
{ {
dbg_msg("SQL", "3 %s"); dbg_msg("SQL", "3 %s");
} }
catch( float ) catch( float )
{ {
dbg_msg("SQL", "4 %s"); dbg_msg("SQL", "4 %s");
} }
catch( char[] ) catch( char[] )
{ {
dbg_msg("SQL", "5 %s"); dbg_msg("SQL", "5 %s");
} }
catch( char ) catch( char )
{ {
dbg_msg("SQL", "6 %s"); dbg_msg("SQL", "6 %s");
} }
catch (...) catch (...)
{ {
dbg_msg("SQL", "Unknown Error cause by the MySQL/C++ Connector, my advice compile server_debug and use it"); 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"); dbg_msg("SQL", "ERROR: SQL connection failed");
return false; return false;
} }
@ -124,32 +123,46 @@ void CSqlScore::Init()
{ {
// create tables // create tables
char aBuf[768]; 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); 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"); dbg_msg("SQL", "Tables were created successfully");
// get the best time // 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); 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); m_pResults = m_pStatement->executeQuery(aBuf);
if(m_pResults->next()) if(m_pResults->next())
{ {
((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = (float)m_pResults->getDouble("Time"); ((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = (float)m_pResults->getDouble("Time");
dbg_msg("SQL", "Getting best time on server done"); dbg_msg("SQL", "Getting best time on server done");
// delete results // delete results
delete m_pResults; delete m_pResults;
} }
// delete statement // delete statement
delete m_pStatement; delete m_pStatement;
} }
catch (sql::SQLException &e) catch (sql::SQLException &e)
{ {
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); 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"); dbg_msg("SQL", "ERROR: Tables were NOT created");
} }
@ -162,9 +175,9 @@ void CSqlScore::Init()
void CSqlScore::LoadScoreThread(void *pUser) void CSqlScore::LoadScoreThread(void *pUser)
{ {
lock_wait(gs_SqlLock); lock_wait(gs_SqlLock);
CSqlScoreData *pData = (CSqlScoreData *)pUser; CSqlScoreData *pData = (CSqlScoreData *)pUser;
// Connect to database // Connect to database
if(pData->m_pSqlData->Connect()) if(pData->m_pSqlData->Connect())
{ {
@ -172,11 +185,11 @@ void CSqlScore::LoadScoreThread(void *pUser)
{ {
// check strings // check strings
pData->m_pSqlData->ClearString(pData->m_aName); pData->m_pSqlData->ClearString(pData->m_aName);
char aBuf[512]; 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); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
if(pData->m_pSqlData->m_pResults->next()) if(pData->m_pSqlData->m_pResults->next())
{ {
@ -192,25 +205,25 @@ void CSqlScore::LoadScoreThread(void *pUser)
} }
} }
} }
dbg_msg("SQL", "Getting best time done"); dbg_msg("SQL", "Getting best time done");
// delete statement and results // delete statement and results
delete pData->m_pSqlData->m_pStatement; delete pData->m_pSqlData->m_pStatement;
delete pData->m_pSqlData->m_pResults; delete pData->m_pSqlData->m_pResults;
} }
catch (sql::SQLException &e) catch (sql::SQLException &e)
{ {
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); 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 account"); dbg_msg("SQL", "ERROR: Could not update account");
} }
// disconnect from database // disconnect from database
pData->m_pSqlData->Disconnect(); pData->m_pSqlData->Disconnect();
} }
delete pData; delete pData;
lock_release(gs_SqlLock); lock_release(gs_SqlLock);
@ -222,7 +235,7 @@ void CSqlScore::LoadScore(int ClientID)
Tmp->m_ClientID = ClientID; Tmp->m_ClientID = ClientID;
str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName)); str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName));
Tmp->m_pSqlData = this; Tmp->m_pSqlData = this;
void *LoadThread = thread_create(LoadScoreThread, Tmp); void *LoadThread = thread_create(LoadScoreThread, Tmp);
#if defined(CONF_FAMILY_UNIX) #if defined(CONF_FAMILY_UNIX)
pthread_detach((pthread_t)LoadThread); pthread_detach((pthread_t)LoadThread);
@ -232,9 +245,9 @@ void CSqlScore::LoadScore(int ClientID)
void CSqlScore::SaveScoreThread(void *pUser) void CSqlScore::SaveScoreThread(void *pUser)
{ {
lock_wait(gs_SqlLock); lock_wait(gs_SqlLock);
CSqlScoreData *pData = (CSqlScoreData *)pUser; CSqlScoreData *pData = (CSqlScoreData *)pUser;
// Connect to database // Connect to database
if(pData->m_pSqlData->Connect()) if(pData->m_pSqlData->Connect())
{ {
@ -242,61 +255,30 @@ void CSqlScore::SaveScoreThread(void *pUser)
{ {
// check strings // check strings
pData->m_pSqlData->ClearString(pData->m_aName); pData->m_pSqlData->ClearString(pData->m_aName);
char aBuf[768]; 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 // 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]); 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); pData->m_pSqlData->m_pStatement->execute(aBuf);
dbg_msg("SQL", "Updating time done"); dbg_msg("SQL", "Updating time done");
// delete results statement // delete results statement
delete pData->m_pSqlData->m_pResults;
delete pData->m_pSqlData->m_pStatement; delete pData->m_pSqlData->m_pStatement;
} }
catch (sql::SQLException &e) catch (sql::SQLException &e)
{ {
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); 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"); dbg_msg("SQL", "ERROR: Could not update time");
} }
// disconnect from database // disconnect from database
pData->m_pSqlData->Disconnect(); //TODO:Check if an exception is caught will this still execute ? pData->m_pSqlData->Disconnect(); //TODO:Check if an exception is caught will this still execute ?
} }
delete pData; delete pData;
lock_release(gs_SqlLock); 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++) for(int i = 0; i < NUM_CHECKPOINTS; i++)
Tmp->m_aCpCurrent[i] = pChar->m_CpCurrent[i]; Tmp->m_aCpCurrent[i] = pChar->m_CpCurrent[i];
Tmp->m_pSqlData = this; Tmp->m_pSqlData = this;
void *SaveThread = thread_create(SaveScoreThread, Tmp); void *SaveThread = thread_create(SaveScoreThread, Tmp);
#if defined(CONF_FAMILY_UNIX) #if defined(CONF_FAMILY_UNIX)
pthread_detach((pthread_t)SaveThread); pthread_detach((pthread_t)SaveThread);
@ -321,9 +303,9 @@ void CSqlScore::SaveScore(int ClientID, float Time, CCharacter *pChar)
void CSqlScore::ShowRankThread(void *pUser) void CSqlScore::ShowRankThread(void *pUser)
{ {
lock_wait(gs_SqlLock); lock_wait(gs_SqlLock);
CSqlScoreData *pData = (CSqlScoreData *)pUser; CSqlScoreData *pData = (CSqlScoreData *)pUser;
// Connect to database // Connect to database
if(pData->m_pSqlData->Connect()) if(pData->m_pSqlData->Connect())
{ {
@ -333,19 +315,27 @@ void CSqlScore::ShowRankThread(void *pUser)
char originalName[MAX_NAME_LENGTH]; char originalName[MAX_NAME_LENGTH];
strcpy(originalName,pData->m_aName); strcpy(originalName,pData->m_aName);
pData->m_pSqlData->ClearString(pData->m_aName); pData->m_pSqlData->ClearString(pData->m_aName);
// check sort methode // check sort methode
char aBuf[512]; char aBuf[512];
pData->m_pSqlData->m_pStatement->execute("SET @rownum := 0;"); 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 (" "FROM ("
"SELECT @rownum := @rownum + 1 AS RANK, Name, Time FROM %s_%s_race ORDER BY Time ASC" "SELECT * FROM ("
") as Query " "SELECT @rownum := @rownum + 1 AS RANK, Name, Time "
"WHERE Name = '%s';", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap,pData->m_aName); "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); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
if(pData->m_pSqlData->m_pResults->rowsCount() != 1) if(pData->m_pSqlData->m_pResults->rowsCount() != 1)
{ {
str_format(aBuf, sizeof(aBuf), "%s is not ranked", originalName); str_format(aBuf, sizeof(aBuf), "%s is not ranked", originalName);
@ -353,43 +343,115 @@ void CSqlScore::ShowRankThread(void *pUser)
} }
else else
{ {
pData->m_pSqlData->m_pResults->next(); pData->m_pSqlData->m_pResults->next();
float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); int since = (int)pData->m_pSqlData->m_pResults->getInt("Ago");
int Rank = (float)pData->m_pSqlData->m_pResults->getInt("Rank"); 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) 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)); str_format(aBuf, sizeof(aBuf), "Your time: %d minute(s) %5.2f second(s)", (int)(Time/60), Time-((int)Time/60*60));
else 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) if(pData->m_Search)
strcat(aBuf, pData->m_aRequestingPlayer); strcat(aBuf, pData->m_aRequestingPlayer);
pData->m_pSqlData->GameServer()->SendChatTarget(-1, aBuf); pData->m_pSqlData->GameServer()->SendChatTarget(-1, aBuf);
} }
dbg_msg("SQL", "Showing rank done"); dbg_msg("SQL", "Showing rank done");
// delete results and statement // delete results and statement
delete pData->m_pSqlData->m_pResults; delete pData->m_pSqlData->m_pResults;
delete pData->m_pSqlData->m_pStatement; delete pData->m_pSqlData->m_pStatement;
} }
catch (sql::SQLException &e) catch (sql::SQLException &e)
{ {
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); 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"); dbg_msg("SQL", "ERROR: Could not show rank");
} }
// disconnect from database // disconnect from database
pData->m_pSqlData->Disconnect();//TODO:Check if an exception is caught will this still execute ? pData->m_pSqlData->Disconnect();//TODO:Check if an exception is caught will this still execute ?
} }
delete pData; delete pData;
lock_release(gs_SqlLock); 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) void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search)
{ {
CSqlScoreData *Tmp = new CSqlScoreData(); CSqlScoreData *Tmp = new CSqlScoreData();
@ -398,7 +460,7 @@ void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search)
Tmp->m_Search = Search; Tmp->m_Search = Search;
str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), " (%s)", Server()->ClientName(ClientID)); str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), " (%s)", Server()->ClientName(ClientID));
Tmp->m_pSqlData = this; Tmp->m_pSqlData = this;
void *RankThread = thread_create(ShowRankThread, Tmp); void *RankThread = thread_create(ShowRankThread, Tmp);
#if defined(CONF_FAMILY_UNIX) #if defined(CONF_FAMILY_UNIX)
pthread_detach((pthread_t)RankThread); pthread_detach((pthread_t)RankThread);
@ -408,9 +470,9 @@ void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search)
void CSqlScore::ShowTop5Thread(void *pUser) void CSqlScore::ShowTop5Thread(void *pUser)
{ {
lock_wait(gs_SqlLock); lock_wait(gs_SqlLock);
CSqlScoreData *pData = (CSqlScoreData *)pUser; CSqlScoreData *pData = (CSqlScoreData *)pUser;
// Connect to database // Connect to database
if(pData->m_pSqlData->Connect()) if(pData->m_pSqlData->Connect())
{ {
@ -418,41 +480,41 @@ void CSqlScore::ShowTop5Thread(void *pUser)
{ {
// check sort methode // check sort methode
char aBuf[512]; 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); pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
// show top5 // show top5
pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------"); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------");
int Rank = pData->m_Num; int Rank = pData->m_Num;
float Time = 0; float Time = 0;
while(pData->m_pSqlData->m_pResults->next()) while(pData->m_pSqlData->m_pResults->next())
{ {
Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time"); 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); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf);
Rank++; Rank++;
} }
pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "------------------------------"); pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "------------------------------");
dbg_msg("SQL", "Showing top5 done"); dbg_msg("SQL", "Showing top5 done");
// delete results and statement // delete results and statement
delete pData->m_pSqlData->m_pResults; delete pData->m_pSqlData->m_pResults;
delete pData->m_pSqlData->m_pStatement; delete pData->m_pSqlData->m_pStatement;
} }
catch (sql::SQLException &e) catch (sql::SQLException &e)
{ {
char aBuf[256]; char aBuf[256];
str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what()); 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"); dbg_msg("SQL", "ERROR: Could not show top5");
} }
// disconnect from database // disconnect from database
pData->m_pSqlData->Disconnect(); pData->m_pSqlData->Disconnect();
} }
delete pData; delete pData;
lock_release(gs_SqlLock); lock_release(gs_SqlLock);
@ -464,7 +526,7 @@ void CSqlScore::ShowTop5(int ClientID, int Debut)
Tmp->m_Num = Debut; Tmp->m_Num = Debut;
Tmp->m_ClientID = ClientID; Tmp->m_ClientID = ClientID;
Tmp->m_pSqlData = this; Tmp->m_pSqlData = this;
void *Top5Thread = thread_create(ShowTop5Thread, Tmp); void *Top5Thread = thread_create(ShowTop5Thread, Tmp);
#if defined(CONF_FAMILY_UNIX) #if defined(CONF_FAMILY_UNIX)
pthread_detach((pthread_t)Top5Thread); pthread_detach((pthread_t)Top5Thread);
@ -477,7 +539,7 @@ void CSqlScore::ClearString(char *pString)
{ {
char newString[MAX_NAME_LENGTH*2-1]; char newString[MAX_NAME_LENGTH*2-1];
int pos = 0; int pos = 0;
for(int i=0;i<str_length(pString);i++) { for(int i=0;i<str_length(pString);i++) {
if(pString[i] == '\\') { if(pString[i] == '\\') {
newString[pos++] = '\\'; newString[pos++] = '\\';
@ -492,15 +554,15 @@ void CSqlScore::ClearString(char *pString)
newString[pos++] = pString[i]; newString[pos++] = pString[i];
} }
} }
newString[pos] = '\0'; newString[pos] = '\0';
strcpy(pString,newString); strcpy(pString,newString);
} }
void CSqlScore::NormalizeMapname(char *pString) { void CSqlScore::NormalizeMapname(char *pString) {
std::string validChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"); std::string validChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
for(int i=0;i<str_length(pString);i++) { for(int i=0;i<str_length(pString);i++) {
if(validChars.find(pString[i]) == std::string::npos) { if(validChars.find(pString[i]) == std::string::npos) {
pString[i] = '_'; pString[i] = '_';

View file

@ -15,12 +15,12 @@ class CSqlScore : public IScore
{ {
CGameContext *m_pGameServer; CGameContext *m_pGameServer;
IServer *m_pServer; IServer *m_pServer;
sql::Driver *m_pDriver; sql::Driver *m_pDriver;
sql::Connection *m_pConnection; sql::Connection *m_pConnection;
sql::Statement *m_pStatement; sql::Statement *m_pStatement;
sql::ResultSet *m_pResults; sql::ResultSet *m_pResults;
// copy of config vars // copy of config vars
const char* m_pDatabase; const char* m_pDatabase;
const char* m_pPrefix; const char* m_pPrefix;
@ -29,34 +29,35 @@ class CSqlScore : public IScore
const char* m_pIp; const char* m_pIp;
char m_aMap[64]; char m_aMap[64];
int m_Port; int m_Port;
CGameContext *GameServer() { return m_pGameServer; } CGameContext *GameServer() { return m_pGameServer; }
IServer *Server() { return m_pServer; } IServer *Server() { return m_pServer; }
static void LoadScoreThread(void *pUser); static void LoadScoreThread(void *pUser);
static void SaveScoreThread(void *pUser); static void SaveScoreThread(void *pUser);
static void ShowRankThread(void *pUser); static void ShowRankThread(void *pUser);
static void ShowTop5Thread(void *pUser); static void ShowTop5Thread(void *pUser);
void Init(); void Init();
bool Connect(); bool Connect();
void Disconnect(); void Disconnect();
// anti SQL injection // anti SQL injection
void ClearString(char *pString); void ClearString(char *pString);
void NormalizeMapname(char *pString); void NormalizeMapname(char *pString);
public: public:
CSqlScore(CGameContext *pGameServer); CSqlScore(CGameContext *pGameServer);
~CSqlScore(); ~CSqlScore();
virtual void LoadScore(int ClientID); virtual void LoadScore(int ClientID);
virtual void SaveScore(int ClientID, float Time, CCharacter *pChar); virtual void SaveScore(int ClientID, float Time, CCharacter *pChar);
virtual void ShowRank(int ClientID, const char* pName, bool Search=false); virtual void ShowRank(int ClientID, const char* pName, bool Search=false);
virtual void ShowTop5(int ClientID, int Debut=1); virtual void ShowTop5(int ClientID, int Debut=1);
static void agoTimeToString(int agoTime, char agoStrign[]);
}; };
struct CSqlScoreData struct CSqlScoreData