· 9 years ago · Nov 03, 2016, 07:34 PM
1/* Plugin Template generated by Pawn Studio */
2
3#include <sourcemod>
4#include "sodstats\include\sodstats.inc"
5
6#define SODSTATS_VERSION "1.0.11"
7
8#define MAX_STEAMID_LENGTH 128
9#define MAX_WEAPON_NAME_LENGTH 32
10
11new String:g_sql_saveplayer[] =
12 "UPDATE players SET score = %i, kills = %i, deaths = %i, shots = %i, hits = %i, name = '%s', time_played = time_played + %i, headshots = %i, last_connect = current_timestamp WHERE steamid = '%s'"
13
14new String:g_sql_createplayer[] =
15 "INSERT INTO players (score, kills, deaths, shots, hits, steamid, name, time_played, headshots, last_connect) VALUES (0, 0, 0, 0, 0, '%s', '%s', 0, 0, current_timestamp)"
16
17new String:g_sqlite_createtable_players[] =
18 "CREATE TABLE IF NOT EXISTS players (rank INTEGER PRIMARY KEY AUTOINCREMENT,score int(12) NOT NULL default 0,steamid varchar(255) NOT NULL default '',kills int(12) NOT NULL default 0,deaths int(12) NOT NULL default 0,shots int(12) NOT NULL default 0,hits int(12) NOT NULL default 0,name varchar(255) NOT NULL default '',time_played int(11) NOT NULL default 0, headshots int(12) NOT NULL default 0, last_connect timestamp NOT NULL default CURRENT_TIMESTAMP);";
19
20new String:g_mysql_createtable_players[] =
21 "CREATE TABLE IF NOT EXISTS players (rank INTEGER PRIMARY KEY AUTO_INCREMENT,score int(12) NOT NULL default 0,steamid varchar(255) NOT NULL default '',kills int(12) NOT NULL default 0,deaths int(12) NOT NULL default 0,shots int(12) NOT NULL default 0,hits int(12) NOT NULL default 0,name varchar(255) NOT NULL default '',time_played int(11) NOT NULL default 0, headshots int(12) NOT NULL default 0, last_connect timestamp NOT NULL default CURRENT_TIMESTAMP);";
22
23new String:g_sql_droptable_players[] =
24 "DROP TABLE IF EXISTS 'players'; VACUUM;";
25
26new String:g_sql_playercount[] =
27 "SELECT * FROM players";
28
29new String:g_sql_addheadshots[] =
30 "ALTER TABLE players ADD COLUMN headshots int(12) NOT NULL default 0";
31
32new String:g_sql_addtimestamp[] =
33 "ALTER TABLE players ADD COLUMN last_connect timestamp DEFAULT NULL;";
34
35new String:g_name[MAXPLAYERS+1][MAX_NAME_LENGTH];
36new String:g_steamid[MAXPLAYERS+1][MAX_STEAMID_LENGTH];
37
38new g_start_points = 1000;
39
40new g_max_difference = 1000;
41new g_min_points = 2;
42new g_max_points = 40;
43
44#define IDENT_SIZE 16
45new String:g_ident[IDENT_SIZE];
46
47#define DBTYPE_MYSQL 1
48#define DBTYPE_SQLITE 2
49new g_dbtype;
50
51new g_kills[MAXPLAYERS+1];
52new g_deaths[MAXPLAYERS+1];
53new g_shots[MAXPLAYERS+1];
54new g_hits[MAXPLAYERS+1];
55new g_score[MAXPLAYERS+1];
56new g_time_joined[MAXPLAYERS+1];
57new g_time_played[MAXPLAYERS+1];
58new g_last_saved_time[MAXPLAYERS+1];
59new g_headshots[MAXPLAYERS+1];
60
61new g_session_kills[MAXPLAYERS+1];
62new g_session_deaths[MAXPLAYERS+1];
63new g_session_shots[MAXPLAYERS+1];
64new g_session_hits[MAXPLAYERS+1];
65new g_session_score[MAXPLAYERS+1];
66new g_session_headshots[MAXPLAYERS+1];
67
68new bool:g_initialized[MAXPLAYERS+1];
69
70new g_player_count;
71new g_gameid;
72
73#define DISPLAYMODE_PUBLIC 0
74#define DISPLAYMODE_PRIVATE 1
75#define DISPLAYMODE_CHAT 2
76new g_displaymode = DISPLAYMODE_PUBLIC;
77
78new Handle:g_henabled;
79new Handle:g_hversion;
80new Handle:g_hstartpoints;
81new Handle:g_hdisplaymode;
82new g_enabled;
83
84new Handle:g_hmax_difference;
85new Handle:g_hmin_points;
86new Handle:g_hmax_points;
87
88#include "sodstats\natives.sp"
89#include "sodstats\css.sp"
90#include "sodstats\tf2.sp"
91#include "sodstats\dod.sp"
92#include "sodstats\empires.sp"
93#include "sodstats\defaultgame.sp"
94
95#include "sodstats\commands\rank.sp"
96#include "sodstats\commands\session.sp"
97#include "sodstats\commands\statsme.sp"
98#include "sodstats\commands\top.sp"
99
100public Plugin:myinfo =
101{
102 name = "SoDStats",
103 author = "]SoD[ Frostbyte",
104 description = "A simple stats and ranking system.",
105 version = SODSTATS_VERSION,
106 url = "http://www.sonsofdavid.net"
107}
108
109public OnPluginStart()
110{
111 decl String:error[256];
112 stats_db = SQL_Connect("storage-local", false, error, sizeof(error));
113
114 if(stats_db == INVALID_HANDLE)
115 {
116 LogError("[SoD-Rank] Unable to connect to database (%s)", error);
117 return;
118 }
119
120 SQL_ReadDriver(stats_db, g_ident, IDENT_SIZE);
121 if(strcmp(g_ident, "mysql", false) == 0)
122 {
123 g_dbtype = DBTYPE_MYSQL;
124 }
125 else if(strcmp(g_ident, "sqlite", false) == 0)
126 {
127 g_dbtype = DBTYPE_SQLITE;
128 }
129 else
130 {
131 LogError("[SoD-Rank] Invalid DB-Type");
132 return;
133 }
134
135 SQL_LockDatabase(stats_db);
136
137 if((g_dbtype == DBTYPE_MYSQL && !SQL_FastQuery(stats_db, g_mysql_createtable_players)) ||
138 (g_dbtype == DBTYPE_SQLITE && !SQL_FastQuery(stats_db, g_sqlite_createtable_players)))
139 {
140 LogError("[SoD-Rank] Could not create players table.");
141 return;
142 }
143
144 if(!SQL_FastQuery(stats_db, g_sql_addheadshots))
145 {
146 //LogError("[SoD-Rank] Could not add headshots column.");
147 //return;
148 }
149
150 if(!SQL_FastQuery(stats_db, g_sql_addtimestamp))
151 {
152 //LogError("[SoD-Rank] Could not add headshots column.");
153 //return;
154 }
155
156 g_player_count = GetPlayerCount();
157 SQL_UnlockDatabase(stats_db);
158
159 g_gameid = GetGameId();
160
161 if(!HookEvents(g_gameid))
162 {
163 LogError("[SoD-Rank] Unable to hook events.");
164 return;
165 }
166
167 g_henabled = CreateConVar("sm_stats_enabled", "1", "Sets whether or not to record stats",FCVAR_NOTIFY, true, 0.0, true, 1.0);
168 g_hstartpoints = CreateConVar("sm_stats_startpoints", "1000", "Sets the starting points for a new player",FCVAR_NOTIFY, true, 0.0, true, 10000.0);
169 g_hdisplaymode = CreateConVar("sm_stats_displaymode", "0", "Sets the stats output mode. (Public = 0, Private = 1, Chat = 2)",FCVAR_NOTIFY, true, 0.0, true, 2.0);
170 g_hversion = CreateConVar("sm_stats_version", SODSTATS_VERSION,"SoD Stats version.", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
171
172 HookConVarChange(g_henabled, EnabledCallback);
173 HookConVarChange(g_hstartpoints, StartPointsCallback);
174 HookConVarChange(g_hdisplaymode, DisplayModeCallback);
175 g_enabled = GetConVarInt(g_henabled);
176 g_start_points = GetConVarInt(g_hstartpoints);
177
178
179 // ***********************************************************
180 // CVars für die Punkteberechnung:
181 // sm_stats_maxdiff
182 // sm_stats_minpoints
183 // sm_stats_maxpoints
184 // ***********************************************************
185 g_hmax_difference = CreateConVar("sm_stats_maxdiff", "1000",
186 "Sets the maximum difference of points at which you gain max points");
187 g_hmin_points = CreateConVar("sm_stats_minpoints", "2",
188 "Sets the minimum points you gain per kill");
189 g_hmax_points = CreateConVar("sm_stats_maxpoints", "40",
190 "Sets the maximum points you gain per kill");
191 HookConVarChange(g_hmax_difference, MaxDifferenceCallback);
192 HookConVarChange(g_hmin_points, MinPointsCallback);
193 HookConVarChange(g_hmax_points, MaxPointsCallback);
194 // ***********************************************************
195
196 if(g_henabled == INVALID_HANDLE || g_hversion == INVALID_HANDLE)
197 {
198 LogError("[SoD-Rank] Could not create stats_enabled cvar.");
199 return;
200 }
201
202 RegAdminCmd("sm_stats_reset", AdminCmd_ResetStats, ADMFLAG_CONFIG, "Resets player stats.");
203 RegAdminCmd("sm_stats_purge", AdminCmd_Purge, ADMFLAG_CONFIG, "sm_stats_purge [days] - Purge players who haven't connected for [days] days.");
204 RegConsoleCmd("say", ConCmd_Say);
205 RegConsoleCmd("say_team", ConCmd_Say);
206
207 RegPluginLibrary("sodstats");
208}
209
210public OnClientDisconnect(userid)
211{
212 // Ignore bot disconnects
213 if(g_initialized[userid] == true)
214 {
215 // Save the player stats
216 SavePlayer(userid);
217 // and uninitialize them
218 g_initialized[userid] = false;
219 }
220}
221
222public OnClientAuthorized(client, const String:steamid[])
223{
224 //new client = GetClientOfUserId(userid);
225 // Don't load bot stats or initialize them
226 if(!IsFakeClient(client))
227 {
228 Format(g_steamid[client], MAX_STEAMID_LENGTH, steamid);
229 GetClientName(client, g_name[client], MAX_NAME_LENGTH);
230 GetPlayerBySteamId(steamid, LoadPlayerCallback, client);
231 }
232}
233
234public EnabledCallback(Handle:convar, const String:oldValue[], const String:newValue[])
235{
236 if(strcmp(newValue, "0") == 0)
237 g_enabled = 0;
238 else
239 g_enabled = 1;
240}
241
242public StartPointsCallback(Handle:convar, const String:oldValue[], const String:newValue[])
243{
244 g_start_points = StringToInt(newValue);
245}
246
247public DisplayModeCallback(Handle:convar, const String:oldValue[], const String:newValue[])
248{
249 g_displaymode = StringToInt(newValue);
250}
251
252// Die drei Callback Funktionen die die Änderungen der CVars für die Punkteberechnung abfangen
253public MaxDifferenceCallback(Handle:convar, const String:oldValue[], const String:newValue[])
254{
255 g_max_difference = StringToInt(newValue);
256}
257public MinPointsCallback(Handle:convar, const, String:oldValue[], const String:newValue[])
258{
259 g_min_points = StringToInt(newValue);
260}
261public MaxPointsCallback(Handle:convar, const, String:oldValue[], const String:newValue[])
262{
263 g_max_points = StringToInt(newValue);
264}
265
266public Action:ConCmd_Say(userid, args)
267{
268 if(!userid || g_enabled == 0)
269 return Plugin_Continue;
270
271 decl String:text[192]; // from rockthevote.sp
272 if(!GetCmdArgString(text, sizeof(text)))
273 return Plugin_Continue;
274
275 new startidx = 0;
276
277 // Strip quotes from argument
278 if(text[strlen(text)-1] == '"')
279 {
280 text[strlen(text)-1] = '\0';
281 startidx = 1;
282 }
283
284 if(strcmp(text[startidx], "rank", false) == 0)
285 {
286 PrintRankToAll(userid);
287 }
288 else if(strcmp(text[startidx], "top", false) == 0 ||
289 strcmp(text[startidx], "top10", false) == 0)
290 {
291 PrintTop(userid);
292 }
293
294 if(g_gameid != ID_EMPIRES)
295 {
296 if(strcmp(text[startidx], "statsme", false) == 0)
297 {
298 PrintStats(userid);
299 }
300 else if(strcmp(text[startidx], "session", false) == 0)
301 {
302 PrintSession(userid);
303 }
304 }
305
306 return Plugin_Continue;
307}
308
309public LoadPlayerCallback(const String:name[], const String:steamid[], any:stats[], any:data, error)
310{
311 new client = data;
312
313 g_session_deaths[client] = 0;
314 g_session_kills[client] = 0;
315 g_session_hits[client] = 0;
316 g_session_shots[client] = 0;
317 g_session_score[client] = 0;
318 g_session_headshots[client] = 0;
319
320 g_time_joined[client] = GetTime();
321 g_last_saved_time[client] = g_time_joined[client];
322
323 if(error == ERROR_PLAYER_NOT_FOUND)
324 {
325 CreatePlayer(client, g_steamid[client]);
326 return;
327 }
328
329 Format(g_name[client], MAX_NAME_LENGTH, name);
330 g_kills[client] = stats[STAT_KILLS];
331 g_deaths[client] = stats[STAT_DEATHS];
332 g_shots[client] = stats[STAT_SHOTS];
333 g_hits[client] = stats[STAT_HITS];
334 g_score[client] = stats[STAT_SCORE];
335 g_time_played[client] = stats[STAT_TIME_PLAYED];
336 g_headshots[client] = stats[STAT_HEADSHOTS];
337
338 g_initialized[client] = true;
339}
340
341public Action:AdminCmd_ResetStats(client, args)
342{
343 ResetStats();
344
345 return Plugin_Handled;
346}
347
348public Action:AdminCmd_Purge(client, args)
349{
350 new argCount = GetCmdArgs();
351
352 if(argCount != 1)
353 {
354 PrintToConsole(client, "SoD-Stats: Invalid number of arguments for command 'sm_stats_purge'");
355 return Plugin_Handled;
356 }
357
358 decl String:svDays[192];
359 if(!GetCmdArg(1, svDays, 192))
360 {
361 PrintToConsole(client, "SoD-Stats: Invalid arguments for sm_stats_purge.");
362 return Plugin_Handled;
363 }
364
365 new days = StringToInt(svDays);
366 if(days <= 0)
367 {
368 PrintToConsole(client, "SoD-Stats: Invalid number of days.");
369 return Plugin_Handled;
370 }
371
372 decl String:query[128];
373
374
375 switch(g_dbtype)
376 {
377 case DBTYPE_MYSQL:
378 Format(query, 128, "DELETE FROM players WHERE last_connect < current_timestamp - interval %i day;", days);
379 case DBTYPE_SQLITE:
380 Format(query, 128, "DELETE FROM players WHERE last_connect < datetime('now', '-%i days');", days);
381 }
382
383 SQL_TQuery(stats_db, SQL_PurgeCallback, query, client);
384
385 return Plugin_Handled;
386}
387
388public SQL_PurgeCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
389{
390 if(hndl == INVALID_HANDLE)
391 {
392 LogError("SQL_PurgeCallback: Invalid query (%s).", error);
393 }
394 else
395 {
396 PrintToConsole(data, "SoD-Stats: Purge successful");
397 }
398}
399
400GetPlayerCount()
401{
402 new Handle:hquery = SQL_Query(stats_db, g_sql_playercount);
403 if(hquery == INVALID_HANDLE)
404 {
405 LogError("[SoD-Stats] Error getting player count.");
406 return 0;
407 }
408 new rows = SQL_GetRowCount(hquery);
409 CloseHandle(hquery);
410
411 return rows;
412}
413
414GetGameId()
415{
416 new String:fldr[64];
417
418 GetGameFolderName(fldr, sizeof(fldr));
419
420 if(strcmp(fldr, "tf") == 0)
421 {
422 return ID_TF2;
423 }
424 else if(strcmp(fldr, "cstrike") == 0)
425 {
426 return ID_CSS;
427 }
428 else if(strcmp(fldr, "dod") == 0)
429 {
430 return ID_DODS;
431 }
432 else if(strcmp(fldr, "FortressForever") == 0)
433 {
434 return ID_FORTRESSFOREVER;
435 }
436 else if(strcmp(fldr, DIR_EMPIRES) == 0)
437 {
438 return ID_EMPIRES;
439 }
440 return ID_DEFAULTGAME;
441}
442
443bool:HookEvents(gameid)
444{
445 switch(gameid)
446 {
447 case ID_CSS:
448 HookEventsCSS();
449 case ID_TF2:
450 HookEventsTF2();
451 case ID_DODS:
452 HookEventsDOD();
453 case ID_FORTRESSFOREVER:
454 HookEventsTF2();
455 case ID_EMPIRES:
456 HookEventsEmpires();
457 case ID_DEFAULTGAME:
458 HookEventsDefault();
459 default:
460 {
461 LogError("[SoD-Rank] Invalid gameid (%i).", g_gameid);
462 return false;
463 }
464 }
465 return true;
466}
467
468SavePlayer(const userid)
469{
470 if(stats_db == INVALID_HANDLE || g_enabled == 0)
471 return false;
472
473 new String:name[256];
474 GetClientName(userid, name, sizeof(name));
475
476 // Make SQL-safe
477 ReplaceString(name, sizeof(name), "'", "");
478
479 // save player here
480 decl String:query[255];
481 new time = GetTime();
482 Format(query, sizeof(query), g_sql_saveplayer, g_score[userid],
483 g_kills[userid],
484 g_deaths[userid],
485 g_shots[userid],
486 g_hits[userid],
487 name,
488 time - g_last_saved_time[userid],
489 g_headshots[userid],
490 g_steamid[userid]);
491 g_last_saved_time[userid] = time;
492 SQL_TQuery(stats_db, SQL_SavePlayerCallback, query);
493 return 0;
494}
495
496public SQL_SavePlayerCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
497{
498 if(hndl == INVALID_HANDLE)
499 LogError("[SoD-Rank] Error saving player (%s)", error);
500}
501
502CreatePlayer(const userid, const String:steamid[])
503{
504 decl String:query[255];
505 new String:name[MAX_NAME_LENGTH];
506 new String:safe_name[MAX_NAME_LENGTH];
507
508 GetClientName(userid, name, sizeof(name));
509 SQL_QuoteString(stats_db, name, safe_name, sizeof(safe_name));
510 Format(query, sizeof(query), g_sql_createplayer, steamid, safe_name);
511
512 SQL_TQuery(stats_db, SQL_CreatePlayerCallback, query, userid);
513}
514
515public SQL_CreatePlayerCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
516{
517 new client = data;
518
519 if(hndl != INVALID_HANDLE)
520 {
521 if(IsClientConnected(client)) // fix
522 GetClientName(client, g_name[client], MAX_NAME_LENGTH);
523
524 g_kills[client] = 0;
525 g_deaths[client] = 0;
526 g_shots[client] = 0;
527 g_hits[client] = 0;
528 g_score[client] = 0;
529 g_time_played[client] = 0;
530 g_headshots[client] = 0;
531
532 g_session_deaths[client] = 0;
533 g_session_kills[client] = 0;
534 g_session_hits[client] = 0;
535 g_session_shots[client] = 0;
536 g_session_score[client] = 0;
537 g_session_headshots[client] = 0;
538
539 g_time_joined[client] = GetTime();
540 g_initialized[client] = true;
541 g_player_count++;
542 }
543 else
544 LogError("[SoD-Stats] SQL_CreatePlayerCallback failure: %s", error);
545}