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