· 8 years ago · Apr 26, 2018, 02:30 PM
1/* CSqlScore class by Sushi */
2#if defined(CONF_SQL)
3#include <string.h>
4
5#include <engine/shared/config.h>
6#include "../entities/character.h"
7#include "../gamemodes/DDRace.h"
8#include "sql_score.h"
9#include <engine/shared/console.h>
10
11static LOCK gs_SqlLock = 0;
12
13CSqlScore::CSqlScore(CGameContext *pGameServer) : m_pGameServer(pGameServer),
14 m_pServer(pGameServer->Server()),
15 m_pDatabase(g_Config.m_SvSqlDatabase),
16 m_pPrefix(g_Config.m_SvSqlPrefix),
17 m_pUser(g_Config.m_SvSqlUser),
18 m_pPass(g_Config.m_SvSqlPw),
19 m_pIp(g_Config.m_SvSqlIp),
20 m_Port(g_Config.m_SvSqlPort)
21{
22 str_copy(m_aMap, g_Config.m_SvMap, sizeof(m_aMap));
23 NormalizeMapname(m_aMap);
24
25 if(gs_SqlLock == 0)
26 gs_SqlLock = lock_create();
27
28 Init();
29}
30
31CSqlScore::~CSqlScore()
32{
33 lock_wait(gs_SqlLock);
34 lock_release(gs_SqlLock);
35}
36
37bool CSqlScore::Connect()
38{
39 try
40 {
41 // Create connection
42 m_pDriver = get_driver_instance();
43 char aBuf[256];
44 str_format(aBuf, sizeof(aBuf), "tcp://%s:%d", m_pIp, m_Port);
45 m_pConnection = m_pDriver->connect(aBuf, m_pUser, m_pPass);
46
47 // Create Statement
48 m_pStatement = m_pConnection->createStatement();
49
50 // Create database if not exists
51 str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s", m_pDatabase);
52 m_pStatement->execute(aBuf);
53
54 // Connect to specific database
55 m_pConnection->setSchema(m_pDatabase);
56 dbg_msg("SQL", "SQL connection established");
57 return true;
58 }
59 catch (sql::SQLException &e)
60 {
61 char aBuf[256];
62 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
63 dbg_msg("SQL", aBuf);
64
65 dbg_msg("SQL", "ERROR: SQL connection failed");
66 return false;
67 }
68 catch (const std::exception& ex) {
69 // ...
70 dbg_msg("SQL", "1 %s",ex.what());
71
72 } catch (const std::string& ex) {
73 // ...
74 dbg_msg("SQL", "2 %s",ex.c_str());
75 }
76 catch( int )
77 {
78 dbg_msg("SQL", "3 %s");
79 }
80 catch( float )
81 {
82 dbg_msg("SQL", "4 %s");
83 }
84
85 catch( char[] )
86 {
87 dbg_msg("SQL", "5 %s");
88 }
89
90 catch( char )
91 {
92 dbg_msg("SQL", "6 %s");
93 }
94 catch (...)
95 {
96 dbg_msg("SQL", "Unknown Error cause by the MySQL/C++ Connector, my advice compile server_debug and use it");
97
98 dbg_msg("SQL", "ERROR: SQL connection failed");
99 return false;
100 }
101 return false;
102}
103
104void CSqlScore::Disconnect()
105{
106 try
107 {
108 delete m_pConnection;
109 dbg_msg("SQL", "SQL connection disconnected");
110 }
111 catch (sql::SQLException &e)
112 {
113 dbg_msg("SQL", "ERROR: No SQL connection");
114 }
115}
116
117// create tables... should be done only once
118void CSqlScore::Init()
119{
120 // create connection
121 if(Connect())
122 {
123 try
124 {
125 // create tables
126 char aBuf[768];
127
128 str_format(aBuf, sizeof(aBuf), "CREATE TABLE IF NOT EXISTS %s_%s_race (Name VARCHAR(%d) NOT NULL, Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_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, KEY Name (Name)) CHARACTER SET utf8 ;", m_pPrefix, m_aMap, MAX_NAME_LENGTH);
129 m_pStatement->execute(aBuf);
130
131 // Check if table has new column with timestamp
132 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);
133 m_pResults = m_pStatement->executeQuery(aBuf);
134
135 if(m_pResults->rowsCount() < 1){
136 // If not... add the column
137 str_format(aBuf, sizeof(aBuf), "ALTER TABLE %s_%s_race ADD Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER Name, ADD INDEX(Name);",m_pPrefix, m_aMap);
138 m_pStatement->execute(aBuf);
139 }
140
141 dbg_msg("SQL", "Tables were created successfully");
142
143 // get the best time
144 str_format(aBuf, sizeof(aBuf), "SELECT Time FROM %s_%s_race ORDER BY `Time` ASC LIMIT 0, 1;", m_pPrefix, m_aMap);
145 m_pResults = m_pStatement->executeQuery(aBuf);
146
147 if(m_pResults->next())
148 {
149 ((CGameControllerDDRace*)GameServer()->m_pController)->m_CurrentRecord = (float)m_pResults->getDouble("Time");
150
151 dbg_msg("SQL", "Getting best time on server done");
152
153 // delete results
154 delete m_pResults;
155 }
156
157 // delete statement
158 delete m_pStatement;
159 }
160 catch (sql::SQLException &e)
161 {
162 char aBuf[256];
163 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
164 dbg_msg("SQL", aBuf);
165 dbg_msg("SQL", "ERROR: Tables were NOT created");
166 }
167
168 // disconnect from database
169 Disconnect();
170 }
171}
172
173// update stuff
174void CSqlScore::LoadScoreThread(void *pUser)
175{
176 lock_wait(gs_SqlLock);
177
178 CSqlScoreData *pData = (CSqlScoreData *)pUser;
179
180 // Connect to database
181 if(pData->m_pSqlData->Connect())
182 {
183 try
184 {
185 // check strings
186 pData->m_pSqlData->ClearString(pData->m_aName);
187
188 char aBuf[512];
189
190
191 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);
192 pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
193 if(pData->m_pSqlData->m_pResults->next())
194 {
195 // get the best time
196 pData->m_pSqlData->PlayerData(pData->m_ClientID)->m_BestTime = (float)pData->m_pSqlData->m_pResults->getDouble("Time");
197 char aColumn[8];
198 if(g_Config.m_SvCheckpointSave)
199 {
200 for(int i = 0; i < NUM_CHECKPOINTS; i++)
201 {
202 str_format(aColumn, sizeof(aColumn), "cp%d", i+1);
203 pData->m_pSqlData->PlayerData(pData->m_ClientID)->m_aBestCpTime[i] = (float)pData->m_pSqlData->m_pResults->getDouble(aColumn);
204 }
205 }
206 }
207
208 dbg_msg("SQL", "Getting best time done");
209
210 // delete statement and results
211 delete pData->m_pSqlData->m_pStatement;
212 delete pData->m_pSqlData->m_pResults;
213 }
214 catch (sql::SQLException &e)
215 {
216 char aBuf[256];
217 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
218 dbg_msg("SQL", aBuf);
219 dbg_msg("SQL", "ERROR: Could not update account");
220 }
221
222 // disconnect from database
223 pData->m_pSqlData->Disconnect();
224 }
225
226 delete pData;
227
228 lock_release(gs_SqlLock);
229}
230
231void CSqlScore::LoadScore(int ClientID)
232{
233 CSqlScoreData *Tmp = new CSqlScoreData();
234 Tmp->m_ClientID = ClientID;
235 str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName));
236 Tmp->m_pSqlData = this;
237
238 void *LoadThread = thread_create(LoadScoreThread, Tmp);
239#if defined(CONF_FAMILY_UNIX)
240 pthread_detach((pthread_t)LoadThread);
241#endif
242}
243
244void CSqlScore::SaveScoreThread(void *pUser)
245{
246 lock_wait(gs_SqlLock);
247
248 CSqlScoreData *pData = (CSqlScoreData *)pUser;
249
250 // Connect to database
251 if(pData->m_pSqlData->Connect())
252 {
253 try
254 {
255 char aBuf[768];
256
257 // check strings
258 pData->m_pSqlData->ClearString(pData->m_aName);
259
260 // if no entry found... create a new one
261 str_format(aBuf, sizeof(aBuf), "INSERT IGNORE INTO %s_%s_race(Name, Timestamp, 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', CURRENT_TIMESTAMP(), '%.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]);
262 pData->m_pSqlData->m_pStatement->execute(aBuf);
263
264 dbg_msg("SQL", "Updating time done");
265
266 // delete results statement
267 delete pData->m_pSqlData->m_pStatement;
268 }
269 catch (sql::SQLException &e)
270 {
271 char aBuf[256];
272 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
273 dbg_msg("SQL", aBuf);
274 dbg_msg("SQL", "ERROR: Could not update time");
275 }
276
277 // disconnect from database
278 pData->m_pSqlData->Disconnect(); //TODO:Check if an exception is caught will this still execute ?
279 }
280
281 delete pData;
282
283 lock_release(gs_SqlLock);
284}
285
286void CSqlScore::SaveScore(int ClientID, float Time, float CpTime[NUM_CHECKPOINTS])
287{
288 CConsole* pCon = (CConsole*)GameServer()->Console();
289 if(pCon->m_Cheated)
290 return;
291 CSqlScoreData *Tmp = new CSqlScoreData();
292 Tmp->m_ClientID = ClientID;
293 str_copy(Tmp->m_aName, Server()->ClientName(ClientID), sizeof(Tmp->m_aName));
294 Tmp->m_Time = Time;
295 for(int i = 0; i < NUM_CHECKPOINTS; i++)
296 Tmp->m_aCpCurrent[i] = CpTime[i];
297 Tmp->m_pSqlData = this;
298
299 void *SaveThread = thread_create(SaveScoreThread, Tmp);
300#if defined(CONF_FAMILY_UNIX)
301 pthread_detach((pthread_t)SaveThread);
302#endif
303}
304
305void CSqlScore::ShowRankThread(void *pUser)
306{
307 lock_wait(gs_SqlLock);
308
309 CSqlScoreData *pData = (CSqlScoreData *)pUser;
310
311 // Connect to database
312 if(pData->m_pSqlData->Connect())
313 {
314 try
315 {
316 // check strings
317 char originalName[MAX_NAME_LENGTH];
318 strcpy(originalName,pData->m_aName);
319 pData->m_pSqlData->ClearString(pData->m_aName);
320
321 // check sort methode
322 char aBuf[600];
323
324 pData->m_pSqlData->m_pStatement->execute("SET @rownum := 0;");
325 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 "
326 "FROM ("
327 "SELECT * FROM ("
328 "SELECT @rownum := @rownum + 1 AS RANK, Name, Time "
329 "FROM ("
330 "SELECT Name, min(Time) as Time "
331 "FROM %s_%s_race "
332 "Group By Name) as all_top_times "
333 "ORDER BY Time ASC) as all_ranks "
334 "WHERE all_ranks.Name = '%s') as one_rank "
335 "LEFT JOIN %s_%s_race as r "
336 "ON one_rank.Name = r.Name && one_rank.Time = r.Time "
337 "ORDER BY Ago ASC "
338 "LIMIT 0,1"
339 ";", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap,pData->m_aName, pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap);
340
341 pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
342
343 if(pData->m_pSqlData->m_pResults->rowsCount() != 1)
344 {
345 str_format(aBuf, sizeof(aBuf), "%s is not ranked", originalName);
346 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf);
347 }
348 else
349 {
350 pData->m_pSqlData->m_pResults->next();
351 int since = (int)pData->m_pSqlData->m_pResults->getInt("Ago");
352 char agoString[40];
353 mem_zero(agoString, sizeof(agoString));
354 agoTimeToString(since,agoString);
355
356 float Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time");
357 int Rank = (int)pData->m_pSqlData->m_pResults->getInt("Rank");
358 if(g_Config.m_SvHideScore)
359 str_format(aBuf, sizeof(aBuf), "Your time: %d minute(s) %5.2f second(s)", (int)(Time/60), Time-((int)Time/60*60));
360 else
361 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);
362
363 if(pData->m_pSqlData->m_pResults->getInt("stamp") != 0){
364 pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID);
365 str_format(aBuf, sizeof(aBuf), "Finished: %s ago", agoString);
366 }
367 if(pData->m_Search)
368 strcat(aBuf, pData->m_aRequestingPlayer);
369 pData->m_pSqlData->GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, pData->m_ClientID);
370
371 }
372
373 dbg_msg("SQL", "Showing rank done");
374
375 // delete results and statement
376 delete pData->m_pSqlData->m_pResults;
377 delete pData->m_pSqlData->m_pStatement;
378 }
379 catch (sql::SQLException &e)
380 {
381 char aBuf[256];
382 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
383 dbg_msg("SQL", aBuf);
384 dbg_msg("SQL", "ERROR: Could not show rank");
385 }
386
387 // disconnect from database
388 pData->m_pSqlData->Disconnect();//TODO:Check if an exception is caught will this still execute ?
389 }
390
391 delete pData;
392
393 lock_release(gs_SqlLock);
394}
395
396void CSqlScore::ShowRank(int ClientID, const char* pName, bool Search)
397{
398 CSqlScoreData *Tmp = new CSqlScoreData();
399 Tmp->m_ClientID = ClientID;
400 str_copy(Tmp->m_aName, pName, sizeof(Tmp->m_aName));
401 Tmp->m_Search = Search;
402 str_format(Tmp->m_aRequestingPlayer, sizeof(Tmp->m_aRequestingPlayer), " (%s)", Server()->ClientName(ClientID));
403 Tmp->m_pSqlData = this;
404
405 void *RankThread = thread_create(ShowRankThread, Tmp);
406#if defined(CONF_FAMILY_UNIX)
407 pthread_detach((pthread_t)RankThread);
408#endif
409}
410
411void CSqlScore::ShowTop5Thread(void *pUser)
412{
413 lock_wait(gs_SqlLock);
414
415 CSqlScoreData *pData = (CSqlScoreData *)pUser;
416
417 // Connect to database
418 if(pData->m_pSqlData->Connect())
419 {
420 try
421 {
422 // check sort methode
423 char aBuf[512];
424 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);
425 pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
426
427 // show top5
428 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------- Top 5 -----------");
429
430 int Rank = pData->m_Num;
431 float Time = 0;
432 while(pData->m_pSqlData->m_pResults->next())
433 {
434 Time = (float)pData->m_pSqlData->m_pResults->getDouble("Time");
435 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));
436 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf);
437 Rank++;
438 }
439 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "-------------------------------");
440
441 dbg_msg("SQL", "Showing top5 done");
442
443 // delete results and statement
444 delete pData->m_pSqlData->m_pResults;
445 delete pData->m_pSqlData->m_pStatement;
446 }
447 catch (sql::SQLException &e)
448 {
449 char aBuf[256];
450 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
451 dbg_msg("SQL", aBuf);
452 dbg_msg("SQL", "ERROR: Could not show top5");
453 }
454
455 // disconnect from database
456 pData->m_pSqlData->Disconnect();
457 }
458
459 delete pData;
460
461 lock_release(gs_SqlLock);
462}
463
464void CSqlScore::ShowTimesThread(void *pUser)
465{
466 lock_wait(gs_SqlLock);
467 CSqlScoreData *pData = (CSqlScoreData *)pUser;
468
469 // Connect to database
470 if(pData->m_pSqlData->Connect())
471 {
472 try
473 {
474 char originalName[MAX_NAME_LENGTH];
475 strcpy(originalName,pData->m_aName);
476 pData->m_pSqlData->ClearString(pData->m_aName);
477
478 char aBuf[512];
479
480 if(pData->m_Search) // last 5 times of a player
481 str_format(aBuf, sizeof(aBuf), "SELECT Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_%s_race WHERE Name = '%s' ORDER BY Ago ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_aName, pData->m_Num-1);
482 else // last 5 times of server
483 str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, UNIX_TIMESTAMP(CURRENT_TIMESTAMP)-UNIX_TIMESTAMP(Timestamp) as Ago, UNIX_TIMESTAMP(Timestamp) as Stamp FROM %s_%s_race ORDER BY Ago ASC LIMIT %d, 5;", pData->m_pSqlData->m_pPrefix, pData->m_pSqlData->m_aMap, pData->m_Num-1);
484
485 pData->m_pSqlData->m_pResults = pData->m_pSqlData->m_pStatement->executeQuery(aBuf);
486
487 // show top5
488 if(pData->m_pSqlData->m_pResults->rowsCount() == 0){
489 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "There are no times in the specified range");
490 goto end;
491 }
492
493 str_format(aBuf, sizeof(aBuf), "------------ Last Times No %d - %d ------------",pData->m_Num,pData->m_Num + pData->m_pSqlData->m_pResults->rowsCount() - 1);
494 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf);
495
496 float pTime = 0;
497 int pSince = 0;
498 int pStamp = 0;
499
500 while(pData->m_pSqlData->m_pResults->next())
501 {
502 char pAgoString[40] = "\0";
503 pSince = (int)pData->m_pSqlData->m_pResults->getInt("Ago");
504 pStamp = (int)pData->m_pSqlData->m_pResults->getInt("Stamp");
505 pTime = (float)pData->m_pSqlData->m_pResults->getDouble("Time");
506
507 agoTimeToString(pSince,pAgoString);
508
509 if(pData->m_Search) // last 5 times of a player
510 {
511 if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet
512 str_format(aBuf, sizeof(aBuf), "%d min %.2f sec, don't know how long ago", (int)(pTime/60), pTime-((int)pTime/60*60));
513 else
514 str_format(aBuf, sizeof(aBuf), "%s ago, %d min %.2f sec", pAgoString,(int)(pTime/60), pTime-((int)pTime/60*60));
515 }
516 else // last 5 times of the server
517 {
518 if(pStamp == 0) // stamp is 00:00:00 cause it's an old entry from old times where there where no stamps yet
519 str_format(aBuf, sizeof(aBuf), "%s, %d m %.2f s, don't know when", pData->m_pSqlData->m_pResults->getString("Name").c_str(), (int)(pTime/60), pTime-((int)pTime/60*60));
520 else
521 str_format(aBuf, sizeof(aBuf), "%s, %s ago, %d m %.2f s", pData->m_pSqlData->m_pResults->getString("Name").c_str(), pAgoString, (int)(pTime/60), pTime-((int)pTime/60*60));
522 }
523 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, aBuf);
524 }
525 pData->m_pSqlData->GameServer()->SendChatTarget(pData->m_ClientID, "----------------------------------------------------");
526
527 dbg_msg("SQL", "Showing times done");
528
529 // delete results and statement
530 delete pData->m_pSqlData->m_pResults;
531 delete pData->m_pSqlData->m_pStatement;
532 }
533 catch (sql::SQLException &e)
534 {
535 char aBuf[256];
536 str_format(aBuf, sizeof(aBuf), "MySQL Error: %s", e.what());
537 dbg_msg("SQL", aBuf);
538 dbg_msg("SQL", "ERROR: Could not show times");
539 }
540end:
541 // disconnect from database
542 pData->m_pSqlData->Disconnect();
543 }
544 delete pData;
545
546 lock_release(gs_SqlLock);
547}
548
549void CSqlScore::ShowTop5(IConsole::IResult *pResult, int ClientID, void *pUserData, int Debut)
550{
551 CSqlScoreData *Tmp = new CSqlScoreData();
552 Tmp->m_Num = Debut;
553 Tmp->m_ClientID = ClientID;
554 Tmp->m_pSqlData = this;
555
556 void *Top5Thread = thread_create(ShowTop5Thread, Tmp);
557#if defined(CONF_FAMILY_UNIX)
558 pthread_detach((pthread_t)Top5Thread);
559#endif
560}
561
562
563void CSqlScore::ShowTimes(int ClientID, int Debut)
564{
565 CSqlScoreData *Tmp = new CSqlScoreData();
566 Tmp->m_Num = Debut;
567 Tmp->m_ClientID = ClientID;
568 Tmp->m_pSqlData = this;
569 Tmp->m_Search = false;
570
571 void *TimesThread = thread_create(ShowTimesThread, Tmp);
572 #if defined(CONF_FAMILY_UNIX)
573 pthread_detach((pthread_t)TimesThread);
574 #endif
575}
576
577void CSqlScore::ShowTimes(int ClientID, const char* pName, int Debut)
578{
579 CSqlScoreData *Tmp = new CSqlScoreData();
580 Tmp->m_Num = Debut;
581 Tmp->m_ClientID = ClientID;
582 str_copy(Tmp->m_aName, pName, sizeof(Tmp->m_aName));
583 Tmp->m_pSqlData = this;
584 Tmp->m_Search = true;
585
586 void *TimesThread = thread_create(ShowTimesThread, Tmp);
587 #if defined(CONF_FAMILY_UNIX)
588 pthread_detach((pthread_t)TimesThread);
589 #endif
590}
591
592// anti SQL injection
593
594void CSqlScore::ClearString(char *pString)
595{
596 char newString[MAX_NAME_LENGTH*2-1];
597 int pos = 0;
598
599 for(int i=0;i<str_length(pString);i++) {
600 if(pString[i] == '\\') {
601 newString[pos++] = '\\';
602 newString[pos++] = '\\';
603 } else if(pString[i] == '\'') {
604 newString[pos++] = '\\';
605 newString[pos++] = '\'';
606 } else if(pString[i] == '"') {
607 newString[pos++] = '\\';
608 newString[pos++] = '"';
609 } else {
610 newString[pos++] = pString[i];
611 }
612 }
613
614 newString[pos] = '\0';
615
616 strcpy(pString,newString);
617}
618
619void CSqlScore::NormalizeMapname(char *pString) {
620 std::string validChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
621
622 for(int i=0;i<str_length(pString);i++) {
623 if(validChars.find(pString[i]) == std::string::npos) {
624 pString[i] = '_';
625 }
626 }
627}
628
629void CSqlScore::agoTimeToString(int agoTime, char agoString[]){
630 char aBuf[20];
631 int times[7] = {
632 60 * 60 * 24 * 365 ,
633 60 * 60 * 24 * 30 ,
634 60 * 60 * 24 * 7,
635 60 * 60 * 24 ,
636 60 * 60 ,
637 60 ,
638 1
639 };
640 char names[7][6] = {
641 "year",
642 "month",
643 "week",
644 "day",
645 "hour",
646 "min",
647 "sec"
648 };
649
650 int seconds = 0;
651 char name[6];
652 int count = 0;
653 int i = 0;
654
655 // finding biggest match
656 for(i = 0; i<7; i++){
657 seconds = times[i];
658 strcpy(name,names[i]);
659
660 count = floor((float)agoTime/(float)seconds);
661 if(count != 0){
662 break;
663 }
664 }
665
666 if(count == 1){
667 str_format(aBuf, sizeof(aBuf), "%d %s", 1 , name);
668 }else{
669 str_format(aBuf, sizeof(aBuf), "%d %ss", count , name);
670 }
671 strcat(agoString,aBuf);
672
673 if (i + 1 < 7) {
674 // getting second piece now
675 int seconds2 = times[i+1];
676 char name2[6];
677 strcpy(name2,names[i+1]);
678
679 // add second piece if it's greater than 0
680 int count2 = floor((float)(agoTime - (seconds * count)) / (float)seconds2);
681
682 if (count2 != 0) {
683 if(count2 == 1){
684 str_format(aBuf, sizeof(aBuf), " and %d %s", 1 , name2);
685 }else{
686 str_format(aBuf, sizeof(aBuf), " and %d %ss", count2 , name2);
687 }
688 strcat(agoString,aBuf);
689 }
690 }
691}
692#endif