· 5 years ago · Feb 19, 2020, 12:18 AM
1/*-- Pragmas --*/
2#pragma semicolon 1
3#pragma newdecls required
4
5/*-- Defines --*/
6#define OVERLAY_RAMPAGE 1
7#define OVERLAY_RANKUP 2
8#define OVERLAY_TIERUP 3
9
10#define MAX_QUERY_SIZE 8192
11#define MAX_TABLE_SIZE 64
12#define WEAPON_STATISTICS_SIZE 4096
13
14/*-- Includes --*/
15#include <sourcemod>
16#include <sourcemod-misc>
17#include <cstrike>
18#include <sdktools>
19#include <colorlib>
20#include <autoexecconfig>
21#include <json>
22
23/*-- Furious Includes --*/
24#include <furious/furious-statistics>
25#include <furious/furious-vip>
26
27#undef REQUIRE_PLUGIN
28#include <furious/furious-store>
29#include <furious/furious-tags>
30#define REQUIRE_PLUGIN
31
32#define TOP_RANKS_NUMBER 10
33
34ConVar convar_TopRankIconIndex[TOP_RANKS_NUMBER];
35int g_TopRankIconIndex[TOP_RANKS_NUMBER];
36int g_PersonalDataPublicLevelOffset = -1;
37
38/*-- ConVars --*/
39ConVar convar_Status;
40ConVar convar_Config_Ranks;
41ConVar convar_Config_Tiers;
42ConVar convar_Table_GlobalData;
43ConVar convar_Table_GlobalStatistics;
44ConVar convar_Table_GlobalMapStatistics;
45ConVar convar_Table_ServerSeasons;
46ConVar convar_Table_ServerMaps;
47ConVar convar_Table_ServerSessions;
48ConVar convar_MonthsPerSeason;
49ConVar convar_RoundClampStatistics;
50ConVar convar_MinimumPlayersStatistics;
51ConVar convar_NoBotsStatistics;
52ConVar convar_Rampage_Overlay;
53ConVar convar_Rampage_Points;
54ConVar convar_SaveOnRoundEnd;
55ConVar convar_SaveOnPlayerDeath;
56ConVar convar_Sound_Rampage;
57ConVar convar_Sound_Connect;
58ConVar convar_Sound_Reconnect;
59ConVar convar_Sound_RankEnabled;
60ConVar convar_Sound_RankDisabled;
61ConVar convar_RankEnabled_Status;
62ConVar convar_RankEnabled_Channel;
63ConVar convar_RankEnabled_Coordinate_X;
64ConVar convar_RankEnabled_Coordinate_Y;
65ConVar convar_RankEnabled_Color;
66ConVar convar_RankDisabled_Status;
67ConVar convar_RankDisabled_Channel;
68ConVar convar_RankDisabled_Coordinate_X;
69ConVar convar_RankDisabled_Coordinate_Y;
70ConVar convar_RankDisabled_Color;
71ConVar convar_ResetRankCredits;
72
73/*-- Forwards --*/
74Handle g_Forward_OnGlobalsValidated;
75
76/*-- Globals --*/
77Database g_Database_Global;
78Database g_Database_Server;
79
80bool plugin_store;
81bool plugin_tags;
82
83char g_sCurrentMap[MAX_NAME_LENGTH];
84bool g_bLate;
85bool g_bBetweenRounds;
86bool g_bRanked;
87ArrayList g_WeaponsList;
88
89float g_fStartTime;
90int g_iCachedPlayers;
91ArrayList g_MapCount;
92
93StringMap g_RanksData;
94ArrayList g_RanksList;
95StringMap g_TiersData;
96ArrayList g_TiersList;
97
98bool g_bActiveSeason;
99int g_iSeason;
100int g_iNextSeason;
101ArrayList g_Seasons;
102
103int g_iCooldown[MAXPLAYERS + 1];
104Handle g_hTimer_Playtime[MAXPLAYERS + 1];
105Menu g_StatisticsMenu[MAXPLAYERS + 1];
106bool g_bToggleStatistics[MAXPLAYERS + 1];
107int g_iLocalKillstreak[MAXPLAYERS + 1];
108bool g_bRampage[MAXPLAYERS + 1];
109
110StringMap g_SessionCache;
111ArrayList g_SessionIDs;
112
113bool g_bIsLoaded[MAXPLAYERS + 1] = {false, ...};
114bool g_bSpecHud[MAXPLAYERS + 1] = {true, ...};
115bool g_HudSkipClient[MAXPLAYERS + 1] = {true, ...};
116Handle g_SpecTimer[MAXPLAYERS + 1];
117
118float g_Cache_GlobalPlaytime[MAXPLAYERS + 1];
119float g_Cache_ServerPlaytime[MAXPLAYERS + 1];
120int g_Cache_Playtimes[MAXPLAYERS + 1];
121
122//* Global Stats *//
123int g_iStatistics_Global_Kills[MAXPLAYERS + 1];
124int g_iStatistics_Global_Deaths[MAXPLAYERS + 1];
125int g_iStatistics_Global_Assists[MAXPLAYERS + 1];
126int g_iStatistics_Global_Headshots[MAXPLAYERS + 1];
127int g_iStatistics_Global_Points[MAXPLAYERS + 1];
128int g_iStatistics_Global_Longest_Killstreak[MAXPLAYERS + 1];
129int g_iStatistics_Global_Hits[MAXPLAYERS + 1];
130int g_iStatistics_Global_Shots[MAXPLAYERS + 1];
131float g_fStatistics_Global_KDR[MAXPLAYERS + 1];
132float g_fStatistics_Global_Accuracy[MAXPLAYERS + 1];
133float g_fStatistics_Global_Playtime[MAXPLAYERS + 1];
134int g_iStatistics_Global_FirstCreated[MAXPLAYERS + 1];
135int g_iStatistics_Global_LastUpdated[MAXPLAYERS + 1];
136
137//* Seasonal Statistics *//
138int g_iStatistics_Seasonal_Kills[MAXPLAYERS + 1];
139int g_iStatistics_Seasonal_Deaths[MAXPLAYERS + 1];
140int g_iStatistics_Seasonal_Assists[MAXPLAYERS + 1];
141int g_iStatistics_Seasonal_Headshots[MAXPLAYERS + 1];
142int g_iStatistics_Seasonal_Points[MAXPLAYERS + 1];
143int g_iStatistics_Seasonal_Longest_Killstreak[MAXPLAYERS + 1];
144int g_iStatistics_Seasonal_Hits[MAXPLAYERS + 1];
145int g_iStatistics_Seasonal_Shots[MAXPLAYERS + 1];
146float g_fStatistics_Seasonal_KDR[MAXPLAYERS + 1];
147float g_fStatistics_Seasonal_Accuracy[MAXPLAYERS + 1];
148float g_fStatistics_Seasonal_Playtime[MAXPLAYERS + 1];
149JSON_Object g_hStatistics_Seasonal_WeaponsStatistics[MAXPLAYERS + 1];
150
151//* Map Statistics *//
152int g_iStatistics_Mapbased_Kills[MAXPLAYERS + 1];
153int g_iStatistics_Mapbased_Deaths[MAXPLAYERS + 1];
154int g_iStatistics_Mapbased_Assists[MAXPLAYERS + 1];
155int g_iStatistics_Mapbased_Headshots[MAXPLAYERS + 1];
156int g_iStatistics_Mapbased_Points[MAXPLAYERS + 1];
157int g_iStatistics_Mapbased_Longest_Killstreak[MAXPLAYERS + 1];
158int g_iStatistics_Mapbased_Hits[MAXPLAYERS + 1];
159int g_iStatistics_Mapbased_Shots[MAXPLAYERS + 1];
160float g_fStatistics_Mapbased_KDR[MAXPLAYERS + 1];
161float g_fStatistics_Mapbased_Accuracy[MAXPLAYERS + 1];
162float g_fStatistics_Mapbased_Playtime[MAXPLAYERS + 1];
163
164//* Cached Data *//
165int g_iCacheData_Rank[MAXPLAYERS + 1];
166int g_iCacheData_Tier[MAXPLAYERS + 1];
167int g_iCacheData_PointsGain[MAXPLAYERS + 1];
168int g_iCacheData_PointsLoss[MAXPLAYERS + 1];
169char g_sCacheData_TierTag[MAXPLAYERS + 1][512];
170
171//* Session Statistics *//
172int g_iStatistics_Sessionbased_Kills[MAXPLAYERS + 1];
173int g_iStatistics_Sessionbased_Deaths[MAXPLAYERS + 1];
174int g_iStatistics_Sessionbased_Assists[MAXPLAYERS + 1];
175int g_iStatistics_Sessionbased_Headshots[MAXPLAYERS + 1];
176int g_iStatistics_Sessionbased_Hits[MAXPLAYERS + 1];
177int g_iStatistics_Sessionbased_Shots[MAXPLAYERS + 1];
178float g_fStatistics_Sessionbased_KDR[MAXPLAYERS + 1];
179float g_fStatistics_Sessionbased_Accuracy[MAXPLAYERS + 1];
180int g_iStatistics_Sessionbased_PointsGained[MAXPLAYERS + 1];
181int g_iStatistics_Sessionbased_PointsLost[MAXPLAYERS + 1];
182int g_iStatistics_Sessionbased_RanksGained[MAXPLAYERS + 1];
183int g_iStatistics_Sessionbased_RanksLost[MAXPLAYERS + 1];
184
185/*enum struct Statistics
186{
187 int kills;
188 int deaths;
189 int assists;
190 int headshots;
191 int points;
192 int longest_killstreak;
193 int hits;
194 int shots;
195 float kdr;
196 float accuracy;
197 float playtime;
198 int first_created;
199 int last_updated;
200
201 //maps
202 JSON_Object weapon_stats;
203
204 //sessions
205 int points_gained;
206 int points_lost;
207 int ranks_gained;
208 int ranks_lost;
209
210 void Init()
211 {
212 this.Reset();
213 this.weapon_stats = new JSON_Object();
214 }
215
216 void Reset()
217 {
218 this.kills = 0;
219 this.deaths = 0;
220 this.assists = 0;
221 this.headshots = 0;
222 this.points = 0;
223 this.longest_killstreak = 0;
224 this.hits = 0;
225 this.shots = 0;
226 this.kdr = 0.0;
227 this.accuracy = 0.0;
228 this.playtime = 0.0;
229 this.first_created = 0;
230 this.last_updated = 0;
231
232 if (this.weapon_stats != null)
233 this.weapon_stats.Cleanup();
234 delete this.weapon_stats;
235
236 this.points_gained = 0;
237 this.points_lost = 0;
238 this.ranks_gained = 0;
239 this.ranks_lost = 0;
240 }
241}
242Statistics g_Stats_Global[MAXPLAYERS + 1];
243Statistics g_Stats_Map[MAXPLAYERS + 1];
244Statistics g_Stats_Session[MAXPLAYERS + 1];*/
245
246/*-- Plugin Info --*/
247public Plugin myinfo =
248{
249 name = "[Furious] Player Statistics",
250 author = "Drixevel",
251 description = "Player Statistics module for Furious Clan.",
252 version = "1.1.9",
253 url = "http://furious-clan.com/"
254};
255
256public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
257{
258 MarkNativeAsOptional("Furious_Store_AddCredits");
259 MarkNativeAsOptional("Furious_Tags_GetPrefixID");
260 MarkNativeAsOptional("Furious_Tags_GetHudPrefix");
261 MarkNativeAsOptional("Furious_Tags_GetHudPrefixColor");
262 MarkNativeAsOptional("Furious_Tags_GetHudGroup");
263
264 RegPluginLibrary("furious_statistics");
265
266 CreateNative("Furious_Statistics_GetTierTag", Native_Server_GetTierTag);
267 CreateNative("Furious_Statistics_GetPlaytime", Native_Server_GetPlaytime);
268 CreateNative("Furious_Statistics_SetSpecHud", Native_SetSpecHud);
269
270 g_Forward_OnGlobalsValidated = CreateGlobalForward("Furious_Statistics_OnGlobalValidated", ET_Ignore, Param_Cell, Param_Cell);
271
272 g_bLate = late;
273 return APLRes_Success;
274}
275
276public void OnPluginStart()
277{
278 LoadTranslations("common.phrases");
279 LoadTranslations("furious.phrases");
280
281 AutoExecConfig_SetFile("frs.statistics");
282 convar_Status = AutoExecConfig_CreateConVar("sm_furious_statistics_status", "1", "Status of the plugin.\n(1 = on, 0 = off)", FCVAR_NOTIFY, true, 0.0, true, 1.0);
283 convar_Config_Ranks = AutoExecConfig_CreateConVar("sm_furious_statistics_config_ranks", "configs/furious/furious_ranks.cfg", "Name of the ranks config to use.", FCVAR_NOTIFY);
284 convar_Config_Tiers = AutoExecConfig_CreateConVar("sm_furious_statistics_config_tiers", "configs/furious/furious_tiers.cfg", "Name of the tiers config to use.", FCVAR_NOTIFY);
285 convar_Table_GlobalData = AutoExecConfig_CreateConVar("sm_furious_statistics_table_server_data", "furious_global_server_data", "Name of the database table to use in side the global database for server settings.", FCVAR_NOTIFY);
286 convar_Table_GlobalStatistics = AutoExecConfig_CreateConVar("sm_furious_statistics_global_table", "furious_global_statistics", "Name of the global statistics table under the global database.", FCVAR_NOTIFY);
287 convar_Table_GlobalMapStatistics = AutoExecConfig_CreateConVar("sm_furious_statistics_global_maps_table", "furious_global_map_statistics", "Name of the database table to save statistics to.", FCVAR_NOTIFY);
288 convar_Table_ServerSeasons = AutoExecConfig_CreateConVar("sm_furious_statistics_table_season", "furious_server_season_", "Name of the seasons database table prefix to use. (Season is concatenate on the end)", FCVAR_NOTIFY);
289 convar_Table_ServerMaps = AutoExecConfig_CreateConVar("sm_furious_statistics_table_map", "furious_server_maps_", "Name of the maps database table prefix to use. (Season is concatenate on the end)", FCVAR_NOTIFY);
290 convar_Table_ServerSessions = AutoExecConfig_CreateConVar("sm_furious_statistics_table_sessions", "furious_server_sessions_", "Name of the sessions database table prefix to use. (Season is concatenate on the end)", FCVAR_NOTIFY);
291 convar_MonthsPerSeason = AutoExecConfig_CreateConVar("sm_furious_statistics_season_time", "3", "Amount of months per season.", FCVAR_NOTIFY, true, 1.0);
292 convar_RoundClampStatistics = AutoExecConfig_CreateConVar("sm_furious_statistics_round_clamp", "1", "ONLY gather statistics while rounds are active.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
293 convar_MinimumPlayersStatistics = AutoExecConfig_CreateConVar("sm_furious_statistics_minimum_players", "1", "Required amount of players minimum for statistics to be recorded.", FCVAR_NOTIFY, true, 1.0);
294 convar_NoBotsStatistics = AutoExecConfig_CreateConVar("sm_furious_statistics_nobots", "0", "If bots are allowed to affect stats for players or not.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
295 convar_Rampage_Overlay = AutoExecConfig_CreateConVar("sm_furious_statistics_rampage_overlay", "overlays/frs-rampage", "Overlay to show for rampage mode.", FCVAR_NOTIFY);
296 convar_Rampage_Points = AutoExecConfig_CreateConVar("sm_furious_statistics_rampage_points", "1", "Extra points to give players during rampage mode.", FCVAR_NOTIFY, true, 0.0);
297 convar_SaveOnRoundEnd = AutoExecConfig_CreateConVar("sm_furious_statistics_save_round_end", "1", "Save statistics on round end.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
298 convar_SaveOnPlayerDeath = AutoExecConfig_CreateConVar("sm_furious_statistics_save_player_death", "1", "Save statistics on player death.", FCVAR_NOTIFY, true, 0.0, true, 1.0);
299 convar_Sound_Rampage = AutoExecConfig_CreateConVar("sm_furious_statistics_rampage_sound", "buttons/light_power_on_switch_01.wav", "Sound to play in rampage mode.", FCVAR_NOTIFY);
300 convar_Sound_Connect = AutoExecConfig_CreateConVar("sm_furious_statistics_sound_connect", "", "Sound to play on first time connect.", FCVAR_NOTIFY);
301 convar_Sound_Reconnect = AutoExecConfig_CreateConVar("sm_furious_statistics_sound_reconnect", "", "Sound to play on reconnect.", FCVAR_NOTIFY);
302 convar_Sound_RankEnabled = AutoExecConfig_CreateConVar("sm_furious_statistics_sound_ranking_enabled", "", "Sound to play on ranking enabled.", FCVAR_NOTIFY);
303 convar_Sound_RankDisabled = AutoExecConfig_CreateConVar("sm_furious_statistics_sound_ranking_disabled", "", "Sound to play on ranking disabled.", FCVAR_NOTIFY);
304 convar_RankEnabled_Status = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankenabled_status", "1", "Whether to show this hud sync or not.", FCVAR_NOTIFY);
305 convar_RankEnabled_Channel = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankenabled_channel", "0", "Which channel to use.", FCVAR_NOTIFY, true, 0.0, true, 6.0);
306 convar_RankEnabled_Coordinate_X = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankenabled_x", "-1.0", "X coordinate for this hud message.", FCVAR_NOTIFY);
307 convar_RankEnabled_Coordinate_Y = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankenabled_y", "-1.0", "Y coordinate for this hud message.", FCVAR_NOTIFY);
308 convar_RankEnabled_Color = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankenabled_color", "255 255 255", "Color for this hud message.", FCVAR_NOTIFY);
309 convar_RankDisabled_Status = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankdisabled_status", "1", "Whether to show this hud sync or not.", FCVAR_NOTIFY);
310 convar_RankDisabled_Channel = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankdisabled_channel", "0", "Which channel to use.", FCVAR_NOTIFY, true, 0.0, true, 6.0);
311 convar_RankDisabled_Coordinate_X = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankdisabled_x", "-1.0", "X coordinate for this hud message.", FCVAR_NOTIFY);
312 convar_RankDisabled_Coordinate_Y = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankdisabled_y", "-1.0", "Y coordinate for this hud message.", FCVAR_NOTIFY);
313 convar_RankDisabled_Color = AutoExecConfig_CreateConVar("sm_furious_statistics_hud_rankdisabled_color", "255 255 255", "Color for this hud message.", FCVAR_NOTIFY);
314 convar_ResetRankCredits = AutoExecConfig_CreateConVar("sm_furious_statistics_resetrank_credits", "1000", "Credits to deduct from clients that want to reset their rank.", FCVAR_NOTIFY, true, 0.0);
315
316 static char buffer1[64], buffer2[3], buffer3[128];
317
318 for( int j = 1; j <= TOP_RANKS_NUMBER; j++ )
319 {
320 Format(buffer1, sizeof(buffer1), "sm_furious_statistics_toprank%i_icon_id", j);
321 Format(buffer3, sizeof(buffer3), "Rank %i icon index as in levelNUM.png", j);
322
323 IntToString(j, buffer2, sizeof(buffer2));
324
325 convar_TopRankIconIndex[j - 1] = AutoExecConfig_CreateConVar(buffer1, buffer2, buffer3, FCVAR_NOTIFY, true, 0.0);
326 }
327
328 AutoExecConfig_ExecuteFile();
329
330 convar_MinimumPlayersStatistics.AddChangeHook(ConVarChanged_MinimumPlayers);
331
332 HookEvent("player_hurt", OnPlayerHurt);
333 HookEvent("player_death", OnPlayerDeath);
334 HookEvent("weapon_fire", OnWeaponFire);
335 HookEvent("player_disconnect", OnPlayerDisconnect);
336 HookEvent("round_freeze_end", OnRoundFreezeEnd);
337 HookEvent("round_end", OnRoundEnd);
338 HookEvent("cs_win_panel_match", OnMatchEnd);
339
340 RegConsoleCmd("sm_statsme", Command_OpenStatisticsMenu, "Opens the statistics menu.");
341 RegConsoleCmd("sm_stats", Command_OpenStatisticsMenu, "Opens the statistics menu.");
342 RegConsoleCmd("sm_session", Command_OpenStatisticsMenu, "Opens the sessions menu.");
343 RegConsoleCmd("sm_top", Command_OpenTopRanksMenu, "Opens top ranks menu.");
344 RegConsoleCmd("sm_timeplayed", Command_TimePlayed, "Displays how much time you've spent on the server.");
345 RegConsoleCmd("sm_played", Command_TimePlayed, "Displays how much time you've spent on the server.");
346 RegConsoleCmd("sm_playtime", Command_TimePlayed, "Displays how much time you've spent on the server.");
347 RegConsoleCmd("sm_weaponme", Command_OpenWeaponsMenu, "Opens weapons menu.");
348 RegConsoleCmd("sm_spec", Command_SwitchToSpectate, "Puts you into spectator.");
349 RegConsoleCmd("sm_spectate", Command_SwitchToSpectate, "Puts you into spectator.");
350 RegConsoleCmd("sm_afk", Command_SwitchToSpectate, "Puts you into spectator.");
351 RegConsoleCmd("sm_brb", Command_SwitchToSpectate, "Puts you into spectator.");
352 RegConsoleCmd("sm_place", Command_PrintRankInfo, "Prints rank information to all for a player.");
353 RegConsoleCmd("sm_rank", Command_PrintRankInfo, "Prints rank information to yourself for a player.");
354 RegConsoleCmd("sm_next", Command_Next, "Shows the top 10 clients above your rank.");
355 RegConsoleCmd("sm_tiers", Command_PrintTiers, "Print the list of tiers in chat.");
356 RegConsoleCmd("sm_ranks", Command_PrintTiers, "Print the list of tiers in chat.");
357 RegConsoleCmd("sm_status", Command_Status, "Display the amount of players in the database.");
358 RegConsoleCmd("sm_resetrank", Command_ResetRank, "Reset your seasonal rank on this server.");
359 RegConsoleCmd("sm_season", Command_Season, "Displays the current season and the next season in chat.");
360
361 RegAdminCmd("sm_playtimedebug", Command_PlaytimeDebug, ADMFLAG_ROOT, "Debugger for testing client playtime.");
362 RegAdminCmd("sm_savestats", Command_SaveStats, ADMFLAG_ROOT, "Save your own statistics manually.");
363 RegAdminCmd("sm_toggleranked", Command_ToggleRanked, ADMFLAG_ROOT, "Toggle on/off the statistics tracking.");
364 RegAdminCmd("sm_overlays", Command_TestOverlays, ADMFLAG_ROOT, "Test and debug overlays in the plugin.");
365 RegAdminCmd("sm_testoverlays", Command_TestOverlays, ADMFLAG_ROOT, "Test and debug overlays in the plugin.");
366 RegAdminCmd("sm_reloadranks", Command_ReloadRanks, ADMFLAG_ROOT, "Reloads ranks data.");
367 RegAdminCmd("sm_reloadtiers", Command_ReloadTiers, ADMFLAG_ROOT, "Reloads tiers data.");
368 RegAdminCmd("sm_testspechud", Command_TestSpecHud, ReadFlagString("z"), "Test the spectator hud.");
369
370 g_WeaponsList = new ArrayList(ByteCountToCells(MAX_NAME_LENGTH));
371
372 g_RanksData = new StringMap();
373 g_RanksList = new ArrayList();
374 g_TiersData = new StringMap();
375 g_TiersList = new ArrayList();
376
377 g_MapCount = new ArrayList();
378
379 g_SessionCache = new StringMap();
380 g_SessionIDs = new ArrayList();
381
382 g_Seasons = new ArrayList(ByteCountToCells(64));
383 g_Seasons.PushString("03-20");
384 g_Seasons.PushString("06-07");
385 g_Seasons.PushString("06-21");
386 g_Seasons.PushString("09-22");
387 g_Seasons.PushString("12-21");
388
389 TriggerTimer(CreateTimer(120.0, Timer_DisplayNextSeason, _, TIMER_REPEAT), true);
390
391 AutoExecConfig_CleanFile();
392
393 g_PersonalDataPublicLevelOffset = FindSendPropInfo("CCSPlayerResource", "m_nPersonaDataPublicLevel");
394}
395
396public void OnAllPluginsLoaded()
397{
398 plugin_store = LibraryExists("furious_store");
399 plugin_tags = LibraryExists("furious_tags");
400}
401
402public void OnLibraryAdded(const char[] name)
403{
404 if (StrEqual(name, "furious_store", false))
405 plugin_store = true;
406
407 if (StrEqual(name, "furious_tags", false))
408 plugin_tags = true;
409}
410
411public void OnLibraryRemoved(const char[] name)
412{
413 if (StrEqual(name, "furious_store", false))
414 plugin_store = false;
415
416 if (StrEqual(name, "furious_tags", false))
417 plugin_tags = false;
418}
419
420public Action Timer_DisplayNextSeason(Handle timer)
421{
422 if (!convar_Status.BoolValue)
423 return Plugin_Continue;
424
425 char sTime[128];
426 FormatTime(sTime, sizeof(sTime), "%A, %B %d, %Y at %R", g_iNextSeason);
427
428 CPrintToChatAll("%t", "next season timer print", sTime);
429
430 return Plugin_Continue;
431}
432
433public void ConVarChanged_MinimumPlayers(ConVar convar, const char[] oldValue, const char[] newValue)
434{
435 if (StrEqual(oldValue, newValue))
436 return;
437
438 ValidateRankCheck();
439}
440
441public void OnMapEnd()
442{
443 if (!convar_Status.BoolValue)
444 return;
445
446 char sQuery[MAX_QUERY_SIZE];
447 char sTable[MAX_TABLE_SIZE];
448
449 convar_Table_GlobalMapStatistics.GetString(sTable, sizeof(sTable));
450
451 float fTime = GetEngineTime();
452 fTime -= g_fStartTime;
453
454 if (g_Database_Global != null)
455 {
456 g_Database_Global.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `map_playtime` = `map_playtime` + '%f', `last_updated` = '%i' WHERE `map` = '%s';", sTable, fTime, GetTime(), g_sCurrentMap);
457 g_Database_Global.Query(OnUpdateMapData, sQuery);
458 }
459
460 g_fStartTime = 0.0;
461
462 Transaction trans = new Transaction();
463
464 if (g_MapCount.Length > 0)
465 {
466 GetTableString_Maps(sTable, sizeof(sTable));
467
468 for (int i = 0; i < g_MapCount.Length; i++)
469 {
470 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `playcount` = `playcount` + 1 WHERE `accountid` = '%i' AND `map` = '%s';", sTable, g_MapCount.Get(i), g_sCurrentMap);
471 trans.AddQuery(sQuery);
472 }
473
474 g_MapCount.Clear();
475 }
476
477 if (g_SessionIDs.Length > 0)
478 {
479 GetTableString_Sessions(sTable, sizeof(sTable));
480
481 int iAccountID; char sAccountID[64]; StringMap trie;
482 char sName[MAX_NAME_LENGTH]; char sSteamID2[64]; char sSteamID3[64]; char sSteamID64[64]; int iLocalRanksGained; int iLocalRanksLost; int iLocalPointsGained; int iLocalPointsLost;
483
484 for (int i = 0; i < g_SessionIDs.Length; i++)
485 {
486 iAccountID = g_SessionIDs.Get(i);
487
488 IntToString(iAccountID, sAccountID, sizeof(sAccountID));
489
490 if (g_SessionCache.GetValue(sAccountID, trie) && trie != null)
491 {
492 trie.GetString("Name", sName, sizeof(sName));
493 trie.GetString("SteamID2", sSteamID2, sizeof(sSteamID2));
494 trie.GetString("SteamID3", sSteamID3, sizeof(sSteamID3));
495 trie.GetString("SteamID64", sSteamID64, sizeof(sSteamID64));
496 trie.GetValue("RanksGained", iLocalRanksGained);
497 trie.GetValue("RanksLost", iLocalRanksLost);
498 trie.GetValue("PointsGained", iLocalPointsGained);
499 trie.GetValue("PointsLost", iLocalPointsLost);
500
501 g_Database_Server.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`name`, `accountid`, `steamid2`, `steamid3`, `steamid64`, `ranks_gained`, `ranks_lost`, `points_gained`, `points_lost`, `map`, `first_created`) VALUES ('%s', '%i', '%s', '%s', '%s', '%i', '%i', '%i', '%i', '%s', '%i');", sTable, sName, iAccountID, sSteamID2, sSteamID3, sSteamID64, iLocalRanksGained, iLocalRanksLost, iLocalPointsGained, iLocalPointsLost, g_sCurrentMap, GetTime());
502 trans.AddQuery(sQuery);
503
504 delete trie;
505 g_SessionCache.Remove(sAccountID);
506 }
507 }
508
509 g_SessionCache.Clear();
510 g_SessionIDs.Clear();
511 }
512
513 g_Database_Server.Execute(trans, onSuccess_Sessions, onError_Sessions);
514}
515
516public void OnUpdateMapData(Database db, DBResultSet results, const char[] error, any data)
517{
518 if (results == null)
519 ThrowError("Error while updating map data: %s", error);
520}
521
522public void onSuccess_Sessions(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData)
523{
524 LogMessage("Sessions data has been uploaded.");
525}
526
527public void onError_Sessions(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData)
528{
529 ThrowError("Error saving sessions data at query %i: %s", failIndex, error);
530}
531
532public void OnMapStart()
533{
534 char sBuffer[PLATFORM_MAX_PATH];
535
536 convar_Sound_Rampage.GetString(sBuffer, sizeof(sBuffer));
537 if (strlen(sBuffer) > 0)
538 {
539 if (!IsSoundPrecached(sBuffer))
540 PrecacheSound(sBuffer);
541
542 Format(sBuffer, sizeof(sBuffer), "sound/%s", sBuffer);
543 AddFileToDownloadsTable(sBuffer);
544 }
545
546 convar_Sound_Connect.GetString(sBuffer, sizeof(sBuffer));
547 if (strlen(sBuffer) > 0)
548 {
549 if (!IsSoundPrecached(sBuffer))
550 PrecacheSound(sBuffer);
551
552 Format(sBuffer, sizeof(sBuffer), "sound/%s", sBuffer);
553 AddFileToDownloadsTable(sBuffer);
554 }
555
556 convar_Sound_Reconnect.GetString(sBuffer, sizeof(sBuffer));
557 if (strlen(sBuffer) > 0)
558 {
559 if (!IsSoundPrecached(sBuffer))
560 PrecacheSound(sBuffer);
561
562 Format(sBuffer, sizeof(sBuffer), "sound/%s", sBuffer);
563 AddFileToDownloadsTable(sBuffer);
564 }
565
566 convar_Sound_RankEnabled.GetString(sBuffer, sizeof(sBuffer));
567 if (strlen(sBuffer) > 0)
568 {
569 if (!IsSoundPrecached(sBuffer))
570 PrecacheSound(sBuffer);
571
572 Format(sBuffer, sizeof(sBuffer), "sound/%s", sBuffer);
573 AddFileToDownloadsTable(sBuffer);
574 }
575
576 convar_Sound_RankDisabled.GetString(sBuffer, sizeof(sBuffer));
577 if (strlen(sBuffer) > 0)
578 {
579 if (!IsSoundPrecached(sBuffer))
580 PrecacheSound(sBuffer);
581
582 Format(sBuffer, sizeof(sBuffer), "sound/%s", sBuffer);
583 AddFileToDownloadsTable(sBuffer);
584 }
585
586 ParseMapData();
587 ParseWeaponsList();
588 ParseRanksConfig();
589 ParseTiersConfig();
590
591 int resourceEnt = GetPlayerResourceEntity();
592
593 if( resourceEnt != -1)
594 {
595 for( int j = 0; j < TOP_RANKS_NUMBER; j++ )
596 {
597 Format(sBuffer, sizeof(sBuffer), "materials/panorama/images/icons/xp/level%i.png", convar_TopRankIconIndex[j].IntValue);
598 AddFileToDownloadsTable(sBuffer);
599 }
600
601 SDKHook(resourceEnt, SDKHook_ThinkPost, OnClientResourceEntityPostThink);
602 }
603 else
604 {
605 LogError("Cannot find the resource entity.");
606 }
607}
608
609void ParseMapData()
610{
611 GetCurrentMap(g_sCurrentMap, sizeof(g_sCurrentMap));
612 g_fStartTime = GetEngineTime();
613
614 if (g_Database_Global != null)
615 {
616 char sTable[MAX_TABLE_SIZE];
617 convar_Table_GlobalMapStatistics.GetString(sTable, sizeof(sTable));
618
619 int time = GetTime();
620
621 char sQuery[MAX_QUERY_SIZE];
622 g_Database_Global.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`map`, `map_loads`, `first_created`, `last_updated`) VALUES ('%s', '1', '%i', '%i') ON DUPLICATE KEY UPDATE `map_loads` = `map_loads` + '1', `last_updated` = '%i';", sTable, g_sCurrentMap, time, time, time);
623 g_Database_Global.Query(TQuery_InsertNewMap, sQuery);
624 }
625}
626
627public void TQuery_InsertNewMap(Database db, DBResultSet results, const char[] error, any data)
628{
629 if (results == null)
630 ThrowError("Error while updating map: %s", error);
631}
632
633public void OnConfigsExecuted()
634{
635 if (!convar_Status.BoolValue)
636 return;
637
638 if (g_Database_Global == null)
639 Database.Connect(OnSQLConnect_Global, "furious_global");
640
641 if (g_Database_Server == null)
642 Database.Connect(OnSQLConnect_Server, "furious_server");
643
644 if (g_bLate)
645 ValidateRankCheck();
646
647 for( int j = 0; j < TOP_RANKS_NUMBER; j++ )
648 {
649 g_TopRankIconIndex[j] = convar_TopRankIconIndex[j].IntValue;
650 }
651}
652
653public void OnSQLConnect_Global(Database db, const char[] error, any data)
654{
655 if (db == null)
656 ThrowError("Error connecting to the global database: %s", error);
657
658 if (g_Database_Global != null)
659 {
660 delete db;
661 return;
662 }
663
664 g_Database_Global = db;
665 LogMessage("Connected to global database successfully.");
666
667 g_Database_Global.SetCharset("utf8mb4");
668
669 char sTable[MAX_TABLE_SIZE];
670 char sQuery[MAX_QUERY_SIZE];
671
672 convar_Table_GlobalData.GetString(sTable, sizeof(sTable));
673 g_Database_Global.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` ( `id` int(12) NOT NULL AUTO_INCREMENT, `hostname` varchar(128) NOT NULL DEFAULT '', `ip` varchar(64) NOT NULL DEFAULT '', `season_number` int(12) NOT NULL DEFAULT 0, `next_season` int(12) NOT NULL DEFAULT 0, `first_created` int(12) NOT NULL, `last_updated` int(12) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `ip` (`ip`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;", sTable);
674 g_Database_Global.Query(TQuery_OnTableCreation_ServerData, sQuery);
675
676 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
677 g_Database_Global.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` ( `id` int(12) NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL DEFAULT '', `accountid` int(32) NOT NULL DEFAULT 0, `steamid2` varchar(64) NOT NULL DEFAULT '', `steamid3` varchar(64) NOT NULL DEFAULT '', `steamid64` varchar(64) NOT NULL DEFAULT '', `ip` varchar(64) NOT NULL DEFAULT '', `clan_tag` varchar(32) NOT NULL DEFAULT '', `clan_name` varchar(32) NOT NULL DEFAULT '', `credits` int(12) NOT NULL DEFAULT 0, `credits_earned` int(12) NOT NULL DEFAULT 0, `credits_timer` FLOAT NOT NULL DEFAULT '0.0' , `kills` int(12) NOT NULL DEFAULT 0, `deaths` int(12) NOT NULL DEFAULT 0, `assists` int(12) NOT NULL DEFAULT 0, `headshots` int(12) NOT NULL DEFAULT 0, `points` int(12) NOT NULL DEFAULT 0, `longest_killstreak` int(12) NOT NULL DEFAULT 0, `hits` int(12) NOT NULL DEFAULT 0, `shots` int(12) NOT NULL DEFAULT 0, `kdr` float NOT NULL DEFAULT 0.0, `accuracy` float NOT NULL DEFAULT 0.0, `playtime` float NOT NULL DEFAULT 0.0, `converted` int(12) NOT NULL DEFAULT 0, `first_created` int(12) NOT NULL, `last_updated` int(12) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `steamid2` (`steamid2`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;", sTable);
678 g_Database_Global.Query(TQuery_OnTableCreation_Statistics, sQuery);
679
680 convar_Table_GlobalMapStatistics.GetString(sTable, sizeof(sTable));
681 g_Database_Global.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` ( `id` int(12) NOT NULL AUTO_INCREMENT, `map` varchar(32) NOT NULL DEFAULT '', `map_loads` int(12) NOT NULL DEFAULT 0, `map_playtime` float NOT NULL DEFAULT 0.0, `first_created` int(12) NOT NULL, `last_updated` int(12) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `map` (`map`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;", sTable);
682 g_Database_Global.Query(TQuery_OnTableCreation_MapStatistics, sQuery);
683}
684
685public void TQuery_OnTableCreation_ServerData(Database db, DBResultSet results, const char[] error, any data)
686{
687 if (results == null)
688 ThrowError("Error creating server_data table: %s", error);
689
690 char sIP[128];
691 GetServerIP(sIP, sizeof(sIP), true);
692
693 char sTable[MAX_TABLE_SIZE];
694 convar_Table_GlobalData.GetString(sTable, sizeof(sTable));
695
696 char sQuery[MAX_QUERY_SIZE];
697 g_Database_Global.Format(sQuery, sizeof(sQuery), "SELECT `season_number`, `next_season` FROM `%s` WHERE `ip` = '%s';", sTable, sIP);
698 g_Database_Global.Query(TQuery_OnQueryServerSettings, sQuery);
699
700 ParseMapData();
701}
702
703public void TQuery_OnTableCreation_Statistics(Database db, DBResultSet results, const char[] error, any data)
704{
705 if (results == null)
706 ThrowError("Error creating statistics table: %s", error);
707}
708
709public void TQuery_OnTableCreation_MapStatistics(Database db, DBResultSet results, const char[] error, any data)
710{
711 if (results == null)
712 ThrowError("Error creating map statistics table: %s", error);
713}
714
715public void OnSQLConnect_Server(Database db, const char[] error, any data)
716{
717 if (db == null)
718 ThrowError("Error connecting to the server database: %s", error);
719
720 if (g_Database_Server != null)
721 {
722 delete db;
723 return;
724 }
725
726 g_Database_Server = db;
727 LogMessage("Connected to server database successfully.");
728
729 g_Database_Server.SetCharset("utf8mb4");
730}
731
732bool ParseWeaponsList()
733{
734 char sPath[PLATFORM_MAX_PATH];
735 FormatEx(sPath, sizeof(sPath), "scripts/items/items_game.txt");
736
737 KeyValues kv = new KeyValues("items_game");
738
739 if (kv.ImportFromFile(sPath) && kv.JumpToKey("items") && kv.GotoFirstSubKey())
740 {
741 g_WeaponsList.Clear();
742
743 char sWeapon[MAX_NAME_LENGTH];
744 do
745 {
746 kv.GetString("name", sWeapon, sizeof(sWeapon));
747
748 if (StrContains(sWeapon, "weapon_") == 0 && g_WeaponsList.FindString(sWeapon) == -1)
749 g_WeaponsList.PushString(sWeapon);
750 }
751 while (kv.GotoNextKey());
752 }
753 else
754 {
755 LogError("Error parsing items list from items_game.txt.");
756 delete kv;
757 return false;
758 }
759
760 delete kv;
761 LogMessage("Successfully parsed items_game.txt for %i weapons.", g_WeaponsList.Length);
762 return true;
763}
764
765public void TQuery_OnQueryServerSettings(Database db, DBResultSet results, const char[] error, any data)
766{
767 if (db == null)
768 ThrowError("Error querying server settings from database: %s", error);
769
770 char sCurrent[64];
771 FormatTime(sCurrent, sizeof(sCurrent), "%m-%d");
772
773 if (results.FetchRow())
774 {
775 g_iSeason = results.FetchInt(0);
776 g_iNextSeason = results.FetchInt(1);
777
778 if (g_Seasons.FindString(sCurrent) != -1)
779 {
780 g_iSeason++;
781 g_iNextSeason = GetTime() + convar_MonthsPerSeason.IntValue * 2592000;
782 }
783 }
784 else
785 {
786 g_iSeason = 1;
787 g_iNextSeason = GetTime() + convar_MonthsPerSeason.IntValue * 2592000;
788 }
789
790 if (g_iSeason == 0)
791 ThrowError("Error while initializing season: Invalid season of 0");
792
793 char sTable[MAX_TABLE_SIZE];
794 convar_Table_GlobalData.GetString(sTable, sizeof(sTable));
795
796 char sHostname[MAX_NAME_LENGTH];
797 FindConVar("hostname").GetString(sHostname, sizeof(sHostname));
798
799 char sIP[64];
800 GetServerIP(sIP, sizeof(sIP), true);
801
802 int iTime = GetTime();
803
804 char sQuery[MAX_QUERY_SIZE];
805 g_Database_Global.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`hostname`, `ip`, `season_number`, `next_season`, `first_created`, `last_updated`) VALUES ('%s', '%s', '%i', '%i', '%i', '%i') ON DUPLICATE KEY UPDATE `hostname` = '%s', `season_number` = '%i', `next_season` = '%i', `last_updated` = '%i';", sTable, sHostname, sIP, g_iSeason, g_iNextSeason, iTime, iTime, sHostname, g_iSeason, g_iNextSeason, iTime);
806 g_Database_Global.Query(TQuery_OnUpdateServerSettings, sQuery);
807
808 Transaction trans = new Transaction();
809
810 GetTableString_Season(sTable, sizeof(sTable));
811 g_Database_Server.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` (`id` int(12) NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL DEFAULT '', `accountid` int(12) NOT NULL DEFAULT 0, `steamid2` varchar(64) NOT NULL DEFAULT '', `steamid3` varchar(64) NOT NULL DEFAULT '', `steamid64` varchar(64) NOT NULL DEFAULT '', `kills` int(12) NOT NULL DEFAULT 0, `deaths` int(12) NOT NULL DEFAULT 0, `assists` int(12) NOT NULL DEFAULT 0, `headshots` int(12) NOT NULL DEFAULT 0, `points` int(12) NOT NULL DEFAULT 0, `longest_killstreak` int(12) NOT NULL DEFAULT 0, `hits` int(12) NOT NULL DEFAULT 0, `shots` int(12) NOT NULL DEFAULT 0, `kdr` float NOT NULL DEFAULT 0.0, `accuracy` float NOT NULL DEFAULT 0.0, `playtime` float NOT NULL DEFAULT 0.0, `weapons_statistics` varchar(4096) NOT NULL DEFAULT '', `first_created` int(32) NOT NULL DEFAULT 0, `last_updated` int(32) NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `accountid` (`accountid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;", sTable);
812 trans.AddQuery(sQuery);
813
814 GetTableString_Maps(sTable, sizeof(sTable));
815 g_Database_Server.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` (`id` int(12) NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL DEFAULT '', `accountid` int(12) NOT NULL DEFAULT 0, `steamid2` varchar(64) NOT NULL DEFAULT '', `steamid3` varchar(64) NOT NULL DEFAULT '', `steamid64` varchar(64) NOT NULL DEFAULT '', `kills` int(12) NOT NULL DEFAULT 0, `deaths` int(12) NOT NULL DEFAULT 0, `assists` int(12) NOT NULL DEFAULT 0, `headshots` int(12) NOT NULL DEFAULT 0, `points` int(12) NOT NULL DEFAULT 0, `longest_killstreak` int(12) NOT NULL DEFAULT 0, `hits` int(12) NOT NULL DEFAULT 0, `shots` int(12) NOT NULL DEFAULT 0, `kdr` float NOT NULL DEFAULT 0.0, `accuracy` float NOT NULL DEFAULT 0.0, `playtime` float NOT NULL DEFAULT 0.0, `playcount` int(12) NOT NULL DEFAULT 0, `map` varchar(64) NOT NULL DEFAULT '', `first_created` int(32) NOT NULL, `last_updated` int(32) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`), UNIQUE KEY `map_ident` (`accountid`,`map`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;", sTable);
816 trans.AddQuery(sQuery);
817
818 GetTableString_Sessions(sTable, sizeof(sTable));
819 g_Database_Server.Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `%s` (`id` int(12) NOT NULL AUTO_INCREMENT, `name` varchar(64) NOT NULL DEFAULT '', `accountid` int(12) NOT NULL DEFAULT 0, `steamid2` varchar(64) NOT NULL DEFAULT '', `steamid3` varchar(64) NOT NULL DEFAULT '', `steamid64` varchar(64) NOT NULL DEFAULT '', `ranks_gained` int(12) NOT NULL DEFAULT 0, `ranks_lost` int(12) NOT NULL DEFAULT 0, `points_gained` int(12) NOT NULL DEFAULT 0, `points_lost` int(12) NOT NULL DEFAULT 0, `map` varchar(32) NOT NULL DEFAULT '', `first_created` int(12) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `id` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 DEFAULT COLLATE=utf8mb4_unicode_ci;", sTable);
820 trans.AddQuery(sQuery);
821
822 g_Database_Server.Execute(trans, Transaction_OnCreateTables_Success, Transaction_OnCreateTables_Failure);
823}
824
825public void TQuery_OnUpdateServerSettings(Database db, DBResultSet results, const char[] error, any data)
826{
827 if (results == null)
828 ThrowError("Error saving server settings to database: %s", error);
829}
830
831public void Transaction_OnCreateTables_Success(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData)
832{
833 char sTime[128];
834 FormatTime(sTime, sizeof(sTime), "%A, %B %d, %Y", g_iNextSeason);
835
836 LogMessage("Current Season: %i - Next Season Date: %s", g_iSeason, sTime);
837 g_bActiveSeason = true;
838
839 if (g_bLate)
840 {
841 char auth[64];
842 for (int i = 1; i <= MaxClients; i++)
843 if (IsClientConnected(i) && GetClientAuthId(i, AuthId_Steam2, auth, sizeof(auth)))
844 OnClientPostAdminCheck(i);
845
846 g_bLate = false;
847 }
848}
849
850public void Transaction_OnCreateTables_Failure(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData)
851{
852 g_bActiveSeason = false;
853 ThrowError("Error loading current season '%i': [%i] %s", g_iSeason, failIndex, error);
854}
855
856public void OnClientPostAdminCheck(int client)
857{
858 if (!convar_Status.BoolValue || IsFakeClient(client))
859 return;
860
861 g_iLocalKillstreak[client] = 0;
862 g_bToggleStatistics[client] = false;
863 g_bRampage[client] = false;
864
865 g_bIsLoaded[client] = false;
866 g_bSpecHud[client] = true;
867
868 g_Cache_GlobalPlaytime[client] = 0.0;
869 g_Cache_ServerPlaytime[client] = 0.0;
870 g_Cache_Playtimes[client] = 0;
871
872 g_iStatistics_Global_Kills[client] = 0;
873 g_iStatistics_Global_Deaths[client] = 0;
874 g_iStatistics_Global_Assists[client] = 0;
875 g_iStatistics_Global_Headshots[client] = 0;
876 g_iStatistics_Global_Points[client] = 0;
877 g_iStatistics_Global_Longest_Killstreak[client] = 0;
878 g_iStatistics_Global_Hits[client] = 0;
879 g_iStatistics_Global_Shots[client] = 0;
880 g_fStatistics_Global_KDR[client] = 0.0;
881 g_fStatistics_Global_Accuracy[client] = 0.0;
882 g_fStatistics_Global_Playtime[client] = 0.0;
883 g_iStatistics_Global_FirstCreated[client] = 0;
884 g_iStatistics_Global_LastUpdated[client] = 0;
885
886 g_iStatistics_Seasonal_Kills[client] = 0;
887 g_iStatistics_Seasonal_Deaths[client] = 0;
888 g_iStatistics_Seasonal_Assists[client] = 0;
889 g_iStatistics_Seasonal_Headshots[client] = 0;
890 g_iStatistics_Seasonal_Points[client] = 0;
891 g_iStatistics_Seasonal_Longest_Killstreak[client] = 0;
892 g_iStatistics_Seasonal_Hits[client] = 0;
893 g_iStatistics_Seasonal_Shots[client] = 0;
894 g_fStatistics_Seasonal_KDR[client] = 0.0;
895 g_fStatistics_Seasonal_Accuracy[client] = 0.0;
896 g_fStatistics_Seasonal_Playtime[client] = 0.0;
897
898 g_iStatistics_Mapbased_Kills[client] = 0;
899 g_iStatistics_Mapbased_Deaths[client] = 0;
900 g_iStatistics_Mapbased_Assists[client] = 0;
901 g_iStatistics_Mapbased_Headshots[client] = 0;
902 g_iStatistics_Mapbased_Points[client] = 0;
903 g_iStatistics_Mapbased_Longest_Killstreak[client] = 0;
904 g_iStatistics_Mapbased_Hits[client] = 0;
905 g_iStatistics_Mapbased_Shots[client] = 0;
906 g_fStatistics_Mapbased_KDR[client] = 0.0;
907 g_fStatistics_Mapbased_Accuracy[client] = 0.0;
908 g_fStatistics_Mapbased_Playtime[client] = 0.0;
909
910 g_iCacheData_Rank[client] = 0;
911 g_iCacheData_Tier[client] = 0;
912 g_iCacheData_PointsGain[client] = 0;
913 g_iCacheData_PointsLoss[client] = 0;
914 g_sCacheData_TierTag[client][0] = '\0';
915
916 g_iStatistics_Sessionbased_Kills[client] = 0;
917 g_iStatistics_Sessionbased_Deaths[client] = 0;
918 g_iStatistics_Sessionbased_Assists[client] = 0;
919 g_iStatistics_Sessionbased_Headshots[client] = 0;
920 g_iStatistics_Sessionbased_Hits[client] = 0;
921 g_iStatistics_Sessionbased_Shots[client] = 0;
922 g_fStatistics_Sessionbased_KDR[client] = 0.0;
923 g_fStatistics_Sessionbased_Accuracy[client] = 0.0;
924 g_iStatistics_Sessionbased_PointsGained[client] = 0;
925 g_iStatistics_Sessionbased_PointsLost[client] = 0;
926 g_iStatistics_Sessionbased_RanksGained[client] = 0;
927 g_iStatistics_Sessionbased_RanksLost[client] = 0;
928
929 int iAccountID = GetSteamAccountID(client);
930
931 char sAccountID[64];
932 IntToString(iAccountID, sAccountID, sizeof(sAccountID));
933
934 StringMap trie;
935 if (g_SessionCache.GetValue(sAccountID, trie) && trie != null)
936 {
937 trie.GetValue("Kills", g_iStatistics_Sessionbased_Kills[client]);
938 trie.GetValue("Deaths", g_iStatistics_Sessionbased_Deaths[client]);
939 trie.GetValue("Assists", g_iStatistics_Sessionbased_Assists[client]);
940 trie.GetValue("Headshots", g_iStatistics_Sessionbased_Headshots[client]);
941 trie.GetValue("Hits", g_iStatistics_Sessionbased_Hits[client]);
942 trie.GetValue("Shots", g_iStatistics_Sessionbased_Shots[client]);
943 trie.GetValue("KDR", g_fStatistics_Sessionbased_KDR[client]);
944 trie.GetValue("Accuracy", g_fStatistics_Sessionbased_Accuracy[client]);
945 trie.GetValue("PointsGained", g_iStatistics_Sessionbased_PointsGained[client]);
946 trie.GetValue("PointsLost", g_iStatistics_Sessionbased_PointsLost[client]);
947 trie.GetValue("RanksGained", g_iStatistics_Sessionbased_RanksGained[client]);
948 trie.GetValue("RanksLost", g_iStatistics_Sessionbased_RanksLost[client]);
949
950 delete trie;
951 g_SessionCache.Remove(sAccountID);
952
953 int index = g_SessionIDs.FindValue(iAccountID);
954 g_SessionIDs.Erase(index);
955 }
956
957 g_MapCount.Push(iAccountID);
958
959 SyncClientStatistics(client);
960 CreateTimer(7.0 + 0.025 * float(client), Timer_CheckLoading, client, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
961
962 if( !IsFakeClient(client) )
963 {
964 KillTimerSafe(g_SpecTimer[client]);
965 g_SpecTimer[client] = CreateTimer(1.0, Timer_DisplaySpectatorHud, GetClientUserId(client), TIMER_REPEAT);
966 }
967}
968
969public Action Timer_CheckLoading(Handle timer, any client)
970{
971 if( g_bIsLoaded[client] )
972 {
973 return Plugin_Stop;
974 }
975
976 char name[MAX_NAME_LENGTH];
977 GetClientName(client, name, sizeof(name));
978 LogMessage("Loading failed for %s, retrying again....", name);
979
980 SyncClientStatistics(client);
981 return Plugin_Continue;
982}
983
984void SyncClientStatistics(int client)
985{
986 char sTable[MAX_TABLE_SIZE];
987 char sQuery[MAX_QUERY_SIZE];
988
989 char sSteamID2[64];
990 if (!GetClientAuthId(client, AuthId_Steam2, sSteamID2, sizeof(sSteamID2)))
991 ThrowError("Error while pulling all client statistics: Not Connected to Steam.");
992
993 int iSteamID = GetSteamAccountID(client);
994 int userid = GetClientUserId(client);
995
996 if (g_hStatistics_Seasonal_WeaponsStatistics[client] != null)
997 {
998 g_hStatistics_Seasonal_WeaponsStatistics[client].Cleanup();
999 delete g_hStatistics_Seasonal_WeaponsStatistics[client];
1000 }
1001
1002 g_hStatistics_Seasonal_WeaponsStatistics[client] = new JSON_Object();
1003
1004 if (g_Database_Global != null)
1005 {
1006 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
1007
1008 g_Database_Global.Format(sQuery, sizeof(sQuery), "SELECT `kills`, `deaths`, `assists`, `headshots`, `points`, `longest_killstreak`, `hits`, `shots`, `kdr`, `accuracy`, `playtime`, `first_created`, `last_updated` FROM `%s` WHERE `steamid2` = '%s';", sTable, sSteamID2);
1009 g_Database_Global.Query(TQuery_PullClientGlobalData, sQuery, userid);
1010 }
1011
1012 if (g_bActiveSeason && g_Database_Server != null)
1013 {
1014 GetTableString_Season(sTable, sizeof(sTable));
1015
1016 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT `kills`, `deaths`, `assists`, `headshots`, `points`, `longest_killstreak`, `hits`, `shots`, `kdr`, `accuracy`, `playtime`, `weapons_statistics` FROM `%s` WHERE `accountid` = '%i';", sTable, iSteamID);
1017 g_Database_Server.Query(TQuery_PullClientSeasonData, sQuery, userid);
1018 }
1019}
1020
1021public void TQuery_PullClientGlobalData(Database db, DBResultSet results, const char[] error, any data)
1022{
1023 if (results == null)
1024 ThrowError("Error pulling client global statistics: %s", error);
1025
1026 int client;
1027 if ((client = GetClientOfUserId(data)) == 0)
1028 return;
1029
1030 if (results.FetchRow())
1031 {
1032 g_iStatistics_Global_Kills[client] = results.FetchInt(0);
1033 g_iStatistics_Global_Deaths[client] = results.FetchInt(1);
1034 g_iStatistics_Global_Assists[client] = results.FetchInt(2);
1035 g_iStatistics_Global_Headshots[client] = results.FetchInt(3);
1036 g_iStatistics_Global_Points[client] = results.FetchInt(4);
1037 g_iStatistics_Global_Longest_Killstreak[client] = results.FetchInt(5);
1038 g_iStatistics_Global_Hits[client] = results.FetchInt(6);
1039 g_iStatistics_Global_Shots[client] = results.FetchInt(7);
1040 g_fStatistics_Global_KDR[client] = results.FetchFloat(8);
1041 g_fStatistics_Global_Accuracy[client] = results.FetchFloat(9);
1042 g_fStatistics_Global_Playtime[client] = results.FetchFloat(10);
1043 g_iStatistics_Global_FirstCreated[client] = results.FetchInt(11);
1044 g_iStatistics_Global_LastUpdated[client] = results.FetchInt(12);
1045
1046 Call_StartForward(g_Forward_OnGlobalsValidated);
1047 Call_PushCell(client);
1048 Call_PushCell(false);
1049 Call_Finish();
1050 }
1051 else
1052 {
1053 char name[MAX_NAME_LENGTH];
1054 GetClientName(client, name, sizeof(name));
1055 LogMessage("%s - cannot fetch global statistics.", name);
1056 }
1057
1058 CreateTimer(5.0, results.RowCount > 0 ? Timer_DelayWelcomeBack : Timer_DelayWelcome, data, TIMER_FLAG_NO_MAPCHANGE);
1059 ValidateClientGlobalData(client);
1060}
1061
1062void ValidateClientGlobalData(int client)
1063{
1064 char sTable[MAX_TABLE_SIZE];
1065 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
1066
1067 char sName0[MAX_NAME_LENGTH], sName[1 + MAX_NAME_LENGTH * 2];
1068 GetClientName(client, sName0, sizeof(sName0));
1069 g_Database_Global.Escape(sName0, sName, sizeof(sName));
1070
1071 char sSteamID2[64];
1072 if (!GetClientAuthId(client, AuthId_Steam2, sSteamID2, sizeof(sSteamID2)))
1073 {
1074 LogError("Error while verifying client Steam2: Steam is not connected.");
1075 sSteamID2[0] = '\0';
1076 }
1077
1078 char sSteamID3[64];
1079 if (!GetClientAuthId(client, AuthId_Steam3, sSteamID3, sizeof(sSteamID3)))
1080 {
1081 LogError("Error while verifying client Steam3: Steam is not connected.");
1082 sSteamID3[0] = '\0';
1083 }
1084
1085 char sSteamID64[64];
1086 if (!GetClientAuthId(client, AuthId_SteamID64, sSteamID64, sizeof(sSteamID64)))
1087 {
1088 LogError("Error while verifying client Steam64: Steam is not connected.");
1089 sSteamID64[0] = '\0';
1090 }
1091
1092 char sIP[64];
1093 GetClientIP(client, sIP, sizeof(sIP));
1094
1095 int iTime = GetTime();
1096
1097 int iAccountID = GetSteamAccountID(client);
1098
1099 char sQuery[MAX_QUERY_SIZE];
1100 g_Database_Global.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`name`, `accountid`, `steamid2`, `steamid3`, `steamid64`, `ip`, `clan_tag`, `clan_name`, `credits`, `first_created`, `last_updated`) VALUES ('%s', '%i', '%s', '%s', '%s', '%s', '', '', '0', '%i', '%i') ON DUPLICATE KEY UPDATE `name` = '%s', `accountid` = '%i', `steamid3` = '%s', `steamid64` = '%s', `ip` = '%s', `last_updated` = '%i';", sTable, sName, iAccountID, sSteamID2, sSteamID3, sSteamID64, sIP, iTime, iTime, sName, iAccountID, sSteamID3, sSteamID64, sIP, iTime);
1101 g_Database_Global.Query(TQuery_OnGlobalUpdate, sQuery, GetClientUserId(client));
1102}
1103
1104public void TQuery_OnGlobalUpdate(Database db, DBResultSet results, const char[] error, any data)
1105{
1106 if (results == null)
1107 ThrowError("Error on updating client global data or creating it: %s", error);
1108
1109 int client;
1110 if ((client = GetClientOfUserId(data)) > 0 && results.AffectedRows == 1)
1111 {
1112 int time = GetTime();
1113 g_iStatistics_Global_FirstCreated[client] = time;
1114 g_iStatistics_Global_LastUpdated[client] = time;
1115
1116 Call_StartForward(g_Forward_OnGlobalsValidated);
1117 Call_PushCell(client);
1118 Call_PushCell(true);
1119 Call_Finish();
1120 }
1121}
1122
1123public Action Timer_DelayWelcomeBack(Handle timer, any data)
1124{
1125 int client;
1126 if ((client = GetClientOfUserId(data)) > 0 && IsClientInGame(client))
1127 {
1128 CPrintToChat(client, "%T", "join advert", client, client);
1129
1130 char sSound[PLATFORM_MAX_PATH];
1131 convar_Sound_Reconnect.GetString(sSound, sizeof(sSound));
1132
1133 if (strlen(sSound) > 0 && IsSoundPrecached(sSound))
1134 EmitSoundToAll(sSound);
1135 }
1136}
1137
1138public Action Timer_DelayWelcome(Handle timer, any data)
1139{
1140 int client;
1141 if ((client = GetClientOfUserId(data)) > 0 && IsClientInGame(client))
1142 {
1143 CPrintToChat(client, "%T", "join advert new player", client, client);
1144
1145 char sSound[PLATFORM_MAX_PATH];
1146 convar_Sound_Connect.GetString(sSound, sizeof(sSound));
1147
1148 if (strlen(sSound) > 0 && IsSoundPrecached(sSound))
1149 EmitSoundToAll(sSound);
1150 }
1151}
1152
1153public void TQuery_PullClientSeasonData(Database db, DBResultSet results, const char[] error, any data)
1154{
1155 if (results == null)
1156 ThrowError("Error pulling client season statistics: %s", error);
1157
1158 int client;
1159 if ((client = GetClientOfUserId(data)) == 0)
1160 return;
1161
1162 if (results.FetchRow())
1163 {
1164 int iSteamID = GetSteamAccountID(client);
1165
1166 g_iStatistics_Seasonal_Kills[client] = results.FetchInt(0);
1167 g_iStatistics_Seasonal_Deaths[client] = results.FetchInt(1);
1168 g_iStatistics_Seasonal_Assists[client] = results.FetchInt(2);
1169 g_iStatistics_Seasonal_Headshots[client] = results.FetchInt(3);
1170 g_iStatistics_Seasonal_Points[client] = results.FetchInt(4);
1171 g_iStatistics_Seasonal_Longest_Killstreak[client] = results.FetchInt(5);
1172 g_iStatistics_Seasonal_Hits[client] = results.FetchInt(6);
1173 g_iStatistics_Seasonal_Shots[client] = results.FetchInt(7);
1174 g_fStatistics_Seasonal_KDR[client] = results.FetchFloat(8);
1175 g_fStatistics_Seasonal_Accuracy[client] = results.FetchFloat(9);
1176 g_fStatistics_Seasonal_Playtime[client] = results.FetchFloat(10);
1177
1178 char sWeaponsData[WEAPON_STATISTICS_SIZE];
1179 results.FetchString(11, sWeaponsData, sizeof(sWeaponsData));
1180
1181 if (strlen(sWeaponsData) > 0)
1182 {
1183 if (g_hStatistics_Seasonal_WeaponsStatistics[client] != null)
1184 {
1185 g_hStatistics_Seasonal_WeaponsStatistics[client].Cleanup();
1186 delete g_hStatistics_Seasonal_WeaponsStatistics[client];
1187 }
1188
1189 g_hStatistics_Seasonal_WeaponsStatistics[client] = json_decode(sWeaponsData);
1190 }
1191
1192 StringMap overlay_data;
1193 g_iCacheData_Tier[client] = CalculateTier(g_iStatistics_Seasonal_Points[client], overlay_data);
1194
1195 if (overlay_data != null)
1196 {
1197 char sTag[128];
1198 overlay_data.GetString("tag", sTag, sizeof(sTag));
1199 strcopy(g_sCacheData_TierTag[client], 512, sTag);
1200
1201 char sPointsPerKill[PLATFORM_MAX_PATH];
1202 overlay_data.GetString("points_per_kill", sPointsPerKill, sizeof(sPointsPerKill));
1203
1204 char sPointsPerDeath[PLATFORM_MAX_PATH];
1205 overlay_data.GetString("points_per_death", sPointsPerDeath, sizeof(sPointsPerDeath));
1206
1207 g_iCacheData_PointsGain[client] = StringToInt(sPointsPerKill);
1208 g_iCacheData_PointsLoss[client] = StringToInt(sPointsPerDeath);
1209 }
1210
1211 char sTable[MAX_TABLE_SIZE];
1212 GetTableString_Season(sTable, sizeof(sTable));
1213
1214 char sQuery[MAX_QUERY_SIZE];
1215 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT s.points, (SELECT COUNT(*) as rank FROM `%s` as r WHERE r.points > s.points OR (r.points = s.points AND r.kills > s.kills)) + 1 as rank, (SELECT COUNT(*) as total FROM `%s`) as total FROM `%s` as s WHERE s.accountid = '%i';", sTable, sTable, sTable, iSteamID);
1216 g_Database_Server.Query(TQuery_PullClientSeasonData2, sQuery, data);
1217 }
1218 else
1219 {
1220 char name[MAX_NAME_LENGTH];
1221 GetClientName(client, name, sizeof(name));
1222 LogMessage("%s - cannot fetch seasonal statistics.", name);
1223 }
1224}
1225
1226public void TQuery_PullClientSeasonData2(Database db, DBResultSet results, const char[] error, any data)
1227{
1228 if (results == null)
1229 ThrowError("Error pulling client season statistics (2): %s", error);
1230
1231 int client;
1232 if ((client = GetClientOfUserId(data)) == 0)
1233 return;
1234
1235 if (results.FetchRow())
1236 {
1237 int iSteamID = GetSteamAccountID(client);
1238
1239 int iPoints = results.FetchInt(0);
1240 int iRank = results.FetchInt(1);
1241 g_iCachedPlayers = results.FetchInt(2);
1242
1243 g_iCacheData_Rank[client] = iRank;
1244
1245 CPrintToChatAll("%t", "join message", client, iRank, iPoints);
1246
1247 char sTable[MAX_TABLE_SIZE];
1248 GetTableString_Maps(sTable, sizeof(sTable));
1249
1250 char sQuery[MAX_QUERY_SIZE];
1251 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT `kills`, `deaths`, `assists`, headshots, points, longest_killstreak, hits, shots, kdr, accuracy, playtime FROM `%s` WHERE accountid = '%i' AND map = '%s';", sTable, iSteamID, g_sCurrentMap);
1252 g_Database_Server.Query(TQuery_PullClientMapBasedData, sQuery, data);
1253 }
1254 else
1255 {
1256 CPrintToChatAll("%t", "join message new player", client);
1257
1258 char name[MAX_NAME_LENGTH];
1259 GetClientName(client, name, sizeof(name));
1260 LogMessage("%s - cannot fetch seasonal statistics (2).", name);
1261 }
1262}
1263
1264public void TQuery_PullClientMapBasedData(Database db, DBResultSet results, const char[] error, any data)
1265{
1266 if (results == null)
1267 ThrowError("Error pulling client season statistics (2): %s", error);
1268
1269 int client;
1270 if ((client = GetClientOfUserId(data)) == 0)
1271 return;
1272
1273 if (results.FetchRow())
1274 {
1275 g_iStatistics_Mapbased_Kills[client] = results.FetchInt(0);
1276 g_iStatistics_Mapbased_Deaths[client] = results.FetchInt(1);
1277 g_iStatistics_Mapbased_Assists[client] = results.FetchInt(2);
1278 g_iStatistics_Mapbased_Headshots[client] = results.FetchInt(3);
1279 g_iStatistics_Mapbased_Points[client] = results.FetchInt(4);
1280 g_iStatistics_Mapbased_Longest_Killstreak[client] = results.FetchInt(5);
1281 g_iStatistics_Mapbased_Hits[client] = results.FetchInt(6);
1282 g_iStatistics_Mapbased_Shots[client] = results.FetchInt(7);
1283 g_fStatistics_Mapbased_KDR[client] = results.FetchFloat(8);
1284 g_fStatistics_Mapbased_Accuracy[client] = results.FetchFloat(9);
1285 g_fStatistics_Mapbased_Playtime[client] = results.FetchFloat(10);
1286
1287 g_bIsLoaded[client] = true;
1288 }
1289 else
1290 {
1291 char name[MAX_NAME_LENGTH];
1292 GetClientName(client, name, sizeof(name));
1293 LogMessage("%s - cannot fetch map-based statistics.", name);
1294 }
1295
1296 ValidateClientServerData(client);
1297}
1298
1299void ValidateClientServerData(int client)
1300{
1301 char sQuery[MAX_QUERY_SIZE];
1302 char sTable[MAX_TABLE_SIZE];
1303
1304 char sName0[MAX_NAME_LENGTH], sName[1 + MAX_NAME_LENGTH * 2];
1305 GetClientName(client, sName0, sizeof(sName0));
1306 g_Database_Server.Escape(sName0, sName, sizeof(sName));
1307
1308 int iAccountID = GetSteamAccountID(client);
1309
1310 char sSteamID2[64];
1311 if (!GetClientAuthId(client, AuthId_Steam2, sSteamID2, sizeof(sSteamID2)))
1312 {
1313 LogError("Error while verifying client Steam2: Steam is not connected.");
1314 sSteamID2[0] = '\0';
1315 }
1316
1317 char sSteamID3[64];
1318 if (!GetClientAuthId(client, AuthId_Steam3, sSteamID3, sizeof(sSteamID3)))
1319 {
1320 LogError("Error while verifying client Steam3: Steam is not connected.");
1321 sSteamID3[0] = '\0';
1322 }
1323
1324 char sSteamID64[64];
1325 if (!GetClientAuthId(client, AuthId_SteamID64, sSteamID64, sizeof(sSteamID64)))
1326 {
1327 LogError("Error while verifying client Steam64: Steam is not connected.");
1328 sSteamID64[0] = '\0';
1329 }
1330
1331 char sWeaponsData[WEAPON_STATISTICS_SIZE];
1332 g_hStatistics_Seasonal_WeaponsStatistics[client].Encode(sWeaponsData, sizeof(sWeaponsData));
1333
1334 int iTime = GetTime();
1335
1336 GetTableString_Season(sTable, sizeof(sTable));
1337
1338 g_Database_Server.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`name`, `accountid`, `steamid2`, `steamid3`, `steamid64`, `weapons_statistics`, `first_created`, `last_updated`) VALUES ('%s', '%i', '%s', '%s', '%s', '%s', '%i', '%i') ON DUPLICATE KEY UPDATE `name` = '%s', `last_updated` = '%i';", sTable, sName, iAccountID, sSteamID2, sSteamID3, sSteamID64, sWeaponsData, iTime, iTime, sName, iTime);
1339 g_Database_Server.Query(TQuery_SyncClient_Season, sQuery, GetClientUserId(client));
1340
1341 GetTableString_Maps(sTable, sizeof(sTable));
1342
1343 g_Database_Server.Format(sQuery, sizeof(sQuery), "INSERT INTO `%s` (`name`, `accountid`, `steamid2`, `steamid3`, `steamid64`, `map`, `first_created`, `last_updated`) VALUES ('%s', '%i', '%s', '%s', '%s', '%s', '%i', '%i') ON DUPLICATE KEY UPDATE `name` = '%s', `last_updated` = '%i';", sTable, sName, iAccountID, sSteamID2, sSteamID3, sSteamID64, g_sCurrentMap, iTime, iTime, sName, iTime);
1344 g_Database_Server.Query(TQuery_SyncClient_Map, sQuery, GetClientUserId(client));
1345}
1346
1347public void TQuery_SyncClient_Season(Database db, DBResultSet results, const char[] error, any data)
1348{
1349 if (results == null)
1350 ThrowError("Error updating client for the season: %s", error);
1351
1352 int client;
1353 if ((client = GetClientOfUserId(data)) > 0)
1354 {
1355 StringMap overlay_data;
1356 g_iCacheData_Tier[client] = CalculateTier(g_iStatistics_Seasonal_Points[client], overlay_data);
1357
1358 if (overlay_data != null)
1359 {
1360 char sTag[128];
1361 overlay_data.GetString("tag", sTag, sizeof(sTag));
1362 strcopy(g_sCacheData_TierTag[client], 512, sTag);
1363
1364 char sPointsPerKill[PLATFORM_MAX_PATH];
1365 overlay_data.GetString("points_per_kill", sPointsPerKill, sizeof(sPointsPerKill));
1366
1367 char sPointsPerDeath[PLATFORM_MAX_PATH];
1368 overlay_data.GetString("points_per_death", sPointsPerDeath, sizeof(sPointsPerDeath));
1369
1370 g_iCacheData_PointsGain[client] = StringToInt(sPointsPerKill);
1371 g_iCacheData_PointsLoss[client] = StringToInt(sPointsPerDeath);
1372 }
1373 }
1374}
1375
1376public void TQuery_SyncClient_Map(Database db, DBResultSet results, const char[] error, any data)
1377{
1378 if (results == null)
1379 ThrowError("Error updating client for the map: %s", error);
1380}
1381
1382public void OnClientCommandKeyValues_Post(int client, KeyValues kv)
1383{
1384 if (!convar_Status.BoolValue || IsFakeClient(client) || g_Database_Global == null)
1385 return;
1386
1387 char sTable[MAX_TABLE_SIZE];
1388 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
1389
1390 char sClantag[32];
1391 kv.GetString("tag", sClantag, sizeof(sClantag));
1392
1393 char sClanname[32];
1394 kv.GetString("name", sClanname, sizeof(sClanname));
1395
1396 char sQuery[MAX_QUERY_SIZE];
1397 g_Database_Global.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `clan_tag` = '%s', `clan_name` = '%s', `last_updated` = '%i' WHERE `accountid` = '%i';", sTable, sClantag, sClanname, GetTime(), GetSteamAccountID(client));
1398 g_Database_Global.Query(TQuery_OnClanDataUpdate, sQuery);
1399}
1400
1401public void TQuery_OnClanDataUpdate(Database db, DBResultSet results, const char[] error, any data)
1402{
1403 if (results == null)
1404 ThrowError("Error on updating client global clan data: %s", error);
1405}
1406
1407public void OnClientDisconnect(int client)
1408{
1409 if (!convar_Status.BoolValue || IsFakeClient(client) || !IsClientAuthorized(client))
1410 return;
1411
1412 if (IsClientInGame(client) && g_bIsLoaded[client])
1413 {
1414 SaveClientGlobalData(client);
1415 SaveClientServerData(client);
1416 SaveClientSessionData(client);
1417 }
1418}
1419
1420public void OnClientDisconnect_Post(int client)
1421{
1422 g_iCooldown[client] = -1;
1423 KillTimerSafe(g_hTimer_Playtime[client]);
1424
1425 delete g_StatisticsMenu[client];
1426
1427 if (g_hStatistics_Seasonal_WeaponsStatistics[client] != null)
1428 {
1429 g_hStatistics_Seasonal_WeaponsStatistics[client].Cleanup();
1430 delete g_hStatistics_Seasonal_WeaponsStatistics[client];
1431 }
1432}
1433
1434void SaveClientGlobalData(int client, Transaction trans = null)
1435{
1436 if (g_Database_Global == null || !IsClientAuthorized(client))
1437 return;
1438
1439 char sTable[MAX_TABLE_SIZE];
1440 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
1441
1442 char sName0[MAX_NAME_LENGTH], sName[1 + MAX_NAME_LENGTH * 2];
1443 GetClientName(client, sName0, sizeof(sName0));
1444 g_Database_Global.Escape(sName0, sName, sizeof(sName));
1445
1446 char sQuery[MAX_QUERY_SIZE];
1447 g_Database_Global.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `name` = '%s', `kills` = '%i', `deaths` = '%i', `assists` = '%i', `headshots` = '%i', `points` = '%i', `longest_killstreak` = '%i', `hits` = '%i', `shots` = '%i', `kdr` = '%f', `accuracy` = '%f', `last_updated` = '%i' WHERE `accountid` = '%i';",
1448 sTable,
1449 sName,
1450 g_iStatistics_Global_Kills[client],
1451 g_iStatistics_Global_Deaths[client],
1452 g_iStatistics_Global_Assists[client],
1453 g_iStatistics_Global_Headshots[client],
1454 g_iStatistics_Global_Points[client],
1455 g_iStatistics_Global_Longest_Killstreak[client],
1456 g_iStatistics_Global_Hits[client],
1457 g_iStatistics_Global_Shots[client],
1458 g_fStatistics_Global_KDR[client],
1459 g_fStatistics_Global_Accuracy[client],
1460 GetTime(),
1461 GetSteamAccountID(client));
1462
1463 if (trans != null)
1464 trans.AddQuery(sQuery);
1465 else
1466 g_Database_Global.Query(TQuery_OnSaveGlobalStats, sQuery);
1467}
1468
1469public void TQuery_OnSaveGlobalStats(Database db, DBResultSet results, const char[] error, any data)
1470{
1471 if (results == null)
1472 ThrowError("Error while saving client global statisitcs: %s", error);
1473}
1474
1475void SaveClientServerData(int client, Transaction trans = null)
1476{
1477 if (g_Database_Server == null)
1478 return;
1479
1480 char sName0[MAX_NAME_LENGTH], sName[1 + MAX_NAME_LENGTH * 2];
1481 GetClientName(client, sName0, sizeof(sName0));
1482 g_Database_Server.Escape(sName0, sName, sizeof(sName));
1483
1484 char sWeaponsStatistics[WEAPON_STATISTICS_SIZE];
1485 if (g_hStatistics_Seasonal_WeaponsStatistics[client] != null)
1486 {
1487 g_hStatistics_Seasonal_WeaponsStatistics[client].Encode(sWeaponsStatistics, sizeof(sWeaponsStatistics));
1488 Format(sWeaponsStatistics, sizeof(sWeaponsStatistics), ", `weapons_statistics` = '%s'", sWeaponsStatistics);
1489 }
1490
1491 int iAccountID = GetSteamAccountID(client);
1492 int time = GetTime();
1493
1494 char sQuery[MAX_QUERY_SIZE];
1495 char sTable[MAX_TABLE_SIZE];
1496
1497 GetTableString_Season(sTable, sizeof(sTable));
1498 Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `name` = '%s', `kills` = '%i', `deaths` = '%i', `assists` = '%i', `headshots` = '%i', `points` = '%i', `longest_killstreak` = '%i', `hits` = '%i', `shots` = '%i', `kdr` = '%f', `accuracy` = '%f'%s, `last_updated` = '%i' WHERE `accountid` = '%i';",
1499 sTable,
1500 sName,
1501 g_iStatistics_Seasonal_Kills[client],
1502 g_iStatistics_Seasonal_Deaths[client],
1503 g_iStatistics_Seasonal_Assists[client],
1504 g_iStatistics_Seasonal_Headshots[client],
1505 g_iStatistics_Seasonal_Points[client],
1506 g_iStatistics_Seasonal_Longest_Killstreak[client],
1507 g_iStatistics_Seasonal_Hits[client],
1508 g_iStatistics_Seasonal_Shots[client],
1509 g_fStatistics_Seasonal_KDR[client],
1510 g_fStatistics_Seasonal_Accuracy[client],
1511 strlen(sWeaponsStatistics) > 0 ? sWeaponsStatistics : "",
1512 time,
1513 iAccountID);
1514
1515 if (trans != null)
1516 trans.AddQuery(sQuery);
1517 else
1518 g_Database_Server.Query(TQuery_OnSaveServerSeason, sQuery);
1519
1520 GetTableString_Maps(sTable, sizeof(sTable));
1521 Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `name` = '%s', `kills` = '%i', `deaths` = '%i', `assists` = '%i', `headshots` = '%i', `points` = '%i', `longest_killstreak` = '%i', `hits` = '%i', `shots` = '%i', `kdr` = '%f', `accuracy` = '%f', `last_updated` = '%i' WHERE `accountid` = '%i' AND `map` = '%s';",
1522 sTable,
1523 sName,
1524 g_iStatistics_Mapbased_Kills[client],
1525 g_iStatistics_Mapbased_Deaths[client],
1526 g_iStatistics_Mapbased_Assists[client],
1527 g_iStatistics_Mapbased_Headshots[client],
1528 g_iStatistics_Mapbased_Points[client],
1529 g_iStatistics_Mapbased_Longest_Killstreak[client],
1530 g_iStatistics_Mapbased_Hits[client],
1531 g_iStatistics_Mapbased_Shots[client],
1532 g_fStatistics_Mapbased_KDR[client],
1533 g_fStatistics_Mapbased_Accuracy[client],
1534 time,
1535 iAccountID,
1536 g_sCurrentMap);
1537
1538 if (trans != null)
1539 trans.AddQuery(sQuery);
1540 else
1541 g_Database_Server.Query(TQuery_OnSaveServerMap, sQuery);
1542}
1543
1544public void TQuery_OnSaveServerSeason(Database db, DBResultSet results, const char[] error, any data)
1545{
1546 if (results == null)
1547 ThrowError("Error while saving server season statistics: %s", error);
1548}
1549
1550public void TQuery_OnSaveServerMap(Database db, DBResultSet results, const char[] error, any data)
1551{
1552 if (results == null)
1553 ThrowError("Error while saving server map statistics: %s", error);
1554}
1555
1556void SaveClientSessionData(int client)
1557{
1558 int iAccountID = GetSteamAccountID(client);
1559
1560 char sName[MAX_NAME_LENGTH];
1561 GetClientName(client, sName, sizeof(sName));
1562
1563 char sAccountID[64];
1564 IntToString(iAccountID, sAccountID, sizeof(sAccountID));
1565
1566 char sSteamID2[64];
1567 GetClientAuthId(client, AuthId_Steam2, sSteamID2, sizeof(sSteamID2));
1568
1569 char sSteamID3[64];
1570 GetClientAuthId(client, AuthId_Steam3, sSteamID3, sizeof(sSteamID3));
1571
1572 char sSteamID64[64];
1573 GetClientAuthId(client, AuthId_SteamID64, sSteamID64, sizeof(sSteamID64));
1574
1575 StringMap trie = new StringMap();
1576
1577 trie.SetString("Name", sName);
1578 trie.SetString("SteamID2", sSteamID2);
1579 trie.SetString("SteamID3", sSteamID3);
1580 trie.SetString("SteamID64", sSteamID64);
1581
1582 trie.SetValue("Kills", g_iStatistics_Sessionbased_Kills[client]);
1583 trie.SetValue("Deaths", g_iStatistics_Sessionbased_Deaths[client]);
1584 trie.SetValue("Assists", g_iStatistics_Sessionbased_Assists[client]);
1585 trie.SetValue("Headshots", g_iStatistics_Sessionbased_Headshots[client]);
1586 trie.SetValue("Hits", g_iStatistics_Sessionbased_Hits[client]);
1587 trie.SetValue("Shots", g_iStatistics_Sessionbased_Shots[client]);
1588 trie.SetValue("KDR", g_fStatistics_Sessionbased_KDR[client]);
1589 trie.SetValue("Accuracy", g_fStatistics_Sessionbased_Accuracy[client]);
1590 trie.SetValue("PointsGained", g_iStatistics_Sessionbased_PointsGained[client]);
1591 trie.SetValue("PointsLost", g_iStatistics_Sessionbased_PointsLost[client]);
1592 trie.SetValue("RanksGained", g_iStatistics_Sessionbased_RanksGained[client]);
1593 trie.SetValue("RanksLost", g_iStatistics_Sessionbased_RanksLost[client]);
1594
1595 g_SessionCache.SetValue(sAccountID, trie);
1596 g_SessionIDs.Push(iAccountID);
1597}
1598
1599public void OnPlayerHurt(Event event, const char[] name, bool dontBroadcast)
1600{
1601 int client = GetClientOfUserId(event.GetInt("userid"));
1602 int attacker = GetClientOfUserId(event.GetInt("attacker"));
1603
1604 if (!convar_Status.BoolValue || g_bBetweenRounds || !g_bRanked || client == 0 || convar_NoBotsStatistics.BoolValue && IsFakeClient(client))
1605 return;
1606
1607 if (attacker > 0 && attacker < MaxClients && !IsFakeClient(attacker))
1608 {
1609 g_iStatistics_Global_Hits[attacker]++;
1610 g_fStatistics_Global_Accuracy[attacker] = CalculateAccuracy(g_iStatistics_Global_Hits[attacker], g_iStatistics_Global_Shots[attacker]);
1611
1612 g_iStatistics_Seasonal_Hits[attacker]++;
1613 g_fStatistics_Seasonal_Accuracy[attacker] = CalculateAccuracy(g_iStatistics_Seasonal_Hits[attacker], g_iStatistics_Seasonal_Shots[attacker]);
1614
1615 g_iStatistics_Mapbased_Hits[attacker]++;
1616 g_fStatistics_Mapbased_Accuracy[attacker] = CalculateAccuracy(g_iStatistics_Mapbased_Hits[attacker], g_iStatistics_Mapbased_Shots[attacker]);
1617
1618 g_iStatistics_Sessionbased_Hits[attacker]++;
1619 g_fStatistics_Sessionbased_Accuracy[attacker] = CalculateAccuracy(g_iStatistics_Sessionbased_Hits[attacker], g_iStatistics_Sessionbased_Shots[attacker]);
1620
1621 char sWeapon[64];
1622 GetClientWeapon(attacker, sWeapon, sizeof(sWeapon));
1623
1624 IncrementWeaponDataValue(attacker, sWeapon, "hits");
1625 }
1626}
1627
1628public void OnPlayerDeath(Event event, const char[] name, bool dontBroadcast)
1629{
1630 int client = GetClientOfUserId(event.GetInt("userid"));
1631 int attacker = GetClientOfUserId(event.GetInt("attacker"));
1632 int assister = GetClientOfUserId(event.GetInt("assister"));
1633
1634 if (!convar_Status.BoolValue || !g_bRanked || client == 0 || (convar_NoBotsStatistics.BoolValue && IsFakeClient(client)))
1635 return;
1636
1637 if (!IsFakeClient(client))
1638 {
1639 // This will allow the HUD to update
1640 PrintHintText(client, "");
1641
1642 g_iStatistics_Global_Deaths[client]++;
1643 g_fStatistics_Global_KDR[client] = CalculateKDR(g_iStatistics_Global_Kills[client], g_iStatistics_Global_Deaths[client]);
1644
1645 g_iStatistics_Seasonal_Deaths[client]++;
1646 g_fStatistics_Seasonal_KDR[client] = CalculateKDR(g_iStatistics_Seasonal_Kills[client], g_iStatistics_Seasonal_Deaths[client]);
1647 g_iStatistics_Seasonal_Points[client] -= g_iCacheData_PointsLoss[client];
1648
1649 g_iStatistics_Mapbased_Deaths[client]++;
1650 g_fStatistics_Mapbased_KDR[client] = CalculateKDR(g_iStatistics_Mapbased_Kills[client], g_iStatistics_Mapbased_Deaths[client]);
1651 g_iStatistics_Mapbased_Points[client] -= g_iCacheData_PointsLoss[client];
1652
1653 g_iStatistics_Sessionbased_Deaths[client]++;
1654 g_fStatistics_Sessionbased_KDR[client] = CalculateKDR(g_iStatistics_Sessionbased_Kills[client], g_iStatistics_Sessionbased_Deaths[client]);
1655 g_iStatistics_Sessionbased_PointsLost[client] += g_iCacheData_PointsLoss[client];
1656
1657 g_iLocalKillstreak[client] = 0;
1658
1659 if (g_bRampage[client])
1660 {
1661 g_bRampage[client] = false;
1662 CPrintToChat(client, "%T", "rampage end", client);
1663 }
1664
1665 if (client != attacker)
1666 CPrintToChat(client, "%T", "kill points loss", client, g_iCacheData_PointsLoss[client], attacker);
1667 else
1668 CPrintToChat(client, "%T", "kill points loss suicide", client, g_iCacheData_PointsLoss[client]);
1669
1670 SaveDeathStatistics(client);
1671 }
1672
1673 if (attacker > 0 && !IsFakeClient(attacker) && client != attacker)
1674 {
1675 g_iStatistics_Global_Kills[attacker]++;
1676 g_fStatistics_Global_KDR[attacker] = CalculateKDR(g_iStatistics_Global_Kills[attacker], g_iStatistics_Global_Deaths[attacker]);
1677
1678 char sWeapon[64];
1679 GetClientWeapon(attacker, sWeapon, sizeof(sWeapon));
1680
1681 if (StrContains(sWeapon, "knife") != -1)
1682 {
1683 if (!g_bRampage[attacker])
1684 ShowClientOverlay(attacker, OVERLAY_RAMPAGE, 3.0);
1685
1686 g_bRampage[attacker] = true;
1687 }
1688
1689 if (g_bRampage[attacker])
1690 CPrintToChat(attacker, "%T", "kill points gain rampage", attacker, g_iCacheData_PointsGain[attacker], client, convar_Rampage_Points.IntValue);
1691 else
1692 CPrintToChat(attacker, "%T", "kill points gain", attacker, g_iCacheData_PointsGain[attacker], client);
1693
1694 g_iStatistics_Seasonal_Kills[attacker]++;
1695 g_fStatistics_Seasonal_KDR[attacker] = CalculateKDR(g_iStatistics_Seasonal_Kills[attacker], g_iStatistics_Seasonal_Deaths[attacker]);
1696 g_iStatistics_Seasonal_Points[attacker] += g_iCacheData_PointsGain[attacker] + (g_bRampage[attacker] ? convar_Rampage_Points.IntValue : 0);
1697
1698 g_iStatistics_Mapbased_Kills[attacker]++;
1699 g_fStatistics_Mapbased_KDR[attacker] = CalculateKDR(g_iStatistics_Mapbased_Kills[attacker], g_iStatistics_Mapbased_Deaths[attacker]);
1700 g_iStatistics_Mapbased_Points[attacker] += g_iCacheData_PointsGain[attacker] + (g_bRampage[attacker] ? convar_Rampage_Points.IntValue : 0);
1701
1702 g_iStatistics_Sessionbased_Kills[attacker]++;
1703 g_fStatistics_Sessionbased_KDR[attacker] = CalculateKDR(g_iStatistics_Sessionbased_Kills[attacker], g_iStatistics_Sessionbased_Deaths[attacker]);
1704 g_iStatistics_Sessionbased_PointsGained[attacker] += g_iCacheData_PointsGain[attacker] + (g_bRampage[attacker] ? convar_Rampage_Points.IntValue : 0);
1705
1706 if (plugin_store)
1707 Furious_Store_AddCredits(attacker, g_iCacheData_PointsGain[attacker]);
1708
1709 g_iLocalKillstreak[attacker]++;
1710
1711 if (g_iLocalKillstreak[attacker] > g_iStatistics_Global_Longest_Killstreak[attacker])
1712 g_iStatistics_Global_Longest_Killstreak[attacker] = g_iLocalKillstreak[attacker];
1713
1714 if (g_iLocalKillstreak[attacker] > g_iStatistics_Seasonal_Longest_Killstreak[attacker])
1715 g_iStatistics_Seasonal_Longest_Killstreak[attacker] = g_iLocalKillstreak[attacker];
1716
1717 if (g_iLocalKillstreak[attacker] > g_iStatistics_Mapbased_Longest_Killstreak[attacker])
1718 g_iStatistics_Mapbased_Longest_Killstreak[attacker] = g_iLocalKillstreak[attacker];
1719
1720 if (event.GetBool("headshot"))
1721 {
1722 g_iStatistics_Global_Headshots[attacker]++;
1723 g_iStatistics_Seasonal_Headshots[attacker]++;
1724 g_iStatistics_Mapbased_Headshots[attacker]++;
1725 g_iStatistics_Sessionbased_Headshots[attacker]++;
1726 }
1727
1728 IncrementWeaponDataValue(attacker, sWeapon, "kills");
1729
1730 if (convar_SaveOnPlayerDeath.BoolValue)
1731 UpdateClientPositions(attacker, true);
1732 }
1733
1734 if (assister > 0 && !IsFakeClient(assister) && client != assister)
1735 {
1736 g_iStatistics_Global_Assists[assister]++;
1737
1738 g_iStatistics_Seasonal_Assists[assister]++;
1739 g_iStatistics_Seasonal_Points[assister]++;
1740
1741 g_iStatistics_Mapbased_Assists[assister]++;
1742 g_iStatistics_Mapbased_Points[assister]++;
1743
1744 g_iStatistics_Sessionbased_Assists[assister]++;
1745 g_iStatistics_Sessionbased_PointsGained[assister]++;
1746
1747 CPrintToChat(assister, "%T", "assist points", assister, client);
1748 }
1749}
1750
1751public Action Timer_ResetOverlayForClient(Handle timer, any data)
1752{
1753 int client;
1754 if ((client = GetClientOfUserId(data)) > 0 && IsClientInGame(client))
1755 {
1756 int iFlags = GetCommandFlags("r_screenoverlay");
1757 SetCommandFlags("r_screenoverlay", iFlags & ~FCVAR_CHEAT);
1758 ClientCommand(client, "r_screenoverlay \"\"");
1759 SetCommandFlags("r_screenoverlay", iFlags);
1760 }
1761}
1762
1763public void OnWeaponFire(Event event, const char[] name, bool dontBroadcast)
1764{
1765 int client = GetClientOfUserId(event.GetInt("userid"));
1766
1767 if (!convar_Status.BoolValue || g_bBetweenRounds || !g_bRanked || client == 0 || IsFakeClient(client))
1768 return;
1769
1770 g_iStatistics_Global_Shots[client]++;
1771 g_fStatistics_Global_Accuracy[client] = CalculateAccuracy(g_iStatistics_Global_Hits[client], g_iStatistics_Global_Shots[client]);
1772
1773 g_iStatistics_Seasonal_Shots[client]++;
1774 g_fStatistics_Seasonal_Accuracy[client] = CalculateAccuracy(g_iStatistics_Seasonal_Hits[client], g_iStatistics_Seasonal_Shots[client]);
1775
1776 g_iStatistics_Mapbased_Shots[client]++;
1777 g_fStatistics_Mapbased_Accuracy[client] = CalculateAccuracy(g_iStatistics_Mapbased_Hits[client], g_iStatistics_Mapbased_Shots[client]);
1778
1779 g_iStatistics_Sessionbased_Shots[client]++;
1780 g_fStatistics_Sessionbased_Accuracy[client] = CalculateAccuracy(g_iStatistics_Sessionbased_Hits[client], g_iStatistics_Sessionbased_Shots[client]);
1781
1782 char sWeapon[64];
1783 GetClientWeapon(client, sWeapon, sizeof(sWeapon));
1784
1785 IncrementWeaponDataValue(client, sWeapon, "shots");
1786}
1787
1788void IncrementWeaponDataValue(int client, const char[] sWeapon, const char[] sStat)
1789{
1790 if (g_hStatistics_Seasonal_WeaponsStatistics[client] == null || strlen(sWeapon) == 0 || strlen(sStat) == 0)
1791 return;
1792
1793 JSON_Object hWeaponObj = g_hStatistics_Seasonal_WeaponsStatistics[client].GetObject(sWeapon);
1794
1795 if (hWeaponObj == null)
1796 {
1797 hWeaponObj = new JSON_Object();
1798 g_hStatistics_Seasonal_WeaponsStatistics[client].SetObject(sWeapon, hWeaponObj);
1799 }
1800
1801 int stat = hWeaponObj.GetInt(sStat);
1802
1803 stat++;
1804
1805 hWeaponObj.SetInt(sStat, stat);
1806}
1807
1808public void OnPlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
1809{
1810 int client = GetClientOfUserId(event.GetInt("userid"));
1811
1812 if (!convar_Status.BoolValue || client == 0 || client > MaxClients || IsFakeClient(client))
1813 return;
1814
1815 char sSteamID2[64];
1816 GetClientAuthId(client, AuthId_Steam2, sSteamID2, sizeof(sSteamID2));
1817
1818 char sReason[64];
1819 event.GetString("reason", sReason, sizeof(sReason));
1820
1821 CPrintToChatAll("%t", "disconnect message", client, sSteamID2, sReason);
1822
1823 float time = GetClientTime(client);
1824 int accountid = GetSteamAccountID(client);
1825
1826 char sTable[MAX_TABLE_SIZE];
1827 char sQuery[MAX_QUERY_SIZE];
1828
1829 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
1830 g_Database_Global.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `playtime` = `playtime` + '%f' WHERE `accountid` = '%i';", sTable, time, accountid);
1831 g_Database_Global.Query(TQuery_UpdatePlaytime, sQuery);
1832
1833 Transaction trans = new Transaction();
1834
1835 GetTableString_Season(sTable, sizeof(sTable));
1836 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `playtime` = `playtime` + '%f' WHERE `accountid` = '%i';", sTable, time, accountid);
1837 trans.AddQuery(sQuery);
1838
1839 GetTableString_Maps(sTable, sizeof(sTable));
1840 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `playtime` = `playtime` + '%f' WHERE `accountid` = '%i' AND `map` = '%s';", sTable, time, accountid, g_sCurrentMap);
1841 trans.AddQuery(sQuery);
1842
1843 g_Database_Server.Execute(trans, _, onError_SavePlayetimes);
1844}
1845
1846public void TQuery_UpdatePlaytime(Database db, DBResultSet results, const char[] error, any dat)
1847{
1848 if (results == null)
1849 ThrowError("Error while updating client playtime: %s", error);
1850}
1851
1852public void onError_SavePlayetimes(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData)
1853{
1854 ThrowError("Error while saving client playtime statistics at query %i: %s", failIndex, error);
1855}
1856
1857public void OnRoundFreezeEnd(Event event, const char[] name, bool dontBroadcast)
1858{
1859 if (!convar_Status.BoolValue)
1860 return;
1861
1862 g_bBetweenRounds = false;
1863 ValidateRankCheck();
1864}
1865
1866void ValidateRankCheck()
1867{
1868 int iCurrent = CountFuriousClients();
1869 int iRequired = convar_MinimumPlayersStatistics.IntValue;
1870
1871 g_bRanked = iCurrent >= iRequired;
1872 AnnounceRankStatus(iCurrent, iRequired);
1873}
1874
1875void AnnounceRankStatus(int current, int required)
1876{
1877 if (g_bRanked)
1878 {
1879 CPrintToChatAll("%t", "ranking enabled print");
1880
1881 char sRankSound[PLATFORM_MAX_PATH];
1882 convar_Sound_RankEnabled.GetString(sRankSound, sizeof(sRankSound));
1883
1884 if (strlen(sRankSound) > 0 && IsSoundPrecached(sRankSound))
1885 EmitSoundToAll(sRankSound);
1886
1887 if (convar_RankEnabled_Status.BoolValue)
1888 {
1889 char sChannel[12];
1890 convar_RankEnabled_Channel.GetString(sChannel, sizeof(sChannel));
1891
1892 char sCoordinate_X[12];
1893 convar_RankEnabled_Coordinate_X.GetString(sCoordinate_X, sizeof(sCoordinate_X));
1894
1895 char sCoordinate_Y[12];
1896 convar_RankEnabled_Coordinate_Y.GetString(sCoordinate_Y, sizeof(sCoordinate_Y));
1897
1898 char sColor[64];
1899 convar_RankEnabled_Color.GetString(sColor, sizeof(sColor));
1900
1901 char sRankHudSync[256];
1902 FormatEx(sRankHudSync, sizeof(sRankHudSync), "%t", "ranking enabled hud sync");
1903
1904 int entity; char output[64];
1905 for (int i = 1; i <= MaxClients; i++)
1906 {
1907 if (!IsClientInGame(i) || IsFakeClient(i))
1908 continue;
1909
1910 entity = CreateEntityByName("game_text");
1911
1912 if (IsValidEntity(entity))
1913 {
1914 DispatchKeyValue(entity, "channel", sChannel);
1915 DispatchKeyValue(entity, "color", sColor);
1916 DispatchKeyValue(entity, "message", sRankHudSync);
1917 DispatchKeyValue(entity, "x", sCoordinate_X);
1918 DispatchKeyValue(entity, "y", sCoordinate_Y);
1919 DispatchSpawn(entity);
1920
1921 SetVariantString("!activator");
1922 AcceptEntityInput(entity, "display", i);
1923
1924 Format(output, sizeof(output), "OnUser1 !self:kill::%.1f:1", 10.0);
1925 SetVariantString(output);
1926 AcceptEntityInput(entity, "AddOutput");
1927 AcceptEntityInput(entity, "FireUser1");
1928 }
1929 }
1930 }
1931 }
1932 else
1933 {
1934 CPrintToChatAll("%t", "ranking disabled print", current, required);
1935
1936 char sRankSound[PLATFORM_MAX_PATH];
1937 convar_Sound_RankDisabled.GetString(sRankSound, sizeof(sRankSound));
1938
1939 if (strlen(sRankSound) > 0 && IsSoundPrecached(sRankSound))
1940 EmitSoundToAll(sRankSound);
1941
1942 if (convar_RankDisabled_Status.BoolValue)
1943 {
1944 char sChannel[12];
1945 convar_RankDisabled_Channel.GetString(sChannel, sizeof(sChannel));
1946
1947 char sCoordinate_X[12];
1948 convar_RankDisabled_Coordinate_X.GetString(sCoordinate_X, sizeof(sCoordinate_X));
1949
1950 char sCoordinate_Y[12];
1951 convar_RankDisabled_Coordinate_Y.GetString(sCoordinate_Y, sizeof(sCoordinate_Y));
1952
1953 char sColor[64];
1954 convar_RankDisabled_Color.GetString(sColor, sizeof(sColor));
1955
1956 char sRankHudSync[256];
1957 FormatEx(sRankHudSync, sizeof(sRankHudSync), "%t", "ranking disabled hud sync", current, required);
1958
1959 int entity; char output[64];
1960 for (int i = 1; i <= MaxClients; i++)
1961 {
1962 if (!IsClientInGame(i) || IsFakeClient(i))
1963 continue;
1964
1965 entity = CreateEntityByName("game_text");
1966
1967 if (IsValidEntity(entity))
1968 {
1969 DispatchKeyValue(entity, "channel", sChannel);
1970 DispatchKeyValue(entity, "color", sColor);
1971 DispatchKeyValue(entity, "message", sRankHudSync);
1972 DispatchKeyValue(entity, "x", sCoordinate_X);
1973 DispatchKeyValue(entity, "y", sCoordinate_Y);
1974 DispatchSpawn(entity);
1975
1976 SetVariantString("!activator");
1977 AcceptEntityInput(entity, "display", i);
1978
1979 Format(output, sizeof(output), "OnUser1 !self:kill::%.1f:1", 10.0);
1980 SetVariantString(output);
1981 AcceptEntityInput(entity, "AddOutput");
1982 AcceptEntityInput(entity, "FireUser1");
1983 }
1984 }
1985 }
1986 }
1987}
1988
1989int CountFuriousClients()
1990{
1991 int iCount;
1992
1993 for (int i = 1; i <= MaxClients; i++)
1994 {
1995 if (!IsClientInGame(i) || IsFakeClient(i) || GetClientTeam(i) <= 1)
1996 continue;
1997
1998 iCount++;
1999 }
2000
2001 return iCount;
2002}
2003
2004public void OnRoundEnd(Event event, const char[] name, bool dontBroadcast)
2005{
2006 if (!convar_Status.BoolValue)
2007 return;
2008
2009 g_bBetweenRounds = true;
2010
2011 if (!convar_RoundClampStatistics.BoolValue)
2012 g_bBetweenRounds = false;
2013
2014 if (convar_SaveOnRoundEnd.BoolValue)
2015 SyncAllClientStats();
2016}
2017
2018public void OnMatchEnd(Event event, const char[] name, bool dontBroadcast)
2019{
2020 char sMap[64];
2021 if (GetNextMap(sMap, sizeof(sMap)))
2022 {
2023 CPrintToChatAll("%t", "next map line");
2024 CPrintToChatAll("%t", "next map name", sMap);
2025 CPrintToChatAll("%t", "next map line");
2026 }
2027}
2028
2029void SyncAllClientStats()
2030{
2031 Transaction trans;
2032
2033 if (g_Database_Global != null)
2034 {
2035 trans = new Transaction();
2036
2037 for (int i = 1; i <= MaxClients; i++)
2038 {
2039 if (!IsClientInGame(i) || IsFakeClient(i))
2040 continue;
2041
2042 SaveClientGlobalData(i, trans);
2043 }
2044
2045 g_Database_Global.Execute(trans);
2046 }
2047
2048 if (g_Database_Server != null)
2049 {
2050 trans = new Transaction();
2051
2052 for (int i = 1; i <= MaxClients; i++)
2053 {
2054 if (!IsClientInGame(i) || IsFakeClient(i))
2055 continue;
2056
2057 SaveClientServerData(i, trans);
2058 }
2059
2060 g_Database_Server.Execute(trans);
2061 }
2062}
2063
2064public Action Command_OpenStatisticsMenu(int client, int args)
2065{
2066 if (!convar_Status.BoolValue || client == 0)
2067 return Plugin_Handled;
2068
2069 char sCommand[64];
2070 GetCmdArg(0, sCommand, sizeof(sCommand));
2071
2072 char sSearch[64];
2073 if (StrEqual(sCommand, "sm_statsme"))
2074 GetClientAuthId(client, AuthId_Steam2, sSearch, sizeof(sSearch));
2075 else
2076 {
2077 g_bToggleStatistics[client] = StrEqual(sCommand, "sm_session");
2078
2079 GetCmdArg(1, sSearch, sizeof(sSearch));
2080
2081 if (args == 0 || strlen(sSearch) == 0)
2082 GetClientAuthId(client, AuthId_Steam2, sSearch, sizeof(sSearch));
2083 }
2084
2085 ShowStatisticsMenu(client, sSearch);
2086
2087 return Plugin_Handled;
2088}
2089
2090void ShowStatisticsMenu(int client, const char[] sSearch)
2091{
2092 bool bIsSteamID = StrContains(sSearch, "STEAM_") != -1;
2093
2094 DataPack pack = new DataPack();
2095 pack.WriteCell(GetClientUserId(client));
2096 pack.WriteCell(bIsSteamID);
2097 pack.WriteString(sSearch);
2098
2099 int size = 2* strlen(sSearch) + 1;
2100 char[] sSearchE = new char[size];
2101 g_Database_Server.Escape(sSearch, sSearchE, size);
2102
2103 char sTable[MAX_TABLE_SIZE];
2104 GetTableString_Season(sTable, sizeof(sTable));
2105
2106 char sSearchSteamID[64];
2107 Format(sSearchSteamID, sizeof(sSearchSteamID), "s.steamid2 = '%s'", sSearchE);
2108
2109 char sSearchName[64];
2110 Format(sSearchName, sizeof(sSearchName), "s.name LIKE '%%%s%%'", sSearchE);
2111
2112 char sQuery[MAX_QUERY_SIZE];
2113 Format(sQuery, sizeof(sQuery), "SELECT s.name, s.accountid, s.steamid2, s.steamid64, s.last_updated, s.points, s.kills, s.deaths, s.assists, s.headshots, s.kdr, s.accuracy, s.playtime, s.weapons_statistics, (SELECT COUNT(*) as rank FROM `%s` as r WHERE r.points > s.points OR (r.points = s.points AND r.kills > s.kills)) + 1 as rank, (SELECT COUNT(*) as total FROM `%s`) as total FROM `%s` as s WHERE %s;", sTable, sTable, sTable, bIsSteamID ? sSearchSteamID: sSearchName);
2114 g_Database_Server.Query(TQuery_PullMenuStatistics, sQuery, pack);
2115}
2116
2117public void TQuery_PullMenuStatistics(Database db, DBResultSet results, const char[] error, DataPack data)
2118{
2119 if (results == null)
2120 {
2121 delete data;
2122 ThrowError("Error pulling client season statistics for display: %s", error);
2123 }
2124
2125 data.Reset();
2126
2127 int client = GetClientOfUserId(data.ReadCell());
2128 bool bIsSteamID = view_as<bool>(data.ReadCell());
2129
2130 char sSearch[64];
2131 data.ReadString(sSearch, sizeof(sSearch));
2132
2133 delete data;
2134
2135 if (client == 0)
2136 return;
2137
2138 if (results == null)
2139 {
2140 CPrintToChat(client, "%T", bIsSteamID ? "steamid not in database" : "player not found", client, sSearch);
2141 return;
2142 }
2143
2144 int iRows = results.RowCount;
2145
2146 if (iRows == 0)
2147 {
2148 CPrintToChat(client, "%T", bIsSteamID ? "steamid not in database" : "player not found", client, sSearch);
2149 return;
2150 }
2151 else if (iRows > 1)
2152 {
2153 CPrintToChat(client, "%T", "found more than one", client, sSearch);
2154 return;
2155 }
2156
2157 if (results.FetchRow())
2158 {
2159 char sName[MAX_NAME_LENGTH];
2160 results.FetchString(0, sName, sizeof(sName));
2161
2162 int iAccountID = results.FetchInt(1);
2163
2164 char sSteamID2[64];
2165 results.FetchString(2, sSteamID2, sizeof(sSteamID2));
2166
2167 char sSteamID64[64];
2168 results.FetchString(3, sSteamID64, sizeof(sSteamID64));
2169
2170 int iLastUpdated = results.FetchInt(4);
2171 int iPoints = results.FetchInt(5);
2172 int iKills = results.FetchInt(6);
2173 int iDeaths = results.FetchInt(7);
2174 int iAssists = results.FetchInt(8);
2175 int iHeadshots = results.FetchInt(9);
2176 float fKDR = results.FetchFloat(10);
2177 float fAccuracy = results.FetchFloat(11);
2178 float fPlaytime = results.FetchFloat(12);
2179
2180 char sWeaponsData[WEAPON_STATISTICS_SIZE];
2181 results.FetchString(13, sWeaponsData, sizeof(sWeaponsData));
2182
2183 int iRank = results.FetchInt(14);
2184 g_iCachedPlayers = results.FetchInt(15);
2185
2186 int target = FindAccountIDOnServer(iAccountID);
2187
2188 GenerateStatisticsMenu(client, target, sName, iAccountID, sSteamID2, sSteamID64, iLastUpdated, iPoints, iKills, iDeaths, iAssists, iHeadshots, fKDR, fAccuracy, fPlaytime, sWeaponsData, iRank);
2189 }
2190}
2191
2192int FindAccountIDOnServer(int iAccountID)
2193{
2194 for (int i = 1; i <= MaxClients; i++)
2195 {
2196 if (!IsClientInGame(i) || IsFakeClient(i) || GetSteamAccountID(i) != iAccountID)
2197 continue;
2198
2199 return i;
2200 }
2201
2202 return -1;
2203}
2204
2205void GenerateStatisticsMenu(int client, int target, const char[] sName, int iAccountID, const char[] sSteamID2, const char[] sSteamID64, int iLastUpdated, int iPoints, int iKills, int iDeaths, int iAssists, int iHeadshots, float fKDR, float fAccuracy, float fPlaytime, const char[] sWeaponsData, int iRank)
2206{
2207 char sFirstCreated[128];
2208
2209 if (target > 0)
2210 FormatTime(sFirstCreated, sizeof(sFirstCreated), "%A, %B %d, %Y", g_iStatistics_Global_FirstCreated[target]);
2211 else
2212 FormatEx(sFirstCreated, sizeof(sFirstCreated), "Not Online");
2213
2214 char sLastUpdated[128];
2215 FormatTime(sLastUpdated, sizeof(sLastUpdated), "%A, %B %d, %Y", iLastUpdated);
2216
2217 float time;
2218
2219 if (target > 0)
2220 time = g_fStatistics_Global_Playtime[target] + GetClientTime(target);
2221 else
2222 time = fPlaytime;
2223
2224 char sPlaytime[128];
2225 FormatSeconds(time, sPlaytime, sizeof(sPlaytime), "%D days %H hours %M minutes");
2226
2227 delete g_StatisticsMenu[client];
2228 g_StatisticsMenu[client] = new Menu(MenuHandle_Statistics, MenuAction_Select|MenuAction_Cancel|MenuAction_End|MenuAction_DrawItem|MenuAction_DisplayItem);
2229 g_StatisticsMenu[client].SetTitle("%s\n%s\nFirst Seen: %s\nLast Seen: %s\nPlaytime: %s", sName, sSteamID2, sFirstCreated, sLastUpdated, sPlaytime);
2230
2231 g_StatisticsMenu[client].AddItem("season", "Season Stats");
2232 g_StatisticsMenu[client].AddItem("session", "Session Stats");
2233 g_StatisticsMenu[client].AddItem("weapon", "Weapon Stats");
2234
2235 PushMenuInt(g_StatisticsMenu[client], "target", (target > 0 ? GetClientUserId(target) : 0));
2236 PushMenuString(g_StatisticsMenu[client], "name", sName);
2237 PushMenuInt(g_StatisticsMenu[client], "accountid", iAccountID);
2238 PushMenuString(g_StatisticsMenu[client], "steamid2", sSteamID2);
2239 PushMenuString(g_StatisticsMenu[client], "steamid64", sSteamID64);
2240 PushMenuInt(g_StatisticsMenu[client], "points", iPoints);
2241 PushMenuInt(g_StatisticsMenu[client], "kills", iKills);
2242 PushMenuInt(g_StatisticsMenu[client], "deaths", iDeaths);
2243 PushMenuInt(g_StatisticsMenu[client], "assists", iAssists);
2244 PushMenuInt(g_StatisticsMenu[client], "headshots", iHeadshots);
2245 PushMenuFloat(g_StatisticsMenu[client], "kdr", fKDR);
2246 PushMenuFloat(g_StatisticsMenu[client], "accuracy", fAccuracy);
2247 PushMenuString(g_StatisticsMenu[client], "weapons_statistics", sWeaponsData);
2248 PushMenuInt(g_StatisticsMenu[client], "rank", iRank);
2249
2250 g_StatisticsMenu[client].Display(client, MENU_TIME_FOREVER);
2251}
2252
2253public int MenuHandle_Statistics(Menu menu, MenuAction action, int param1, int param2)
2254{
2255 switch (action)
2256 {
2257 case MenuAction_DrawItem:
2258 {
2259 char sInfo[32]; int style;
2260 menu.GetItem(param2, sInfo, sizeof(sInfo), style);
2261
2262 int target = GetClientOfUserId(GetMenuInt(menu, "target"));
2263
2264 return (StrEqual(sInfo, "session") && target < 0) ? ITEMDRAW_DISABLED : style;
2265 }
2266 case MenuAction_DisplayItem:
2267 {
2268 char sInfo[32]; char sDisplay[32];
2269 menu.GetItem(param2, sInfo, sizeof(sInfo), _, sDisplay, sizeof(sDisplay));
2270
2271 char sItemDisplay[512];
2272 if (StrEqual(sInfo, "season"))
2273 {
2274 int iRank = GetMenuInt(menu, "rank");
2275 int iPoints = GetMenuInt(menu, "points");
2276 int iKills = GetMenuInt(menu, "kills");
2277 int iDeaths = GetMenuInt(menu, "deaths");
2278 int iAssists = GetMenuInt(menu, "assists");
2279 int iHeadshots = GetMenuInt(menu, "headshots");
2280 float fKDR = GetMenuFloat(menu, "kdr");
2281 float fAccuracy = GetMenuFloat(menu, "accuracy");
2282
2283 char sSeasonStats[256];
2284 Format(sSeasonStats, sizeof(sSeasonStats), "Season Stats\nRank: %i\nPoints: %i\nKills: %i | Deaths: %i\nAssists: %i\nKDR: %.2f\nHeadshots: %i\nAccuracy: %.2f", iRank, iPoints, iKills, iDeaths, iAssists, fKDR, iHeadshots, fAccuracy);
2285
2286 FormatEx(sItemDisplay, sizeof(sItemDisplay), "%s", g_bToggleStatistics[param1] ? "Season Stats" : sSeasonStats);
2287 return RedrawMenuItem(sItemDisplay);
2288 }
2289 else if (StrEqual(sInfo, "session"))
2290 {
2291 int target;
2292 if ((target = GetClientOfUserId(GetMenuInt(menu, "target"))) == 0)
2293 {
2294 g_StatisticsMenu[param1].Display(param1, MENU_TIME_FOREVER);
2295 return 0;
2296 }
2297
2298 char sSessionStats[256];
2299 Format(sSessionStats, sizeof(sSessionStats), "Session Stats\nRanks Gained: %i\nPoints Gained: %i\nKills: %i | Deaths: %i\nAssists: %i\nKDR: %.2f\nHeadshots: %i\nAccuracy: %.2f", g_iStatistics_Sessionbased_RanksGained[target], g_iStatistics_Sessionbased_PointsGained[target], g_iStatistics_Sessionbased_Kills[target], g_iStatistics_Sessionbased_Deaths[target], g_iStatistics_Sessionbased_Assists[target], g_fStatistics_Sessionbased_KDR[target], g_iStatistics_Sessionbased_Headshots[target], g_fStatistics_Sessionbased_Accuracy[target]);
2300
2301 FormatEx(sItemDisplay, sizeof(sItemDisplay), "%s", !g_bToggleStatistics[param1] ? "Session Stats" : sSessionStats);
2302 return RedrawMenuItem(sItemDisplay);
2303 }
2304 }
2305 case MenuAction_Select:
2306 {
2307 if (!convar_Status.BoolValue)
2308 return 0;
2309
2310 char sInfo[32]; char sDisplay[32];
2311 menu.GetItem(param2, sInfo, sizeof(sInfo), _, sDisplay, sizeof(sDisplay));
2312
2313 if (StrEqual(sInfo, "season"))
2314 {
2315 g_bToggleStatistics[param1] = false;
2316 g_StatisticsMenu[param1].Display(param1, MENU_TIME_FOREVER);
2317 }
2318 else if (StrEqual(sInfo, "session"))
2319 {
2320 g_bToggleStatistics[param1] = true;
2321 g_StatisticsMenu[param1].Display(param1, MENU_TIME_FOREVER);
2322 }
2323 else if (StrEqual(sInfo, "weapon"))
2324 {
2325 char sName[MAX_NAME_LENGTH];
2326 GetMenuString(menu, "name", sName, sizeof(sName));
2327
2328 char sWeaponsData[WEAPON_STATISTICS_SIZE];
2329 GetMenuString(menu, "weapons_statistics", sWeaponsData, sizeof(sWeaponsData));
2330
2331 DisplayWeaponStats(param1, sName, sWeaponsData, true);
2332 }
2333 }
2334 }
2335
2336 return 0;
2337}
2338
2339void DisplayWeaponStats(int client, const char[] name, const char[] json_data, bool info = false)
2340{
2341 Menu menu = new Menu(MenuHandle_WeaponKills);
2342 menu.SetTitle("%s's Weapon Kills\n\n ", name);
2343
2344 JSON_Object hJSON = json_decode(json_data);
2345
2346 if (hJSON == null)
2347 {
2348 delete menu;
2349
2350 if (info && g_StatisticsMenu[client] != null)
2351 g_StatisticsMenu[client].Display(client, MENU_TIME_FOREVER);
2352
2353 return;
2354 }
2355
2356 char sWeapon[MAX_NAME_LENGTH]; JSON_Object hWeaponObject; int iKills;
2357 for (int i = 0; i < g_WeaponsList.Length; i++)
2358 {
2359 g_WeaponsList.GetString(i, sWeapon, sizeof(sWeapon));
2360
2361 iKills = 0;
2362 hWeaponObject = hJSON.GetObject(sWeapon);
2363
2364 if (hWeaponObject == null)
2365 continue;
2366
2367 iKills = hWeaponObject.GetInt("kills");
2368
2369 ReplaceString(sWeapon, sizeof(sWeapon), "weapon_", "");
2370 sWeapon[0] = CharToUpper(sWeapon[0]);
2371
2372 AddMenuItemFormat(menu, "", ITEMDRAW_DISABLED, "%s: %i", sWeapon, iKills);
2373 }
2374
2375 //This cleans up the hWeaponObject handles as well. (See line 694 in json.inc)
2376 hJSON.Cleanup();
2377 delete hJSON;
2378
2379 if (menu.ItemCount == 0)
2380 menu.AddItem("", "[No Data Available]", ITEMDRAW_DISABLED);
2381
2382 menu.ExitBackButton = info;
2383 menu.Display(client, MENU_TIME_FOREVER);
2384}
2385
2386public int MenuHandle_WeaponKills(Menu menu, MenuAction action, int param1, int param2)
2387{
2388 switch (action)
2389 {
2390 case MenuAction_Cancel:
2391 {
2392 if (param2 == MenuCancel_ExitBack)
2393 g_StatisticsMenu[param1].Display(param1, MENU_TIME_FOREVER);
2394 }
2395
2396 case MenuAction_End:
2397 delete menu;
2398 }
2399}
2400
2401public Action Command_OpenTopRanksMenu(int client, int args)
2402{
2403 if (!convar_Status.BoolValue || client == 0)
2404 return Plugin_Handled;
2405
2406 int iAmount = 10;
2407
2408 char sArg[12];
2409 GetCmdArgString(sArg, sizeof(sArg));
2410
2411 if (strlen(sArg) > 0)
2412 iAmount = StringToInt(sArg);
2413
2414 if (iAmount > 50)
2415 iAmount = 50;
2416
2417 DisplayTopRanksMenu(client, iAmount);
2418 return Plugin_Handled;
2419}
2420
2421void DisplayTopRanksMenu(int client, int amount)
2422{
2423 if (!IsClientInGame(client) || IsFakeClient(client) || amount == 0 || amount > 50)
2424 return;
2425
2426 char sTable[MAX_TABLE_SIZE];
2427 GetTableString_Season(sTable, sizeof(sTable));
2428
2429 DataPack pack = new DataPack();
2430 pack.WriteCell(GetClientUserId(client));
2431 pack.WriteCell(amount);
2432
2433 char sQuery[MAX_QUERY_SIZE];
2434 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT `name`, `steamid2`, `kdr`, `points` FROM `%s` ORDER BY `points` DESC LIMIT 0,%i;", sTable, amount);
2435 g_Database_Server.Query(TQuery_DisplayTopRanksMenu, sQuery, pack);
2436}
2437
2438public void TQuery_DisplayTopRanksMenu(Database db, DBResultSet results, const char[] error, DataPack data)
2439{
2440 if (results == null)
2441 {
2442 delete data;
2443 ThrowError("Error on pulling ranks data to show top menu: %s", error);
2444 }
2445
2446 data.Reset();
2447
2448 int client = GetClientOfUserId(data.ReadCell());
2449 int amount = data.ReadCell();
2450
2451 delete data;
2452
2453 if (client == 0)
2454 return;
2455
2456 Menu menu = new Menu(MenuHandle_DisplayTopRanksMenu);
2457 menu.SetTitle("Top %i ranks:", amount);
2458
2459 while (results.FetchRow())
2460 {
2461 char sName[MAX_NAME_LENGTH];
2462 results.FetchString(0, sName, sizeof(sName));
2463
2464 char sSteamID2[64];
2465 results.FetchString(1, sSteamID2, sizeof(sSteamID2));
2466
2467 float fKDR = results.FetchFloat(2);
2468 int iPoints = results.FetchInt(3);
2469
2470 AddMenuItemFormat(menu, sSteamID2, ITEMDRAW_DEFAULT, "%s - Points: %i (KDR %.2f)", sName, iPoints, fKDR);
2471 }
2472
2473 menu.Display(client, MENU_TIME_FOREVER);
2474}
2475
2476public int MenuHandle_DisplayTopRanksMenu(Menu menu, MenuAction action, int param1, int param2)
2477{
2478 switch (action)
2479 {
2480 case MenuAction_Select:
2481 {
2482 if (!convar_Status.BoolValue)
2483 return;
2484
2485 char sInfo[64];
2486 menu.GetItem(param2, sInfo, sizeof(sInfo));
2487
2488 ShowStatisticsMenu(param1, sInfo);
2489 }
2490
2491 case MenuAction_End:
2492 {
2493 delete menu;
2494 }
2495 }
2496}
2497
2498public Action Command_TimePlayed(int client, int args)
2499{
2500 if (!convar_Status.BoolValue || client == 0)
2501 return Plugin_Handled;
2502
2503 int time = GetTime();
2504
2505 if (g_iCooldown[client] > time)
2506 {
2507 CPrintToChat(client, "Please wait a couple seconds before using this command again.");
2508 return Plugin_Handled;
2509 }
2510
2511 if (args == 0)
2512 {
2513 float session = GetClientTime(client);
2514 float total_time = g_fStatistics_Global_Playtime[client] + session;
2515 float seasion_time = g_fStatistics_Seasonal_Playtime[client] + session;
2516
2517 char sSeasonTime[128];
2518 FormatSeconds(total_time, sSeasonTime, sizeof(sSeasonTime), "%D days %H hours %M minutes");
2519
2520 char sTotalTime[128];
2521 FormatSeconds(seasion_time, sTotalTime, sizeof(sTotalTime), "%D days %H hours %M minutes");
2522
2523 CPrintToChatAll("%t", "time played", client, sSeasonTime, g_iSeason, sTotalTime);
2524 g_iCooldown[client] = time + 3;
2525
2526 return Plugin_Handled;
2527 }
2528
2529 char sTarget[MAX_TARGET_LENGTH];
2530 GetCmdArgString(sTarget, sizeof(sTarget));
2531 int target = FindTargetEx(client, sTarget, true, false);
2532
2533 if (target > 0)
2534 {
2535 float session = GetClientTime(target);
2536 float total_time = g_fStatistics_Global_Playtime[target] + session;
2537 float season_time = g_fStatistics_Seasonal_Playtime[target] + session;
2538
2539 char sTotalTime[128];
2540 FormatSeconds(total_time, sTotalTime, sizeof(sTotalTime), "%D days %H hours %M minutes");
2541
2542 char sSeasonTime[128];
2543 FormatSeconds(season_time, sSeasonTime, sizeof(sSeasonTime), "%D days %H hours %M minutes");
2544
2545 CPrintToChatAll("%t", "time played", target, sSeasonTime, g_iSeason, sTotalTime);
2546 g_iCooldown[client] = time + 3;
2547
2548 return Plugin_Handled;
2549 }
2550
2551 g_Cache_GlobalPlaytime[client] = 0.0;
2552 g_Cache_ServerPlaytime[client] = 0.0;
2553 g_Cache_Playtimes[client] = 0;
2554
2555 char sSearch[512];
2556
2557 if (IsStringNumeric(sTarget))
2558 FormatEx(sSearch, sizeof(sSearch), "`%s` = '%i'", strlen(sTarget) > 10 ? "steamid64" : "accountid", StringToInt(sTarget));
2559 else if (StrContains(sTarget, "STEAM_", false) == 0)
2560 FormatEx(sSearch, sizeof(sSearch), "`steamid2` = '%s'", sTarget);
2561 else
2562 FormatEx(sSearch, sizeof(sSearch), "`name` = '%s'", sTarget);
2563
2564 char sTable[MAX_TABLE_SIZE];
2565 char sQuery[MAX_QUERY_SIZE];
2566
2567 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
2568 g_Database_Global.Format(sQuery, sizeof(sQuery), "SELECT `name`, `playtime` FROM `%s` WHERE %s;", sTable, sSearch);
2569 g_Database_Global.Query(TQuery_OnParsePlaytime, sQuery, GetClientUserId(client));
2570
2571 GetTableString_Season(sTable, sizeof(sTable));
2572 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT `name`, `playtime` FROM `%s` WHERE %s;", sTable, sSearch);
2573 g_Database_Server.Query(TQuery_OnParsePlaytime, sQuery, GetClientUserId(client));
2574
2575 g_iCooldown[client] = time + 3;
2576
2577 return Plugin_Handled;
2578}
2579
2580public void TQuery_OnParsePlaytime(Database db, DBResultSet results, const char[] error, any data)
2581{
2582 if (results == null)
2583 {
2584 int client;
2585 if ((client = GetClientOfUserId(data)) > 0)
2586 CPrintToChat(client, "%T", "profile target not found", client);
2587
2588 ThrowError("Error displaying target playtime to client: %s", error);
2589 }
2590
2591 int client;
2592 if ((client = GetClientOfUserId(data)) == 0)
2593 return;
2594
2595 if (g_Cache_Playtimes[client] == -1)
2596 {
2597 g_Cache_Playtimes[client] = 0;
2598 return;
2599 }
2600
2601 if (results.RowCount == 0)
2602 {
2603 CPrintToChat(client, "%T", "profile target not found", client);
2604 g_Cache_Playtimes[client] = -1;
2605 return;
2606 }
2607
2608 if (results.FetchRow())
2609 {
2610 char sName[MAX_NAME_LENGTH];
2611 results.FetchString(0, sName, sizeof(sName));
2612
2613 float playtime = results.FetchFloat(1);
2614
2615 if (g_Cache_Playtimes[client] == 0)
2616 g_Cache_GlobalPlaytime[client] = playtime;
2617 else if (g_Cache_Playtimes[client] == 1)
2618 g_Cache_ServerPlaytime[client] = playtime;
2619
2620 g_Cache_Playtimes[client]++;
2621
2622 if (g_Cache_Playtimes[client] >= 2)
2623 {
2624 char sTotalTime[128];
2625 FormatSeconds(g_Cache_GlobalPlaytime[client], sTotalTime, sizeof(sTotalTime), "%D days %H hours %M minutes");
2626
2627 char sSeasonTime[128];
2628 FormatSeconds(g_Cache_ServerPlaytime[client], sSeasonTime, sizeof(sSeasonTime), "%D days %H hours %M minutes");
2629
2630 CPrintToChatAll("%t", "time played", sName, sSeasonTime, g_iSeason, sTotalTime);
2631
2632 g_Cache_GlobalPlaytime[client] = 0.0;
2633 g_Cache_ServerPlaytime[client] = 0.0;
2634 g_Cache_Playtimes[client] = 0;
2635 }
2636 }
2637}
2638
2639public Action Command_OpenWeaponsMenu(int client, int args)
2640{
2641 if (!convar_Status.BoolValue || client == 0)
2642 return Plugin_Handled;
2643
2644 char sTable[MAX_TABLE_SIZE];
2645 GetTableString_Season(sTable, sizeof(sTable));
2646
2647 char sQuery[MAX_QUERY_SIZE];
2648 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT `name`, `weapons_statistics` FROM `%s` WHERE `accountid` = '%i';", sTable, GetSteamAccountID(client));
2649 g_Database_Server.Query(TQuery_PullWeaponStatistics, sQuery, GetClientUserId(client));
2650
2651 return Plugin_Handled;
2652}
2653
2654public void TQuery_PullWeaponStatistics(Database db, DBResultSet results, const char[] error, any data)
2655{
2656 if (results == null)
2657 ThrowError("Error displaying weapon statistics to client: %s", error);
2658
2659 int client;
2660 if ((client = GetClientOfUserId(data)) > 0 && results.FetchRow())
2661 {
2662 char sName[MAX_NAME_LENGTH];
2663 results.FetchString(0, sName, sizeof(sName));
2664
2665 char sWeaponsData[WEAPON_STATISTICS_SIZE];
2666 results.FetchString(1, sWeaponsData, sizeof(sWeaponsData));
2667
2668 DisplayWeaponStats(client, sName, sWeaponsData);
2669 }
2670}
2671
2672public Action Command_SwitchToSpectate(int client, int args)
2673{
2674 if (!convar_Status.BoolValue || client == 0)
2675 return Plugin_Handled;
2676
2677 if (IsClientInGame(client) && GetClientTeam(client) != CS_TEAM_SPECTATOR)
2678 {
2679 ChangeClientTeam(client, CS_TEAM_SPECTATOR);
2680 CPrintToChat(client, "%T", "moved to spectate", client);
2681
2682 if (GetAlivePlayers(GetClientTeam(client)) == 1 && g_bActiveSeason)
2683 {
2684 g_iStatistics_Seasonal_Points[client] -= g_iCacheData_PointsLoss[client];
2685
2686 g_iStatistics_Mapbased_Points[client] -= g_iCacheData_PointsLoss[client];
2687
2688 g_iStatistics_Sessionbased_PointsLost[client] += g_iCacheData_PointsLoss[client];
2689 }
2690 }
2691
2692 return Plugin_Handled;
2693}
2694
2695int GetAlivePlayers(int team = 0)
2696{
2697 int count;
2698
2699 for (int i = 1; i <= MaxClients; i++)
2700 {
2701 if (!IsClientInGame(i) || !IsPlayerAlive(i) || (team > 0 && GetClientTeam(i) != team))
2702 continue;
2703
2704 count++;
2705 }
2706
2707 return count;
2708}
2709
2710void ParseRanksConfig()
2711{
2712 char sConfig[PLATFORM_MAX_PATH];
2713 convar_Config_Ranks.GetString(sConfig, sizeof(sConfig));
2714
2715 char sPath[PLATFORM_MAX_PATH];
2716 BuildPath(Path_SM, sPath, sizeof(sPath), sConfig);
2717
2718 KeyValues kv = new KeyValues("furious_ranks");
2719
2720 if (kv.ImportFromFile(sPath) && kv.GotoFirstSubKey())
2721 {
2722 ClearTrieSafe(g_RanksData);
2723 g_RanksList.Clear();
2724
2725 char sRank[64]; char sKey[64]; char sValue[PLATFORM_MAX_PATH];
2726 do
2727 {
2728 kv.GetSectionName(sRank, sizeof(sRank));
2729
2730 if (strlen(sRank) > 0 && kv.GotoFirstSubKey(false))
2731 {
2732 StringMap local_trie = new StringMap();
2733
2734 do
2735 {
2736 kv.GetSectionName(sKey, sizeof(sKey));
2737 kv.GetString(NULL_STRING, sValue, sizeof(sValue));
2738
2739 local_trie.SetString(sKey, sValue);
2740
2741 if (StrEqual(sKey, "sound") && strlen(sValue) > 0)
2742 {
2743 PrecacheSound(sValue);
2744
2745 if (StrContains(sValue, "sound/") != 0)
2746 Format(sValue, sizeof(sValue), "sound/%s", sValue);
2747
2748 AddFileToDownloadsTable(sValue);
2749 }
2750
2751 }
2752 while (kv.GotoNextKey(false));
2753
2754 g_RanksData.SetValue(sRank, local_trie);
2755 g_RanksList.Push(StringToInt(sRank));
2756
2757 kv.GoBack();
2758 }
2759 }
2760 while (kv.GotoNextKey());
2761 }
2762
2763 LogMessage("Ranks config parsed. [%i sections loaded]", g_RanksList.Length);
2764 delete kv;
2765}
2766
2767void SaveDeathStatistics(int client)
2768{
2769 if (!convar_Status.BoolValue || client == 0 || !IsClientInGame(client) || IsFakeClient(client))
2770 return;
2771
2772 char sTable[MAX_TABLE_SIZE];
2773 char sQuery[MAX_QUERY_SIZE];
2774 Transaction trans = new Transaction();
2775
2776 int iAccountID = GetSteamAccountID(client);
2777
2778 GetTableString_Season(sTable, sizeof(sTable));
2779 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `deaths` = '%i' WHERE `accountid` = '%i';", sTable, g_iStatistics_Seasonal_Deaths[client], iAccountID);
2780 trans.AddQuery(sQuery);
2781
2782 GetTableString_Maps(sTable, sizeof(sTable));
2783 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `deaths` = '%i' WHERE `accountid` = '%i';", sTable, g_iStatistics_Mapbased_Deaths[client], iAccountID);
2784 trans.AddQuery(sQuery);
2785
2786 g_Database_Server.Execute(trans, Transaction_OnUpdateClientDeaths_Success, Transaction_OnUpdateClientDeaths_Failure);
2787}
2788
2789public void Transaction_OnUpdateClientDeaths_Success(Database db, any data, int numQueries, DBResultSet[] results, any[] queryData)
2790{
2791
2792}
2793
2794public void Transaction_OnUpdateClientDeaths_Failure(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData)
2795{
2796 ThrowError("Error updating client deaths: [%i] %s", failIndex, error);
2797}
2798
2799void UpdateClientPositions(int client, bool bOverlay = true)
2800{
2801 if (!convar_Status.BoolValue || client == 0 || !IsClientInGame(client) || IsFakeClient(client))
2802 return;
2803
2804 char sTable[MAX_TABLE_SIZE];
2805 GetTableString_Season(sTable, sizeof(sTable));
2806
2807 int iAccountID = GetSteamAccountID(client);
2808
2809 char sQuery[MAX_QUERY_SIZE];
2810 Transaction trans = new Transaction();
2811
2812 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `kills` = '%i', `points` = '%i', `kdr` = '%f' WHERE `accountid` = '%i';",
2813 sTable,
2814 g_iStatistics_Seasonal_Kills[client],
2815 g_iStatistics_Seasonal_Points[client],
2816 g_fStatistics_Seasonal_KDR[client],
2817 iAccountID);
2818 trans.AddQuery(sQuery);
2819
2820 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT s.points, (SELECT COUNT(*) as rank FROM `%s` as r WHERE r.points > s.points OR (r.points = s.points AND r.kills > s.kills)) + 1 as rank, (SELECT COUNT(*) as total FROM `%s`) as total FROM `%s` as s WHERE s.accountid = '%i';",
2821 sTable,
2822 sTable,
2823 sTable,
2824 iAccountID);
2825 trans.AddQuery(sQuery);
2826
2827 DataPack dPack = new DataPack();
2828 WritePackCell(dPack, GetClientUserId(client));
2829 WritePackCell(dPack, bOverlay);
2830
2831 g_Database_Server.Execute(trans, Transaction_OnUpdateClientRank_Success, Transaction_OnUpdateClientRank_Failure, dPack);
2832}
2833
2834public void Transaction_OnUpdateClientRank_Success(Database db, DataPack data, int numQueries, DBResultSet[] results, any[] queryData)
2835{
2836 data.Reset();
2837
2838 int client = GetClientOfUserId(data.ReadCell());
2839 bool bOverlay = view_as<bool>(data.ReadCell());
2840
2841 delete data;
2842
2843 if (client == 0)
2844 return;
2845
2846 if (results[1] != null && results[1].FetchRow())
2847 {
2848 int iPoints = results[1].FetchInt(0);
2849 int iCurrentRank = results[1].FetchInt(1);
2850 g_iCachedPlayers = results[1].FetchInt(2);
2851
2852 StringMap overlay_data;
2853 CalculateRank(iCurrentRank, overlay_data);
2854
2855 if (bOverlay && iCurrentRank < g_iCacheData_Rank[client])
2856 {
2857 if (overlay_data != null)
2858 ShowClientOverlay(client, OVERLAY_RANKUP, 3.0, overlay_data, iCurrentRank);
2859 else
2860 LogError("Invalid Overlay data handle for client %N.", client);
2861 }
2862
2863 g_iCacheData_Rank[client] = iCurrentRank;
2864
2865 overlay_data = null;
2866 int iNewTier = CalculateTier(iPoints, overlay_data);
2867
2868 if (g_iCacheData_Tier[client] < iNewTier)
2869 {
2870 g_iCacheData_Tier[client] = iNewTier;
2871
2872 if (overlay_data != null)
2873 {
2874 char sTag[128];
2875 GetTrieString(overlay_data, "tag", sTag, sizeof(sTag));
2876 strcopy(g_sCacheData_TierTag[client], 512, sTag);
2877
2878 char sPointsPerKill[PLATFORM_MAX_PATH];
2879 GetTrieString(overlay_data, "points_per_kill", sPointsPerKill, sizeof(sPointsPerKill));
2880
2881 char sPointsPerDeath[PLATFORM_MAX_PATH];
2882 GetTrieString(overlay_data, "points_per_death", sPointsPerDeath, sizeof(sPointsPerDeath));
2883
2884 g_iCacheData_PointsGain[client] = StringToInt(sPointsPerKill);
2885 g_iCacheData_PointsLoss[client] = StringToInt(sPointsPerDeath);
2886
2887 CPrintToChat(client, "%T", "tier up", client, g_iCacheData_Tier[client], g_iCacheData_PointsGain[client], g_iCacheData_PointsLoss[client]);
2888
2889 if (bOverlay)
2890 ShowClientOverlay(client, OVERLAY_TIERUP, 3.0, overlay_data, g_iCacheData_Tier[client]);
2891 }
2892 }
2893 else if (g_iCacheData_Tier[client] > iNewTier)
2894 {
2895 g_iCacheData_Tier[client] = iNewTier;
2896
2897 if (overlay_data != null)
2898 {
2899 char sTag[128];
2900 GetTrieString(overlay_data, "tag", sTag, sizeof(sTag));
2901 strcopy(g_sCacheData_TierTag[client], 512, sTag);
2902
2903 char sPointsPerKill[PLATFORM_MAX_PATH];
2904 GetTrieString(overlay_data, "points_per_kill", sPointsPerKill, sizeof(sPointsPerKill));
2905
2906 char sPointsPerDeath[PLATFORM_MAX_PATH];
2907 GetTrieString(overlay_data, "points_per_death", sPointsPerDeath, sizeof(sPointsPerDeath));
2908
2909 g_iCacheData_PointsGain[client] = StringToInt(sPointsPerKill);
2910 g_iCacheData_PointsLoss[client] = StringToInt(sPointsPerDeath);
2911
2912 CPrintToChat(client, "%T", "tier down", client, g_iCacheData_Tier[client], g_iCacheData_PointsGain[client], g_iCacheData_PointsLoss[client]);
2913 }
2914 }
2915 }
2916}
2917
2918public void Transaction_OnUpdateClientRank_Failure(Database db, DataPack data, int numQueries, const char[] error, int failIndex, any[] queryData)
2919{
2920 delete data;
2921 ThrowError("Error updating client ranking and tier: [%i] %s", failIndex, error);
2922}
2923
2924void ShowClientOverlay(int client, int overlay, float timer = 3.0, StringMap local_trie = null, int data = -1)
2925{
2926 char sOverlay[PLATFORM_MAX_PATH];
2927 char sTextCenter[256];
2928 char sTextHint[256];
2929 char sTextPrint[256];
2930 char sSound[PLATFORM_MAX_PATH];
2931
2932 switch (overlay)
2933 {
2934 case OVERLAY_RAMPAGE:
2935 {
2936 convar_Rampage_Overlay.GetString(sOverlay, sizeof(sOverlay));
2937 FormatEx(sTextHint, sizeof(sTextHint), "%t", "rampage center text", client);
2938 FormatEx(sTextPrint, sizeof(sTextPrint), "%t", "rampage on", client);
2939 convar_Sound_Rampage.GetString(sSound, sizeof(sSound));
2940 }
2941
2942 case OVERLAY_RANKUP:
2943 {
2944 if (local_trie == null)
2945 ThrowError("Error showing rankup overlay: invalid local handle");
2946
2947 local_trie.GetString("overlay", sOverlay, sizeof(sOverlay));
2948 local_trie.GetString("text_center", sTextCenter, sizeof(sTextCenter));
2949 local_trie.GetString("text_hint", sTextHint, sizeof(sTextHint));
2950 local_trie.GetString("text_print", sTextPrint, sizeof(sTextPrint));
2951 local_trie.GetString("sound", sSound, sizeof(sSound));
2952
2953 if (data != -1)
2954 {
2955 char sRank[32];
2956 IntToString(data, sRank, sizeof(sRank));
2957 ReplaceString(sTextCenter, sizeof(sTextCenter), "{RANK}", sRank);
2958 ReplaceString(sTextHint, sizeof(sTextHint), "{RANK}", sRank);
2959 ReplaceString(sTextPrint, sizeof(sTextPrint), "{RANK}", sRank);
2960 }
2961 }
2962
2963 case OVERLAY_TIERUP:
2964 {
2965 if (local_trie == null)
2966 ThrowError("Error showing tierup overlay: invalid local handle");
2967
2968 local_trie.GetString("overlay", sOverlay, sizeof(sOverlay));
2969 local_trie.GetString("text_center", sTextCenter, sizeof(sTextCenter));
2970 local_trie.GetString("text_hint", sTextHint, sizeof(sTextHint));
2971 local_trie.GetString("text_print", sTextPrint, sizeof(sTextPrint));
2972 local_trie.GetString("sound", sSound, sizeof(sSound));
2973
2974 if (data != -1)
2975 {
2976 char sTier[32];
2977 IntToString(data, sTier, sizeof(sTier));
2978 ReplaceString(sTextCenter, sizeof(sTextCenter), "{TIER}", sTier);
2979 ReplaceString(sTextHint, sizeof(sTextHint), "{TIER}", sTier);
2980 ReplaceString(sTextPrint, sizeof(sTextPrint), "{TIER}", sTier);
2981 }
2982 }
2983 }
2984
2985 DataPack pack = new DataPack();
2986 CreateDataTimer(timer, Timer_DisplayOverlayToClient, pack, TIMER_FLAG_NO_MAPCHANGE);
2987 WritePackCell(pack, GetClientUserId(client));
2988 WritePackString(pack, sOverlay);
2989 WritePackString(pack, sTextCenter);
2990 WritePackString(pack, sTextHint);
2991 WritePackString(pack, sTextPrint);
2992 WritePackString(pack, sSound);
2993}
2994
2995public Action Timer_DisplayOverlayToClient(Handle timer, DataPack data)
2996{
2997 data.Reset();
2998
2999 int client = GetClientOfUserId(data.ReadCell());
3000
3001 char sOverlay[PLATFORM_MAX_PATH];
3002 data.ReadString(sOverlay, sizeof(sOverlay));
3003
3004 char sTextCenter[PLATFORM_MAX_PATH];
3005 data.ReadString(sTextCenter, sizeof(sTextCenter));
3006
3007 char sTextHint[PLATFORM_MAX_PATH];
3008 data.ReadString(sTextHint, sizeof(sTextHint));
3009
3010 char sTextPrint[PLATFORM_MAX_PATH];
3011 data.ReadString(sTextPrint, sizeof(sTextPrint));
3012
3013 char sSound[PLATFORM_MAX_PATH];
3014 data.ReadString(sSound, sizeof(sSound));
3015
3016 if (client > 0 && IsClientInGame(client))
3017 {
3018 if (strlen(sOverlay) > 0)
3019 {
3020 int iFlags = GetCommandFlags("r_screenoverlay");
3021 SetCommandFlags("r_screenoverlay", iFlags & ~FCVAR_CHEAT);
3022 ClientCommand(client, "r_screenoverlay \"%s\"", sOverlay);
3023 SetCommandFlags("r_screenoverlay", iFlags);
3024
3025 CreateTimer(3.0, Timer_ResetOverlayForClient, GetClientUserId(client), TIMER_FLAG_NO_MAPCHANGE);
3026 }
3027
3028 if (strlen(sTextCenter) > 0)
3029 PrintCenterText(client, sTextCenter);
3030
3031 if (strlen(sTextHint) > 0)
3032 PrintHintText(client, sTextHint);
3033
3034 if (strlen(sTextPrint) > 0)
3035 CPrintToChat(client, sTextPrint);
3036
3037 if (strlen(sSound) > 0 && IsSoundPrecached(sSound))
3038 EmitSoundToClient(client, sSound);
3039 }
3040}
3041
3042void ParseTiersConfig()
3043{
3044 char sConfig[PLATFORM_MAX_PATH];
3045 convar_Config_Tiers.GetString(sConfig, sizeof(sConfig));
3046
3047 char sPath[PLATFORM_MAX_PATH];
3048 BuildPath(Path_SM, sPath, sizeof(sPath), sConfig);
3049
3050 KeyValues kv = new KeyValues("furious_tiers");
3051 int sections;
3052
3053 if (kv.ImportFromFile(sPath) && kv.GotoFirstSubKey())
3054 {
3055 ClearTrieSafe(g_TiersData);
3056 g_TiersList.Clear();
3057
3058 char sRequiredPoints[64]; StringMap local_trie; char sKey[64]; char sValue[PLATFORM_MAX_PATH];
3059 do
3060 {
3061 kv.GetSectionName(sRequiredPoints, sizeof(sRequiredPoints));
3062
3063 if (strlen(sRequiredPoints) > 0 && kv.GotoFirstSubKey(false))
3064 {
3065 local_trie = new StringMap();
3066
3067 do
3068 {
3069 kv.GetSectionName(sKey, sizeof(sKey));
3070 kv.GetString(NULL_STRING, sValue, sizeof(sValue));
3071
3072 local_trie.SetString(sKey, sValue);
3073
3074 if (StrEqual(sKey, "sound") && strlen(sValue) > 0)
3075 {
3076 PrecacheSound(sValue);
3077
3078 if (StrContains(sValue, "sound/") != 0)
3079 Format(sValue, sizeof(sValue), "sound/%s", sValue);
3080
3081 AddFileToDownloadsTable(sValue);
3082 }
3083
3084 }
3085 while (kv.GotoNextKey(false));
3086
3087 g_TiersData.SetValue(sRequiredPoints, local_trie);
3088 g_TiersList.Push(StringToInt(sRequiredPoints));
3089
3090 kv.GoBack();
3091 }
3092
3093 sections++;
3094 }
3095 while (kv.GotoNextKey());
3096 }
3097
3098 LogMessage("Tiers config parsed. [%i sections loaded]", sections);
3099 delete kv;
3100}
3101
3102void CalculateRank(int rank, Handle& overlay_data)
3103{
3104 char sRank[12];
3105 IntToString(rank, sRank, sizeof(sRank));
3106 g_RanksData.GetValue(sRank, overlay_data);
3107
3108 if (overlay_data == null)
3109 g_RanksData.GetValue("default", overlay_data);
3110}
3111
3112int CalculateTier(int points, Handle& overlay_data)
3113{
3114 int iTier = -1;
3115 int iRequiredPoints;
3116
3117 for (int i = 0; i < g_TiersList.Length; i++)
3118 {
3119 iRequiredPoints = g_TiersList.Get(i);
3120
3121 if (points >= iRequiredPoints)
3122 iTier = iRequiredPoints;
3123 }
3124
3125 char sTier[12];
3126 IntToString(iTier, sTier, sizeof(sTier));
3127
3128 g_TiersData.GetValue(sTier, overlay_data);
3129
3130 return iTier;
3131}
3132
3133public Action Command_PrintRankInfo(int client, int args)
3134{
3135 if (!convar_Status.BoolValue || client == 0)
3136 return Plugin_Handled;
3137
3138 char sCommand[64];
3139 GetCmdArg(0, sCommand, sizeof(sCommand));
3140
3141 bool bIsPlace = StrContains(sCommand, "place") != -1;
3142
3143 char sSearch[64];
3144 GetCmdArg(1, sSearch, sizeof(sSearch));
3145
3146 if (args == 0 || strlen(sSearch) == 0)
3147 GetClientAuthId(client, AuthId_Steam2, sSearch, sizeof(sSearch));
3148
3149 int size = 2 * strlen(sSearch) + 1;
3150 char[] sSearch2 = new char[size];
3151 g_Database_Server.Escape(sSearch, sSearch2, size);
3152
3153 bool bIsSteamID = StrContains(sSearch2, "STEAM_") != -1;
3154
3155 DataPack pack = new DataPack();
3156 pack.WriteCell(GetClientUserId(client));
3157 pack.WriteCell(bIsPlace);
3158 pack.WriteCell(bIsSteamID);
3159 pack.WriteString(sSearch2);
3160
3161 char sTable[MAX_TABLE_SIZE];
3162 GetTableString_Season(sTable, sizeof(sTable));
3163
3164 char sSearchBuffer[128];
3165 FormatEx(sSearchBuffer, sizeof(sSearchBuffer), bIsSteamID ? "s.steamid2 = '%s'" : "s.name LIKE '%%%s%%'", sSearch2);
3166
3167 Transaction trans = new Transaction();
3168
3169 for (int i = 1; i <= MaxClients; i++)
3170 {
3171 if (!IsClientInGame(i) || IsFakeClient(i))
3172 continue;
3173
3174 SaveClientServerData(i, trans);
3175 }
3176
3177 char sQuery[MAX_QUERY_SIZE];
3178 Format(sQuery, sizeof(sQuery), "SELECT s.name, s.kills, s.deaths, s.points, s.kdr, (SELECT COUNT(*) as rank FROM `%s` as r WHERE r.points > s.points OR (r.points = s.points AND r.kills > s.kills)) + 1 as rank, (SELECT COUNT(*) as total FROM `%s`) as total FROM `%s` as s WHERE %s;",
3179 sTable,
3180 sTable,
3181 sTable,
3182 sSearchBuffer);
3183 trans.AddQuery(sQuery, pack);
3184
3185 g_Database_Server.Execute(trans, onSuccess_ShowRank, onFailed_ShowRank);
3186
3187 return Plugin_Handled;
3188}
3189
3190public void onSuccess_ShowRank(Database db, any data, int numQueries, DBResultSet[] results, DataPack[] queryData)
3191{
3192 int check = numQueries - 1;
3193
3194 queryData[check].Reset();
3195
3196 int client = GetClientOfUserId(queryData[check].ReadCell());
3197 bool bIsPlace = view_as<bool>(queryData[check].ReadCell());
3198 bool bIsSteamID = view_as<bool>(queryData[check].ReadCell());
3199
3200 char sSearch[64];
3201 queryData[check].ReadString(sSearch, sizeof(sSearch));
3202
3203 delete queryData[check];
3204
3205 if (client == 0)
3206 return;
3207
3208 if (results[check] == null)
3209 {
3210 CPrintToChat(client, "%T", bIsSteamID ? "steamid not in database" : "player not found", client, sSearch);
3211 return;
3212 }
3213
3214 if (results[check].RowCount > 1)
3215 {
3216 CPrintToChat(client, "%T", "found more than one", client, sSearch);
3217 return;
3218 }
3219
3220 char sName[MAX_NAME_LENGTH]; int iKills; int iDeaths; int iPoints; float fKDR; int iRank; int iTotal;
3221 if (results[check].FetchRow())
3222 {
3223 results[check].FetchString(0, sName, sizeof(sName));
3224 iKills = results[check].FetchInt(1);
3225 iDeaths = results[check].FetchInt(2);
3226 iPoints = results[check].FetchInt(3);
3227 fKDR = results[check].FetchFloat(4);
3228 iRank = results[check].FetchInt(5);
3229 iTotal = results[check].FetchInt(6);
3230
3231 if (iRank == 0)
3232 iRank = 1;
3233 }
3234
3235 StringMap overlay_data;
3236 CalculateTier(iPoints, overlay_data);
3237
3238 char sTag[128];
3239 if (overlay_data != null)
3240 overlay_data.GetString("tag", sTag, sizeof(sTag));
3241
3242 switch (bIsPlace)
3243 {
3244 case true:
3245 {
3246 CPrintToChatAll("%t", "rank message 1", sName, iRank, iTotal, strlen(sTag) > 0 ? sTag : "N/A", iPoints);
3247 CPrintToChatAll("%t", "rank message 2", iKills, iDeaths, fKDR);
3248 }
3249 case false:
3250 {
3251 CPrintToChat(client, "%T", "rank message 1", client, sName, iRank, iTotal, strlen(sTag) > 0 ? sTag : "N/A", iPoints);
3252 CPrintToChat(client, "%T", "rank message 2", client, iKills, iDeaths, fKDR);
3253 }
3254 }
3255}
3256
3257public void onFailed_ShowRank(Database db, any data, int numQueries, const char[] error, int failIndex, any[] queryData)
3258{
3259 LogError("Error while showing rank at query %i: %s", failIndex, error);
3260}
3261
3262public Action Command_Next(int client, int args)
3263{
3264 if (!convar_Status.BoolValue || client == 0)
3265 return Plugin_Handled;
3266
3267 int iAmount = 10;
3268
3269 if (args > 0)
3270 {
3271 char sAmount[12];
3272 GetCmdArgString(sAmount, sizeof(sAmount));
3273 iAmount = StringToInt(sAmount);
3274
3275 if (iAmount <= 0 || iAmount > 10)
3276 iAmount = 10;
3277 }
3278
3279 char sTable[MAX_TABLE_SIZE];
3280 GetTableString_Season(sTable, sizeof(sTable));
3281
3282 DataPack pack = new DataPack();
3283 pack.WriteCell(GetClientUserId(client));
3284 pack.WriteCell(iAmount);
3285
3286 char sQuery[MAX_QUERY_SIZE];
3287 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT s.points, (SELECT COUNT(*) as rank FROM `%s` as r WHERE r.points > s.points OR (r.points = s.points AND r.kills > s.kills)) + 1 as rank, (SELECT COUNT(*) as total FROM `%s`) as total FROM `%s` as s WHERE s.accountid = '%i' LIMIT %i;", sTable, sTable, sTable, GetSteamAccountID(client), iAmount);
3288 g_Database_Server.Query(TQuery_OnGetNextClientData, sQuery, pack);
3289
3290 return Plugin_Handled;
3291}
3292
3293public void TQuery_OnGetNextClientData(Database db, DBResultSet results, const char[] error, DataPack data)
3294{
3295 if (results == null)
3296 {
3297 delete data;
3298 ThrowError("Error pulling client next data: %s", error);
3299 }
3300
3301 data.Reset();
3302
3303 int client = GetClientOfUserId(data.ReadCell());
3304 int iAmount = data.ReadCell();
3305
3306 if (client > 0 && results.FetchRow())
3307 {
3308 int iClientPoints = results.FetchInt(0);
3309 int iClientRank = results.FetchInt(1);
3310
3311 data.WriteCell(iClientPoints);
3312 data.WriteCell(iClientRank);
3313
3314 char sTable[MAX_TABLE_SIZE];
3315 GetTableString_Season(sTable, sizeof(sTable));
3316
3317 char sQuery[MAX_QUERY_SIZE];
3318 g_Database_Server.Format(sQuery, sizeof(sQuery), "SELECT f.* FROM `%s` AS f, (SELECT `points` FROM `%s` WHERE `accountid` = '%i') AS s WHERE f.points >= s.points LIMIT %i;", sTable, sTable, GetSteamAccountID(client), iAmount);
3319 g_Database_Server.Query(TQuery_OnGetNextData, sQuery, data);
3320 }
3321 else
3322 delete data;
3323}
3324
3325public void TQuery_OnGetNextData(Database db, DBResultSet results, const char[] error, DataPack data)
3326{
3327 if (results == null)
3328 {
3329 delete data;
3330 ThrowError("Error pulling client next data: %s", error);
3331 }
3332
3333 data.Reset();
3334
3335 int client = GetClientOfUserId(data.ReadCell());
3336 int iAmount = data.ReadCell();
3337 int iClientPoints = data.ReadCell();
3338 int iClientRank = data.ReadCell();
3339
3340 delete data;
3341
3342 if (client == 0)
3343 return;
3344
3345 int iRows = results.RowCount;
3346
3347 if (iRows < iAmount)
3348 iAmount = iRows;
3349
3350 char sTitle[128];
3351 Format(sTitle, sizeof(sTitle), "%i - Next Players", iAmount);
3352
3353 Panel panel = new Panel();
3354 panel.SetTitle(sTitle);
3355
3356 int iInc;
3357 while (results.FetchRow())
3358 {
3359 char sName[MAX_NAME_LENGTH];
3360 results.FetchString(1, sName, sizeof(sName));
3361
3362 int iAccountID = results.FetchInt(2);
3363 int iPoints = results.FetchInt(10);
3364
3365 int iRank = iClientRank + iInc;
3366 int iPlus = iPoints - iClientPoints;
3367
3368 char sPlus[12];
3369 Format(sPlus, sizeof(sPlus), "+%i", iPlus);
3370
3371 char sDisplay[512];
3372 Format(sDisplay, sizeof(sDisplay), " %i %i %s %s", iRank, iPoints, GetSteamAccountID(client) != iAccountID ? sPlus : "-", sName);
3373
3374 panel.DrawText(sDisplay);
3375 iInc++;
3376 }
3377
3378 panel.DrawItem("exit");
3379 panel.Send(client, VoidPanel, MENU_TIME_FOREVER);
3380 delete panel;
3381}
3382
3383public int VoidPanel(Menu menu, MenuAction action, int param1, int param2)
3384{
3385 delete menu;
3386}
3387
3388public Action Command_PrintTiers(int client, int args)
3389{
3390 if (!convar_Status.BoolValue || client == 0)
3391 return Plugin_Handled;
3392
3393 int iTierPoints; char sTierPoints[12]; StringMap local_trie; char sTag[128]; char sPointsPerKill[PLATFORM_MAX_PATH]; char sPointsPerDeath[PLATFORM_MAX_PATH];
3394 for (int i = 0; i < g_TiersList.Length; i++)
3395 {
3396 iTierPoints = g_TiersList.Get(i);
3397
3398 IntToString(iTierPoints, sTierPoints, sizeof(sTierPoints));
3399 g_TiersData.GetValue(sTierPoints, local_trie);
3400
3401 if (local_trie == null)
3402 continue;
3403
3404 local_trie.GetString("tag", sTag, sizeof(sTag));
3405 local_trie.GetString("points_per_kill", sPointsPerKill, sizeof(sPointsPerKill));
3406 local_trie.GetString("points_per_death", sPointsPerDeath, sizeof(sPointsPerDeath));
3407
3408 CReplyToCommand(client, "%T", "tiers list format", client, sTag, iTierPoints, StringToInt(sPointsPerKill), StringToInt(sPointsPerDeath));
3409 }
3410
3411 return Plugin_Handled;
3412}
3413
3414public Action Command_Status(int client, int args)
3415{
3416 if (!convar_Status.BoolValue || client == 0)
3417 return Plugin_Handled;
3418
3419 if (IsClientInGame(client))
3420 {
3421 char sTable[MAX_TABLE_SIZE];
3422 convar_Table_GlobalStatistics.GetString(sTable, sizeof(sTable));
3423
3424 char sQuery[MAX_QUERY_SIZE];
3425 g_Database_Global.Format(sQuery, sizeof(sQuery), "SELECT COUNT(*) FROM `%s`;", sTable);
3426 g_Database_Global.Query(TQuery_OnGetGlobalCount, sQuery, GetClientUserId(client));
3427 }
3428
3429 return Plugin_Handled;
3430}
3431
3432public void TQuery_OnGetGlobalCount(Database db, DBResultSet results, const char[] error, any data)
3433{
3434 if (results == null)
3435 ThrowError("Error retrieving global player count: %s", error);
3436
3437 int client;
3438 if ((client = GetClientOfUserId(data)) == 0)
3439 return;
3440
3441 if (results.FetchRow())
3442 CPrintToChat(client, "%T", "status message", client, results.FetchInt(0));
3443}
3444
3445void GetTableString_Season(char[] sTable, int size)
3446{
3447 convar_Table_ServerSeasons.GetString(sTable, size);
3448 Format(sTable, size, "%s%i", sTable, g_iSeason);
3449}
3450
3451void GetTableString_Maps(char[] sTable, int size)
3452{
3453 convar_Table_ServerMaps.GetString(sTable, size);
3454 Format(sTable, size, "%s%i", sTable, g_iSeason);
3455}
3456
3457void GetTableString_Sessions(char[] sTable, int size)
3458{
3459 convar_Table_ServerSessions.GetString(sTable, size);
3460 Format(sTable, size, "%s%i", sTable, g_iSeason);
3461}
3462
3463public Action Timer_DisplaySpectatorHud(Handle timer, any userId)
3464{
3465 if (!convar_Status.BoolValue)
3466 {
3467 return Plugin_Continue;
3468 }
3469
3470 int client = GetClientOfUserId(userId);
3471
3472 if( client == 0 || !IsClientInGame(client) )
3473 {
3474 return Plugin_Continue;
3475 }
3476
3477 int specCount = 0;
3478 int observerMode; int spectated, i;
3479
3480 for (i = 1; i <= MaxClients; i++)
3481 {
3482 if( i == client )
3483 {
3484 continue;
3485 }
3486
3487 if (!IsClientInGame(i) || IsFakeClient(i) || !IsClientObserver(i) || !g_bSpecHud[i])
3488 continue;
3489
3490 observerMode = GetEntProp(i, Prop_Send, "m_iObserverMode");
3491
3492 if (observerMode != 4 && observerMode != 5)
3493 continue;
3494
3495 spectated = GetEntPropEnt(i, Prop_Send, "m_hObserverTarget");
3496
3497 if (spectated > 0 && spectated == client && IsPlayerAlive(client))
3498 {
3499 specCount++;
3500 }
3501 }
3502
3503 if( specCount > 0 && IsPlayerAlive(client) && Furious_VIP_IsSpecListEnabled(client) && CheckCommandAccess(client, "", ADMFLAG_CUSTOM5 | ADMFLAG_CUSTOM1 | ADMFLAG_CUSTOM2 | ADMFLAG_GENERIC | ADMFLAG_ROOT, true) )
3504 {
3505 static char buffer[512], name[32];
3506 int pos = 0;
3507
3508 for (i = 1; i <= MaxClients; i++)
3509 {
3510 if( i == client )
3511 {
3512 continue;
3513 }
3514
3515 if (!IsClientInGame(i) || IsFakeClient(i) || !IsClientObserver(i) || !g_bSpecHud[i])
3516 continue;
3517
3518 observerMode = GetEntProp(i, Prop_Send, "m_iObserverMode");
3519
3520 if (observerMode != 4 && observerMode != 5)
3521 continue;
3522
3523 spectated = GetEntPropEnt(i, Prop_Send, "m_hObserverTarget");
3524
3525 if (spectated > 0 && spectated == client )
3526 {
3527 GetClientName(i, name, sizeof(name));
3528 pos += Format(buffer[pos], sizeof(buffer) - pos, "%s. ", name);
3529 }
3530 }
3531
3532 PrintHintText(client, "%T", "spectator hud 2", client, buffer);
3533 }
3534
3535 float fTime = GetClientTime(client);
3536 float fTotal = g_fStatistics_Global_Playtime[client] + fTime;
3537
3538 char sTime[256];
3539 FormatSeconds(fTime, sTime, sizeof(sTime), "%Dd %Hh %Mm %Ss");
3540
3541 char sTotal[256];
3542 FormatSeconds(fTotal, sTotal, sizeof(sTotal), "%Dd %Hh %Mm");
3543
3544 char sPrefix[96]; char sPrefixColor[96]; char sGroup[96];
3545
3546 if (plugin_tags)
3547 {
3548 int index = Furious_Tags_GetPrefixID(client);
3549
3550 if (index > -1)
3551 {
3552 Furious_Tags_GetHudPrefix(index, sPrefix, sizeof(sPrefix));
3553 Furious_Tags_GetHudPrefixColor(index, sPrefixColor, sizeof(sPrefixColor));
3554 Furious_Tags_GetHudGroup(index, sGroup, sizeof(sGroup));
3555 }
3556 }
3557
3558 strcopy(sPrefixColor, sizeof(sPrefixColor), "#04ff00");
3559
3560 char sTag[512];
3561 FormatEx(sTag, sizeof(sTag), "<span color='%s'>%s%s</span>", sPrefixColor, sPrefix, sGroup);
3562
3563 for (i = 1; i <= MaxClients; i++)
3564 {
3565 if( i == client )
3566 {
3567 continue;
3568 }
3569
3570 if (!IsClientInGame(i) || IsFakeClient(i) || !IsClientObserver(i) || !g_bSpecHud[i])
3571 continue;
3572
3573 observerMode = GetEntProp(i, Prop_Send, "m_iObserverMode");
3574
3575 if (observerMode != 4 && observerMode != 5)
3576 continue;
3577
3578 spectated = GetEntPropEnt(i, Prop_Send, "m_hObserverTarget");
3579
3580 if( (spectated > 0 && spectated == client) && IsPlayerAlive(client) )
3581 {
3582 PrintHintText(i, "%T", "spectator hud", i, sTag, "", client, specCount, g_iCacheData_Rank[client], g_iCachedPlayers, g_iStatistics_Seasonal_Points[client], sTime, sTotal);
3583 }
3584 }
3585
3586 if( !g_HudSkipClient[client] && IsPlayerAlive(client) )
3587 {
3588 PrintHintText(client, "%T", "spectator hud", client, sTag, "", client, specCount, g_iCacheData_Rank[client], g_iCachedPlayers, g_iStatistics_Seasonal_Points[client], sTime, sTotal);
3589 }
3590
3591 return Plugin_Continue;
3592}
3593
3594public Action Command_PlaytimeDebug(int client, int args)
3595{
3596 if (!convar_Status.BoolValue || client == 0 || KillTimerSafe(g_hTimer_Playtime[client]))
3597 return Plugin_Handled;
3598
3599 g_hTimer_Playtime[client] = CreateTimer(1.0, Timer_DisplayPlaytimeDebug, GetClientUserId(client), TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
3600 return Plugin_Handled;
3601}
3602
3603public Action Timer_DisplayPlaytimeDebug(Handle timer, any data)
3604{
3605 int client;
3606 if ((client = GetClientOfUserId(data)) > 0 && IsClientInGame(client))
3607 {
3608 float fTime = GetClientTime(client);
3609 float fTotal = g_fStatistics_Global_Playtime[client] + fTime;
3610
3611 PrintHintText(client, "%.2f + %.2f = %.2f", g_fStatistics_Global_Playtime[client], fTime, fTotal);
3612 }
3613}
3614
3615public Action Command_SaveStats(int client, int args)
3616{
3617 if (!convar_Status.BoolValue || client == 0)
3618 return Plugin_Handled;
3619
3620 SaveClientGlobalData(client);
3621 SaveClientServerData(client);
3622
3623 PrintToChat(client, "Stats have been saved.");
3624
3625 return Plugin_Handled;
3626}
3627
3628public Action Command_ToggleRanked(int client, int args)
3629{
3630 if (!convar_Status.BoolValue || client == 0)
3631 return Plugin_Handled;
3632
3633 g_bRanked = !g_bRanked;
3634 AnnounceRankStatus(0, 0);
3635
3636 return Plugin_Handled;
3637}
3638
3639public Action Command_TestOverlays(int client, int args)
3640{
3641 if (!convar_Status.BoolValue || client == 0)
3642 return Plugin_Handled;
3643
3644 if (args == 0)
3645 {
3646 OpenTestOverlaysMenu(client);
3647 return Plugin_Handled;
3648 }
3649
3650 char sOverlay[12];
3651 GetCmdArg(1, sOverlay, sizeof(sOverlay));
3652 int overlay = StringToInt(sOverlay);
3653
3654 char sData[32];
3655 GetCmdArg(2, sData, sizeof(sData));
3656 int data = StringToInt(sData);
3657
3658 StringMap overlay_data;
3659 switch (overlay)
3660 {
3661 case OVERLAY_RANKUP:
3662 {
3663 CalculateRank(data, overlay_data);
3664
3665 if (overlay_data == null)
3666 {
3667 PrintToChat(client, "Error while pulling overlay data for the rank %i.", data);
3668 return Plugin_Handled;
3669 }
3670 }
3671 case OVERLAY_TIERUP:
3672 {
3673 CalculateTier(data, overlay_data);
3674
3675 if (overlay_data == null)
3676 {
3677 PrintToChat(client, "Error while pulling overlay data for the tier %i.", data);
3678 return Plugin_Handled;
3679 }
3680 }
3681 }
3682
3683 PrintToChat(client, "Showing overlay: %i", overlay);
3684 ShowClientOverlay(client, overlay, 0.1, overlay_data);
3685
3686 return Plugin_Handled;
3687}
3688
3689void OpenTestOverlaysMenu(int client)
3690{
3691 Menu menu = new Menu(MenuHandler_TestOverlays);
3692 menu.SetTitle("Pick a type:");
3693
3694 menu.AddItem("1", "Rampage");
3695 menu.AddItem("2", "Rankup");
3696 menu.AddItem("3", "Tierup");
3697
3698 menu.Display(client, MENU_TIME_FOREVER);
3699}
3700
3701public int MenuHandler_TestOverlays(Menu menu, MenuAction action, int param1, int param2)
3702{
3703 switch (action)
3704 {
3705 case MenuAction_Select:
3706 {
3707 char sOverlay[12];
3708 menu.GetItem(param2, sOverlay, sizeof(sOverlay));
3709 int overlay = StringToInt(sOverlay);
3710
3711 switch (overlay)
3712 {
3713 case OVERLAY_RAMPAGE:
3714 {
3715 ShowClientOverlay(param1, OVERLAY_RAMPAGE, 0.1);
3716 OpenTestOverlaysMenu(param1);
3717 }
3718 case OVERLAY_RANKUP: OpenTestOverlaysRankupMenu(param1);
3719 case OVERLAY_TIERUP: OpenTestOverlaysTierupMenu(param1);
3720 }
3721 }
3722 case MenuAction_End:
3723 delete menu;
3724 }
3725}
3726
3727void OpenTestOverlaysRankupMenu(int client)
3728{
3729 Menu menu = new Menu(MenuHandler_TestOverlays_Rankup);
3730 menu.SetTitle("Pick a rank ID:");
3731
3732 char sID[12];
3733 for (int i = 0; i < g_RanksList.Length; i++)
3734 {
3735 IntToString(g_RanksList.Get(i), sID, sizeof(sID));
3736 menu.AddItem(sID, sID);
3737 }
3738
3739 menu.ExitBackButton = true;
3740 menu.Display(client, MENU_TIME_FOREVER);
3741}
3742
3743public int MenuHandler_TestOverlays_Rankup(Menu menu, MenuAction action, int param1, int param2)
3744{
3745 switch (action)
3746 {
3747 case MenuAction_Select:
3748 {
3749 char sID[12];
3750 menu.GetItem(param2, sID, sizeof(sID));
3751 int rank = StringToInt(sID);
3752
3753 StringMap overlay_data;
3754 CalculateRank(rank, overlay_data);
3755
3756 if (overlay_data == null)
3757 {
3758 PrintToChat(param1, "Error while pulling overlay data for the rank %i.", rank);
3759 OpenTestOverlaysRankupMenu(param1);
3760 return;
3761 }
3762
3763 PrintToChat(param1, "Showing rankup overlay: %i", rank);
3764 ShowClientOverlay(param1, OVERLAY_RANKUP, 0.1, overlay_data);
3765
3766 OpenTestOverlaysRankupMenu(param1);
3767 }
3768 case MenuAction_Cancel:
3769 {
3770 if (param2 == MenuCancel_ExitBack)
3771 OpenTestOverlaysMenu(param1);
3772 }
3773 case MenuAction_End:
3774 delete menu;
3775 }
3776}
3777
3778void OpenTestOverlaysTierupMenu(int client)
3779{
3780 Menu menu = new Menu(MenuHandler_TestOverlays_Tierup);
3781 menu.SetTitle("Pick a Tier ID:");
3782
3783 char sID[12];
3784 for (int i = 0; i < g_TiersList.Length; i++)
3785 {
3786 IntToString(g_TiersList.Get(i), sID, sizeof(sID));
3787 menu.AddItem(sID, sID);
3788 }
3789
3790 menu.ExitBackButton = true;
3791 menu.Display(client, MENU_TIME_FOREVER);
3792}
3793
3794public int MenuHandler_TestOverlays_Tierup(Menu menu, MenuAction action, int param1, int param2)
3795{
3796 switch (action)
3797 {
3798 case MenuAction_Select:
3799 {
3800 char sID[12];
3801 menu.GetItem(param2, sID, sizeof(sID));
3802 int tier = StringToInt(sID);
3803
3804 StringMap overlay_data;
3805 CalculateTier(tier, overlay_data);
3806
3807 if (overlay_data == null)
3808 {
3809 PrintToChat(param1, "Error while pulling overlay data for the tier %i.", tier);
3810 OpenTestOverlaysTierupMenu(param1);
3811 return;
3812 }
3813
3814 PrintToChat(param1, "Showing tierup overlay: %i", tier);
3815 ShowClientOverlay(param1, OVERLAY_TIERUP, 0.1, overlay_data);
3816
3817 OpenTestOverlaysTierupMenu(param1);
3818 }
3819 case MenuAction_Cancel:
3820 {
3821 if (param2 == MenuCancel_ExitBack)
3822 OpenTestOverlaysMenu(param1);
3823 }
3824 case MenuAction_End:
3825 delete menu;
3826 }
3827}
3828
3829public Action Command_ReloadRanks(int client, int args)
3830{
3831 ParseRanksConfig();
3832 ReplyToCommand(client, "Ranks data has been reloaded.");
3833 return Plugin_Handled;
3834}
3835
3836public Action Command_ReloadTiers(int client, int args)
3837{
3838 ParseTiersConfig();
3839 ReplyToCommand(client, "Tiers data has been reloaded.");
3840 return Plugin_Handled;
3841}
3842
3843public Action Command_TestSpecHud(int client, int args)
3844{
3845 g_HudSkipClient[client] = !g_HudSkipClient[client];
3846 ReplyToCommand(client, "The spectator hud is now %s in first person.", g_HudSkipClient[client] ? "disabled" : "enabled");
3847 return Plugin_Handled;
3848}
3849
3850public int Native_Server_GetTierTag(Handle plugin, int numParams)
3851{
3852 int client = GetNativeCell(1);
3853
3854 if (client == 0 || client > MaxClients)
3855 return false;
3856
3857 SetNativeString(2, g_sCacheData_TierTag[client], GetNativeCell(3));
3858 return true;
3859}
3860
3861public int Native_Server_GetPlaytime(Handle plugin, int numParams)
3862{
3863 int client = GetNativeCell(1);
3864
3865 if (client == 0 || client > MaxClients)
3866 return 0;
3867
3868 return view_as<int>(GetClientTime(client) + g_fStatistics_Global_Playtime[client]);
3869}
3870
3871public int Native_SetSpecHud(Handle plugin, int numParams)
3872{
3873 int client = GetNativeCell(1);
3874
3875 if (client == 0 || client > MaxClients)
3876 return false;
3877
3878 g_bSpecHud[client] = GetNativeCell(2);
3879 return true;
3880}
3881
3882public Action Command_ResetRank(int client, int args)
3883{
3884 if (!convar_Status.BoolValue || client == 0)
3885 return Plugin_Handled;
3886
3887 int iCredits = Furious_Store_GetCredits(client);
3888 int iDeduct = convar_ResetRankCredits.IntValue;
3889
3890 if (iCredits >= iDeduct)
3891 {
3892 static char buffer[256];
3893
3894 if( iDeduct == 0 )
3895 {
3896 Format(buffer, sizeof(buffer), "Do you want to reset your seasonal statistics?");
3897 }
3898 else
3899 {
3900 Format(buffer, sizeof(buffer), "Do you want to reset your seasonal statistics for %i credits?", iDeduct);
3901 }
3902
3903 SendConfirmationMenu(client, Confirmation_ResetRank, buffer, _, iCredits);
3904 }
3905 else
3906 CPrintToChat(client, "%T", "reset rank not enough credits", client, iDeduct);
3907
3908 return Plugin_Handled;
3909}
3910
3911public void Confirmation_ResetRank(int client, ConfirmationResponses response, int credits)
3912{
3913 int iDeduct = convar_ResetRankCredits.IntValue;
3914
3915 if (response == Confirm_Yes)
3916 {
3917 credits -= iDeduct;
3918 Furious_Store_SetCredits(client, credits, false);
3919
3920 ResetSeasonalData(client);
3921 CPrintToChat(client, "%T", "reset rank", client, iDeduct);
3922 }
3923}
3924
3925void ResetSeasonalData(int client)
3926{
3927 if (!g_bActiveSeason)
3928 {
3929 CPrintToChat(client, "%T", "season not active", client);
3930 return;
3931 }
3932
3933 char sTable[MAX_TABLE_SIZE];
3934 GetTableString_Season(sTable, sizeof(sTable));
3935
3936 char sQuery[MAX_QUERY_SIZE];
3937 g_Database_Server.Format(sQuery, sizeof(sQuery), "UPDATE `%s` SET `kills` = '0', `deaths` = '0', `assists` = '0', `headshots` = '0', `points` = '0', `longest_killstreak` = '0', `hits` = '0', `shots` = '0', `kdr` = '0.0', `accuracy` = '0.0', `playtime` = '0.0', `weapons_statistics` = '{ }' WHERE `accountid` = '%i';", sTable, GetSteamAccountID(client));
3938 g_Database_Server.Query(TQuery_OnResetSeasonalStats, sQuery, GetClientUserId(client));
3939}
3940
3941public void TQuery_OnResetSeasonalStats(Database db, DBResultSet results, const char[] error, any data)
3942{
3943 if (results == null)
3944 ThrowError("Error resetting client seasonal statistics: %s", error);
3945
3946 int client;
3947 if ((client = GetClientOfUserId(data)) > 0)
3948 {
3949 g_iStatistics_Seasonal_Kills[client] = 0;
3950 g_iStatistics_Seasonal_Deaths[client] = 0;
3951 g_iStatistics_Seasonal_Assists[client] = 0;
3952 g_iStatistics_Seasonal_Headshots[client] = 0;
3953 g_iStatistics_Seasonal_Points[client] = 0;
3954 g_iStatistics_Seasonal_Longest_Killstreak[client] = 0;
3955 g_iStatistics_Seasonal_Hits[client] = 0;
3956 g_iStatistics_Seasonal_Shots[client] = 0;
3957 g_fStatistics_Seasonal_KDR[client] = 0.0;
3958 g_fStatistics_Seasonal_Accuracy[client] = 0.0;
3959 g_fStatistics_Seasonal_Playtime[client] = 0.0;
3960
3961 if (g_hStatistics_Seasonal_WeaponsStatistics[client] != null)
3962 {
3963 g_hStatistics_Seasonal_WeaponsStatistics[client].Cleanup();
3964 delete g_hStatistics_Seasonal_WeaponsStatistics[client];
3965 }
3966
3967 g_hStatistics_Seasonal_WeaponsStatistics[client] = new JSON_Object();
3968
3969 g_iCacheData_Rank[client] = 0;
3970 g_iCacheData_Tier[client] = 0;
3971 g_iCacheData_PointsGain[client] = 0;
3972 g_iCacheData_PointsLoss[client] = 0;
3973 g_sCacheData_TierTag[client][0] = '\0';
3974
3975 CPrintToChat(client, "%T", "seasonal statistics reset", client, g_iSeason);
3976 }
3977}
3978
3979public Action Command_Season(int client, int args)
3980{
3981 if (!convar_Status.BoolValue)
3982 return Plugin_Continue;
3983
3984 char sTime[128];
3985 FormatTime(sTime, sizeof(sTime), "%A, %B %d, %Y at %R", g_iNextSeason);
3986
3987 CPrintToChat(client, "%T", "print current and next season", client, g_iSeason, g_iSeason+1, sTime);
3988
3989 return Plugin_Handled;
3990}
3991
3992public void OnClientResourceEntityPostThink(int entity)
3993{
3994 if( g_PersonalDataPublicLevelOffset != -1 )
3995 {
3996 int rank = -1;
3997
3998 for( int i = 1; i <= MaxClients; i++ )
3999 {
4000 if( !IsClientInGame(i) )
4001 {
4002 continue;
4003 }
4004
4005 if( IsFakeClient(i) )
4006 {
4007 continue;
4008 }
4009
4010 rank = g_iCacheData_Rank[i] - 1;
4011 SetEntData(entity, g_PersonalDataPublicLevelOffset + (i * 4), rank >= 0 && rank < TOP_RANKS_NUMBER ? g_TopRankIconIndex[rank] : -1);
4012 }
4013 }
4014}