· 9 years ago · Oct 29, 2016, 07:44 PM
1/*
2 [CS:S] CT Bans
3 by: databomb
4
5 Description:
6
7 Allows admins to restrict access to the CT team from those who violate the server's rules. There are already two plugins posted that I'm aware of for this. Mr. Zero's Team Restrict plugin which has basic command functionality but doesn't save the data past a map change/disconnect. Azelphur's TeamBans plugin also uses ClientPrefs but doesn't allow for banning those who disconnect or timed team bans.
8
9 Features:
10 - CT Bans are stored in the ClientPrefs database and survive map changes, re-joins, and server crashes.
11 - The Rage Ban feature allows admins to CT ban rage quitters who break the server's rules and then quickly disconnect.
12 - You may give a timed CT ban which will work based on in minutes spent alive (so idlers in spectate or those who suicide at the beginning of the round will not be working toward an unban.)
13 - The timed CT bans are stored in a SQL table for stateful access.
14 - The plugin logs CT ban to a SQL table in addition to your regular SM logs.
15 - Re-displays the team selection screen after an improper selection was made.
16 - SM Menu integration for the rageban and ctban commands.
17 - Displays helpful message to users who are CT banned when they join the server.
18 - SM Translations support.
19
20 Installation:
21 Place the phrases.txt in your addons/sourcemod/translations directory.
22 Place the .smx in your addons/sourcemod/plugins directory.
23 Check your logs/server-console after the initial load for any SQL errors. If you have any SQL errors check your addons/sourcemod/configs/databases.cfg file and verify you can connect using the drivers you have specified.
24
25 Command Usage:
26
27 sm_ctban <player>
28 Bans the selected player from joining the CT team.
29
30 sm_removectban <player> | sm_unctban <player>
31 Removes the CT ban on the selected player.
32
33 sm_isbanned <player>
34 Reports back the status of the current player's CT ban and the time remaining on the ban, if any.
35
36 sm_rageban
37 Brings up a menu so you may choose a recently disconnected player to permanently CT ban.
38
39 sm_ctban_offline <steamid>
40 Bans the given Steam Id from playing on the CT team.
41
42 sm_removectban_offline <steamid> | sm_unctban_offline <steamid>
43 Unbans the given Steam Id from the CT team.
44
45 Settings:
46 sm_ctban_enable, [0,1]: Toggles functionality. When set to 0 this will allow those players who are CT banned to join the CT team.
47 sm_ctban_soundfile, <path>: The path to the soundfile to play when denying a team-change request. Set to "" to disable.
48 sm_ctban_joinbanmsg, <message>: This message is appended to a time-stamp when a CT banned user joins the server.
49 sm_ctban_table_prefix, <prefix>: This prefix will be added in front of the table names.
50 sm_ctban_database_driver, <driver>: This specifies which driver to use from database.cfg
51
52 Special Thanks:
53 Azelphur for the idea of using bitmasks to improve efficiency and snippets of cross-mod code.
54 Kigen for the idea of CT banning based on time spent alive.
55
56 Future Considerations:
57 Allow offline editing as soon as SetAuthIdCookie native is added to latest SourceMod
58 Improved web interface
59 Allowing for more than 7 reasons to CT Ban
60
61 Change Log:
62 1.6.1.3 Fixed bug with EscapeString function which caused query failures
63 1.6.1.2 Fixed SQL Injection vulnerability
64 1.6.1.1 Fixed problem in UTIL_TeamMenu()
65 1.6.1 Support for new SM1.4 natives, Added confi file generation
66 1.6.0 Added support for new SM1.4 natives
67 1.5.0 Initial public release
68 1.4.4 Stable internal build
69
70*/
71
72#pragma semicolon 1
73#define CHAT_BANNER "\x03[SM] \x01%t"
74
75#include <sourcemod>
76#include <clientprefs>
77#include <sdktools>
78#include <adminmenu>
79#include <cstrike>
80#include <ctban>
81
82#define PLUGIN_VERSION "1.6.1.3"
83
84// compilation settings:
85// (set DEBUG and OCAOFF to 1 for best debugging results)
86// (setting USESQL to 0 is not recommended)
87#define DEBUG 0
88#define OCAOFF 0
89#define USESQL 1
90
91new Handle:g_CT_Cookie = INVALID_HANDLE;
92new Handle:gH_Cvar_Enabled = INVALID_HANDLE;
93new Handle:g_Handles[MAXPLAYERS+1];
94new Handle:gH_TopMenu = INVALID_HANDLE;
95new Handle:gH_Cvar_SoundName = INVALID_HANDLE;
96new String:gS_SoundPath[PLATFORM_MAX_PATH];
97new Handle:gH_Cvar_JoinBanMessage = INVALID_HANDLE;
98new Handle:gH_Cvar_Database_Driver = INVALID_HANDLE;
99new Handle:gA_DNames = INVALID_HANDLE;
100new Handle:gA_DSteamIDs = INVALID_HANDLE;
101new Handle:gH_CP_DataBase = INVALID_HANDLE;
102new Handle:gH_BanDatabase = INVALID_HANDLE;
103new Handle:gH_Cvar_Table_Prefix = INVALID_HANDLE;
104new g_iCookieIndex;
105new bool:g_bAuthIdNativeExists = false;
106new Handle:gA_TimedBanLocalList = INVALID_HANDLE;
107new gA_LocalTimeRemaining[MAXPLAYERS+1];
108#if USESQL == 0
109new Handle:gA_TimedBanSteamList = INVALID_HANDLE;
110#endif
111new gA_CTBanTargetUserId[MAXPLAYERS+1];
112new gA_CTBanTimeLength[MAXPLAYERS+1];
113new String:g_sLogTableName[32];
114new String:g_sTimesTableName[32];
115
116public Plugin:myinfo =
117{
118 name = "CT Ban",
119 author = "databomb",
120 description = "Allows admins to ban players from joining the CT team.",
121 version = PLUGIN_VERSION,
122 url = "vintagejailbreak.org"
123};
124
125public OnPluginStart()
126{
127 CreateConVar("sm_ctban_version", PLUGIN_VERSION, "CT Ban Version", FCVAR_SPONLY|FCVAR_DONTRECORD|FCVAR_REPLICATED|FCVAR_NOTIFY);
128 gH_Cvar_Enabled = CreateConVar("sm_ctban_enable","1","Enables CT bans cookie handling", FCVAR_PLUGIN);
129 gH_Cvar_SoundName = CreateConVar("sm_ctban_soundfile", "buttons/button11.wav", "The name of the sound to play when an action is denied",FCVAR_PLUGIN);
130 gH_Cvar_JoinBanMessage = CreateConVar("sm_ctban_joinbanmsg", "To appeal this go to VintageJailbreak.org", "This text is appended to the time the user was last CT banned when they join T or Spectator teams.", FCVAR_PLUGIN);
131 gH_Cvar_Table_Prefix = CreateConVar("sm_ctban_table_prefix", "", "Adds a prefix to the CT Bans table, leave this blank unless you have a need to add a prefix for multiple servers on one database.", FCVAR_PLUGIN);
132 gH_Cvar_Database_Driver = CreateConVar("sm_ctban_database_driver", "default", "Specifies the configuration driver to use from SourceMod's database.cfg", FCVAR_PLUGIN);
133
134 AutoExecConfig(true, "ctban");
135
136 g_CT_Cookie = RegClientCookie("Banned_From_CT", "Tells if you are restricted from joining the CT team", CookieAccess_Protected);
137
138 RegAdminCmd("sm_ctban", Command_CTBan, ADMFLAG_SLAY, "sm_ctban <player> <optional: time> - Bans a player from being a CT.");
139 RegAdminCmd("sm_isbanned", Command_IsCTBanned, ADMFLAG_GENERIC, "sm_isbanned <player> - Lets you know if a player is banned from CT team.");
140 RegAdminCmd("sm_removectban", Command_UnCTBan, ADMFLAG_SLAY, "sm_removectban <player> - Unrestricts a player from being a CT.");
141 RegAdminCmd("sm_unctban", Command_UnCTBan, ADMFLAG_SLAY, "sm_unctban <player> - Unrestricts a player from being a CT.");
142 RegAdminCmd("sm_rageban", Command_RageBan, ADMFLAG_SLAY, "sm_rageban <player> - Allows you to ban those who rage quit.");
143 RegAdminCmd("sm_ctban_offline", Command_Offline_CTBan, ADMFLAG_KICK, "sm_ctban_offline <steamid> - Allows admins to CT Ban players who have long left the server using their Steam Id.");
144 RegAdminCmd("sm_unctban_offline", Command_Offline_UnCTBan, ADMFLAG_KICK, "sm_unctban_offline <steamid> - Allows admins to remove CT Bans on players who have long left the server using their Steam Id.");
145 RegAdminCmd("sm_removectban_offline", Command_Offline_UnCTBan, ADMFLAG_KICK, "sm_unctban_offline <steamid> - Allows admins to remove CT Bans on players who have long left the server using their Steam Id.");
146
147 LoadTranslations("ctban.phrases");
148 LoadTranslations("common.phrases");
149
150 // create arrays for the rage bans
151 gA_DNames = CreateArray(MAX_TARGET_LENGTH);
152 gA_DSteamIDs = CreateArray(22);
153 g_iCookieIndex = 0;
154
155 // Hook this to block joins when player is banned
156 AddCommandListener(Command_CheckJoin, "jointeam");
157
158 // create local array for timed bans
159 // block 0: client index
160 gA_TimedBanLocalList = CreateArray(2);
161 for (new idx = 1; idx <= MaxClients; idx++)
162 {
163 gA_LocalTimeRemaining[idx] = 0;
164 gA_CTBanTargetUserId[idx] = 0;
165 }
166 #if USESQL == 0
167 // steam array structure:
168 // blocks 0-21: steamID string
169 // block 22: ban time remaining
170 gA_TimedBanSteamList = CreateArray(23);
171 #endif
172
173 // periodic timer to handle timed bans
174 CreateTimer(60.0, CheckTimedCTBans, _, TIMER_REPEAT);
175
176 /* Account for late loading */
177 new Handle:topmenu;
178 if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
179 {
180 OnAdminMenuReady(topmenu);
181 }
182}
183
184public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
185{
186 CreateNative("IsBannedFromCT", Native_IsBannedFromCT);
187 return APLRes_Success;
188}
189
190public OnAllPluginsLoaded()
191{
192 g_bAuthIdNativeExists = IsSetAuthIdNativePresent();
193}
194
195// consider if someone does a 'retry' and clientprefs is accessinsg on its ClientConnectCallback and this is also trying to set a value
196public OnClientAuthorized(client, const String:sSteamID[])
197{
198 #if OCAOFF == 0
199 // check if the Steam ID is in the Rage Ban list
200 new iNeedle = FindStringInArray(gA_DSteamIDs, sSteamID);
201 if (iNeedle != -1)
202 {
203 RemoveFromArray(gA_DNames, iNeedle);
204 RemoveFromArray(gA_DSteamIDs, iNeedle);
205 #if DEBUG == 1
206 LogMessage("removed %N from Rage Bannable player list for re-connecting to the server", client);
207 #endif
208 }
209 #endif
210
211 #if USESQL == 1
212 // check if the Steam ID is in the Timed Ban list
213 decl String:query[255];
214 Format(query, sizeof(query), "SELECT ctbantime FROM %s WHERE steamid = '%s'", g_sTimesTableName, sSteamID);
215 SQL_TQuery(gH_BanDatabase, DB_Callback_OnClientAuthed, query, _:client);
216
217 #else
218
219 new iSteamArrayIndex = FindStringInArray(gA_TimedBanSteamList, sSteamID);
220 if (iSteamArrayIndex != -1)
221 {
222 gA_LocalTimeRemaining[client] = GetArrayCell(gA_TimedBanSteamList, iSteamArrayIndex, 22);
223 #if DEBUG == 1
224 LogMessage("%N joined with %i time remaining on ban", client, gA_LocalTimeRemaining[client]);
225 #endif
226 }
227 #endif
228}
229
230public DB_Callback_OnClientAuthed(Handle:owner, Handle:hndl, const String:error[], any:client)
231{
232 if (hndl == INVALID_HANDLE)
233 {
234 LogError("Error in OnClientAuthorized query: %s", error);
235 }
236 else
237 {
238 new iRowCount = SQL_GetRowCount(hndl);
239 #if DEBUG == 1
240 LogMessage("SQL Auth: %d row count", iRowCount);
241 #endif
242 if (iRowCount)
243 {
244 SQL_FetchRow(hndl);
245 new iBanTimeRemaining = SQL_FetchInt(hndl, 0);
246 #if DEBUG == 1
247 LogMessage("SQL Auth: %N joined with %i time remaining on ban", client, iBanTimeRemaining);
248 #endif
249 // update local time
250 PushArrayCell(gA_TimedBanLocalList, client);
251 gA_LocalTimeRemaining[client] = iBanTimeRemaining;
252 }
253 }
254}
255
256public AdminMenu_RageBan(Handle:topmenu, TopMenuAction:action, TopMenuObject:object_id, param, String:buffer[], maxlength)
257{
258 if (action == TopMenuAction_DisplayOption)
259 {
260 Format(buffer, maxlength, "Rage Ban");
261 }
262 else if (action == TopMenuAction_SelectOption)
263 {
264 DisplayRageBanMenu(param, GetArraySize(gA_DNames));
265 }
266}
267
268DisplayRageBanMenu(Client, ArraySize)
269{
270 if (ArraySize == 0)
271 {
272 PrintToChat(Client, CHAT_BANNER, "No Targets");
273 }
274 else
275 {
276 new Handle:menu = CreateMenu(MenuHandler_RageBan);
277
278 SetMenuTitle(menu, "%T", "Rage Ban Menu Title", Client);
279 SetMenuExitBackButton(menu, true);
280
281 for (new ArrayIndex = 0; ArrayIndex < ArraySize; ArrayIndex++)
282 {
283 decl String:sName[MAX_TARGET_LENGTH];
284 GetArrayString(gA_DNames, ArrayIndex, sName, sizeof(sName));
285 decl String:sSteamID[22];
286 GetArrayString(gA_DSteamIDs, ArrayIndex, sSteamID, sizeof(sSteamID));
287 AddMenuItem(menu, sSteamID, sName);
288 }
289
290 DisplayMenu(menu, Client, MENU_TIME_FOREVER);
291 }
292}
293
294public MenuHandler_RageBan(Handle:menu, MenuAction:action, param1, param2)
295{
296 if (action == MenuAction_End)
297 {
298 CloseHandle(menu);
299 }
300 else if (action == MenuAction_Cancel)
301 {
302 if ((param2 == MenuCancel_ExitBack) && (gH_TopMenu != INVALID_HANDLE))
303 {
304 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
305 }
306 }
307 else if (action == MenuAction_Select)
308 {
309 decl String:sInfoString[22];
310 GetMenuItem(menu, param2, sInfoString, sizeof(sInfoString));
311
312 if (g_bAuthIdNativeExists)
313 {
314 SetAuthIdCookie(sInfoString, g_CT_Cookie, "1");
315 }
316 else
317 {
318 // determine if they're in the clientprefs SQL database yet
319 if (gH_CP_DataBase != INVALID_HANDLE)
320 {
321 decl String:query[255];
322 Format(query, sizeof(query), "SELECT value FROM sm_cookie_cache WHERE player = '%s' and cookie_id = '%i'", sInfoString, g_iCookieIndex);
323 new Handle:TheDataPack = CreateDataPack();
324 // authID
325 WritePackString(TheDataPack, sInfoString);
326 // admin who banned (client index)
327 WritePackCell(TheDataPack, param1);
328 // array index to CTBan
329 WritePackCell(TheDataPack, param2);
330 SQL_TQuery(gH_CP_DataBase, CP_Callback_CheckBan, query, TheDataPack);
331 }
332 }
333 #if DEBUG == 1
334 PrintToChat(param1, CHAT_BANNER, "Ready to CT Ban", sInfoString);
335 #endif
336 }
337}
338
339public CP_Callback_CheckBan(Handle:owner, Handle:hndl, const String:error[], any:stringPack)
340{
341 if (hndl == INVALID_HANDLE)
342 {
343 LogError("CT Ban query had a failure: %s", error);
344 CloseHandle(stringPack);
345 }
346 else
347 {
348 ResetPack(stringPack);
349 decl String:authID[22];
350 ReadPackString(stringPack, authID, sizeof(authID));
351 new iAdminIndex = ReadPackCell(stringPack);
352 new iArrayBanIndex = ReadPackCell(stringPack);
353 CloseHandle(stringPack);
354
355 new iTimeStamp = GetTime();
356
357 new iRowCount = SQL_GetRowCount(hndl);
358 if (iRowCount)
359 {
360 #if DEBUG == 1
361 SQL_FetchRow(hndl);
362 new iCTBanStatus = SQL_FetchInt(hndl, 0);
363 LogMessage("CTBan status on player is currently %i. Will do UPDATE on %s", iCTBanStatus, authID);
364 #endif
365
366 decl String:query[255];
367 Format(query, sizeof(query), "UPDATE sm_cookie_cache SET value = '1', timestamp = %i WHERE player = '%s' AND cookie_id = '%i'", iTimeStamp, authID, g_iCookieIndex);
368 #if DEBUG == 1
369 LogMessage("Query to run: %s", query);
370 #endif
371 SQL_TQuery(gH_CP_DataBase, CP_Callback_IssueBan, query);
372 }
373 else
374 {
375 #if DEBUG == 1
376 LogMessage("couldn't find steamID in database, need to INSERT");
377 #endif
378
379 decl String:query[255];
380 Format(query, sizeof(query), "INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES ('%s', %i, '1', %i)", authID, g_iCookieIndex, iTimeStamp);
381 #if DEBUG == 1
382 LogMessage("Query to run: %s", query);
383 #endif
384 SQL_TQuery(gH_CP_DataBase, CP_Callback_IssueBan, query);
385 }
386
387 // log this info
388 decl String:sTargetName[MAX_TARGET_LENGTH];
389 GetArrayString(gA_DNames, iArrayBanIndex, sTargetName, sizeof(sTargetName));
390 decl String:adminSteamID[32];
391 GetClientAuthId(iAdminIndex, AuthId_Steam2, adminSteamID, sizeof(adminSteamID));
392
393 #if USESQL == 1
394 decl String:logQuery[350];
395 decl String:temp[300];
396 new String:escapedPerpName[300];
397 Format(temp, sizeof(temp), "%s", sTargetName);
398 SQL_EscapeString(gH_BanDatabase, temp, escapedPerpName, sizeof(escapedPerpName));
399 Format(logQuery, sizeof(logQuery), "INSERT INTO %s (timestamp, perp_steamid, perp_name, admin_steamid, admin_name, bantime, timeleft, reason) VALUES (%d, '%s', '%s', '%s', 'Console', 0, 0, 'Rage ban')", g_sLogTableName, iTimeStamp, authID, escapedPerpName, adminSteamID);
400 #if DEBUG == 1
401 LogMessage("log query: %s", logQuery);
402 #endif
403 SQL_TQuery(gH_BanDatabase, DB_Callback_CTBan, logQuery, iAdminIndex);
404 #endif
405
406 LogMessage("%N (%s) has issued a rage ban on %s (%s) indefinitely.", iAdminIndex, adminSteamID, sTargetName, authID);
407
408 ShowActivity2(iAdminIndex, "[SM] ", "%t", "Rage Ban", sTargetName);
409
410 // clear the position from array
411 RemoveFromArray(gA_DNames, iArrayBanIndex);
412 RemoveFromArray(gA_DSteamIDs, iArrayBanIndex);
413 #if DEBUG == 1
414 LogMessage("Removed %i index from rage ban menu.", iArrayBanIndex);
415 #endif
416 }
417}
418
419public CP_Callback_IssueBan(Handle:owner, Handle:hndl, const String:error[], any:data)
420{
421 if (hndl == INVALID_HANDLE)
422 {
423 LogError("Error writing to database: %s", error);
424 }
425 else
426 {
427 #if DEBUG == 1
428 LogMessage("succesfully wrote to the database");
429 #endif
430 }
431}
432
433public Action:Command_Offline_CTBan(client, args)
434{
435 decl String:sAuthId[32];
436 GetCmdArgString(sAuthId, sizeof(sAuthId));
437 if (g_bAuthIdNativeExists)
438 {
439 SetAuthIdCookie(sAuthId, g_CT_Cookie, "1");
440 ReplyToCommand(client, CHAT_BANNER, "Banned AuthId", sAuthId);
441 }
442 else
443 {
444 ReplyToCommand(client, CHAT_BANNER, "Feature Not Available");
445 }
446 return Plugin_Handled;
447}
448
449public Action:Command_Offline_UnCTBan(client, args)
450{
451 decl String:sAuthId[32];
452 GetCmdArgString(sAuthId, sizeof(sAuthId));
453 if (g_bAuthIdNativeExists)
454 {
455 SetAuthIdCookie(sAuthId, g_CT_Cookie, "0");
456 ReplyToCommand(client, CHAT_BANNER, "Unbanned AuthId", sAuthId);
457 }
458 else
459 {
460 ReplyToCommand(client, CHAT_BANNER, "Feature Not Available");
461 }
462 return Plugin_Handled;
463}
464
465public Action:Command_RageBan(client, args)
466{
467 new iArraySize = GetArraySize(gA_DNames);
468 if (iArraySize == 0)
469 {
470 ReplyToCommand(client, CHAT_BANNER, "No Targets");
471 return Plugin_Handled;
472 }
473
474 if (!args)
475 {
476 if (client)
477 {
478 DisplayRageBanMenu(client, iArraySize);
479 }
480 else
481 {
482 ReplyToCommand(client, CHAT_BANNER, "Feature Not Available On Console");
483 }
484 return Plugin_Handled;
485 }
486 else
487 {
488 ReplyToCommand(client, "[SM] Usage: sm_rageban");
489 }
490
491 return Plugin_Handled;
492}
493
494public Action:CheckTimedCTBans(Handle:timer)
495{
496 // check if anyone has a time
497 new iTimeArraySize = GetArraySize(gA_TimedBanLocalList);
498
499 // credit for this idea goes to Kigen
500 for (new idx = 0; idx < iTimeArraySize; idx++)
501 {
502 new iBannedClientIndex = GetArrayCell(gA_TimedBanLocalList, idx);
503 if (IsClientInGame(iBannedClientIndex))
504 {
505 if (IsPlayerAlive(iBannedClientIndex))
506 {
507 gA_LocalTimeRemaining[iBannedClientIndex]--;
508 #if DEBUG == 1
509 LogMessage("found alive time banned client with %i remaining", gA_LocalTimeRemaining[iBannedClientIndex]);
510 #endif
511 // check if we should remove the CT ban
512 if (gA_LocalTimeRemaining[iBannedClientIndex] <= 0)
513 {
514 // remove CT ban
515 RemoveFromArray(gA_TimedBanLocalList, idx);
516 iTimeArraySize--;
517 Remove_CTBan(0, iBannedClientIndex, true);
518 #if DEBUG == 1
519 LogMessage("removed CT ban on %N", iBannedClientIndex);
520 #endif
521 }
522 }
523 }
524 }
525}
526
527public OnConfigsExecuted()
528{
529 SQL_TConnect(CP_Callback_Connect, "clientprefs");
530
531 decl String:sDatabaseDriver[64];
532 GetConVarString(gH_Cvar_Database_Driver, sDatabaseDriver, sizeof(sDatabaseDriver));
533 SQL_TConnect(DB_Callback_Connect, sDatabaseDriver);
534}
535
536public DB_Callback_Connect(Handle:owner, Handle:hndl, const String:error[], any:data)
537{
538 if (hndl == INVALID_HANDLE)
539 {
540 LogError("Default database database connection failure: %s", error);
541 SetFailState("Error while connecting to default database. Exiting.");
542 }
543 else
544 {
545 gH_BanDatabase = hndl;
546
547 // figure out table prefix situation
548 decl String:sPrefix[64];
549 GetConVarString(gH_Cvar_Table_Prefix, sPrefix, sizeof(sPrefix));
550 if (strlen(sPrefix) > 0)
551 {
552 Format(g_sTimesTableName, sizeof(g_sTimesTableName), "%s_CTBan_Times", sPrefix);
553 }
554 else
555 {
556 Format(g_sTimesTableName, sizeof(g_sTimesTableName), "CTBan_Times");
557 }
558
559 decl String:sQuery[255];
560 Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS %s (steamid VARCHAR(22), ctbantime INT(16), PRIMARY KEY (steamid))", g_sTimesTableName);
561
562 // create database if not already there
563 SQL_TQuery(gH_BanDatabase, DB_Callback_Create, sQuery);
564
565 if (strlen(sPrefix) > 0)
566 {
567 Format(g_sLogTableName, sizeof(g_sLogTableName), "%s_CTBan_Log", sPrefix);
568 }
569 else
570 {
571 Format(g_sLogTableName, sizeof(g_sLogTableName), "CTBan_Log");
572 }
573
574 Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS %s (timestamp INT, perp_steamid VARCHAR(22), perp_name VARCHAR(32), admin_steamid VARCHAR(22), admin_name VARCHAR(32), bantime INT(16), timeleft INT(16), reason VARCHAR(200), PRIMARY KEY (timestamp))", g_sLogTableName);
575 SQL_TQuery(gH_BanDatabase, DB_Callback_Create, sQuery);
576 }
577}
578
579public DB_Callback_Create(Handle:owner, Handle:hndl, const String:error[], any:data)
580{
581 if (hndl == INVALID_HANDLE)
582 {
583 LogError("Error establishing table creation: %s", error);
584 SetFailState("Unable to ascertain creation of table in default database. Exiting.");
585 }
586}
587
588public CP_Callback_Connect(Handle:owner, Handle:hndl, const String:error[], any:data)
589{
590 if (hndl == INVALID_HANDLE)
591 {
592 LogError("Clientprefs database connection failure: %s", error);
593 SetFailState("Error while connecting to clientprefs database. Exiting.");
594 }
595 else
596 {
597 gH_CP_DataBase = hndl;
598
599 // find the Banned_From_CT Cookie id #
600 SQL_TQuery(gH_CP_DataBase, CP_Callback_FindCookie, "SELECT id FROM sm_cookies WHERE name = 'Banned_From_CT'");
601 }
602}
603
604public CP_Callback_FindCookie(Handle:owner, Handle:hndl, const String:error[], any:data)
605{
606 if (hndl == INVALID_HANDLE)
607 {
608 LogError("Cookie query failure: %s", error);
609 }
610 else
611 {
612 new iRowCount = SQL_GetRowCount(hndl);
613 if (iRowCount)
614 {
615 SQL_FetchRow(hndl);
616 new CookieIDIndex = SQL_FetchInt(hndl, 0);
617 #if DEBUG == 1
618 LogMessage("found cookie index as %i", CookieIDIndex);
619 #endif
620 g_iCookieIndex = CookieIDIndex;
621 }
622 else
623 {
624 LogError("Could not find the cookie index. Rageban functionality disabled.");
625 }
626 }
627}
628
629public OnMapStart()
630{
631 // pre-cache deny sound
632 decl String:buffer[PLATFORM_MAX_PATH];
633 GetConVarString(gH_Cvar_SoundName, gS_SoundPath, sizeof(gS_SoundPath));
634 if(strcmp(gS_SoundPath, ""))
635 {
636 PrecacheSound(gS_SoundPath, true);
637 Format(buffer, sizeof(buffer), "sound/%s", gS_SoundPath);
638 AddFileToDownloadsTable(buffer);
639 }
640}
641
642public OnAdminMenuReady(Handle:topmenu)
643{
644 /* Block us from being called twice */
645 if (topmenu == gH_TopMenu)
646 {
647 return;
648 }
649
650 /* Save the Handle */
651 gH_TopMenu = topmenu;
652
653 /* Build the "Player Commands" category */
654 new TopMenuObject:frequent_commands = FindTopMenuCategory(gH_TopMenu, "ts_commands");
655
656 if (frequent_commands != INVALID_TOPMENUOBJECT)
657 {
658 AddToTopMenu(gH_TopMenu,
659 "sm_ctban",
660 TopMenuObject_Item,
661 AdminMenu_CTBan,
662 frequent_commands,
663 "sm_ctban",
664 ADMFLAG_SLAY);
665 }
666
667 /* Build the "Player Commands" category */
668 new TopMenuObject:player_commands = FindTopMenuCategory(gH_TopMenu, ADMINMENU_PLAYERCOMMANDS);
669
670 if (player_commands != INVALID_TOPMENUOBJECT)
671 {
672 AddToTopMenu(gH_TopMenu,
673 "sm_rageban",
674 TopMenuObject_Item,
675 AdminMenu_RageBan,
676 player_commands,
677 "sm_rageban",
678 ADMFLAG_SLAY);
679
680 if (frequent_commands == INVALID_TOPMENUOBJECT)
681 {
682 AddToTopMenu(gH_TopMenu,
683 "sm_ctban",
684 TopMenuObject_Item,
685 AdminMenu_CTBan,
686 player_commands,
687 "sm_ctban",
688 ADMFLAG_SLAY);
689 }
690 }
691}
692
693public AdminMenu_CTBan(Handle:topmenu,
694 TopMenuAction:action,
695 TopMenuObject:object_id,
696 param,
697 String:buffer[],
698 maxlength)
699{
700 if (action == TopMenuAction_DisplayOption)
701 {
702 Format(buffer, maxlength, "CT Ban");
703 }
704 else if (action == TopMenuAction_SelectOption)
705 {
706 DisplayCTBanPlayerMenu(param);
707 }
708}
709
710DisplayCTBanPlayerMenu(client)
711{
712 new Handle:menu = CreateMenu(MenuHandler_CTBanPlayerList);
713
714 SetMenuTitle(menu, "%T", "CT Ban Menu Title", client);
715 SetMenuExitBackButton(menu, true);
716
717 AddTargetsToMenu(menu, client, true, false);
718
719 DisplayMenu(menu, client, MENU_TIME_FOREVER);
720}
721
722DisplayCTBanTimeMenu(client, targetUserId)
723{
724 new Handle:menu = CreateMenu(MenuHandler_CTBanTimeList);
725
726 SetMenuTitle(menu, "%T", "CT Ban Length Menu", client, GetClientOfUserId(targetUserId));
727 SetMenuExitBackButton(menu, true);
728
729 AddMenuItem(menu, "0", "Permanent");
730 AddMenuItem(menu, "5", "5 Minutes");
731 AddMenuItem(menu, "10", "10 Minutes");
732 AddMenuItem(menu, "30", "30 Minutes");
733 AddMenuItem(menu, "60", "1 Hour");
734 AddMenuItem(menu, "120", "2 Hours");
735 AddMenuItem(menu, "240", "4 Hours");
736 AddMenuItem(menu, "720", "12 Hours");
737 AddMenuItem(menu, "1440", "1 Day");
738 AddMenuItem(menu, "2880", "3 Days");
739 AddMenuItem(menu, "10080", "1 Week");
740
741 DisplayMenu(menu, client, MENU_TIME_FOREVER);
742}
743
744DisplayCTBanReasonMenu(client)
745{
746 new Handle:menu = CreateMenu(MenuHandler_CTBanReasonList);
747
748 SetMenuTitle(menu, "%T", "CT Ban Reason Menu", client, GetClientOfUserId(gA_CTBanTargetUserId[client]));
749 SetMenuExitBackButton(menu, true);
750
751 decl String:sMenuReason[128];
752 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 1", client);
753 AddMenuItem(menu, "1", sMenuReason);
754 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 2", client);
755 AddMenuItem(menu, "2", sMenuReason);
756 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 3", client);
757 AddMenuItem(menu, "3", sMenuReason);
758 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 4", client);
759 AddMenuItem(menu, "4", sMenuReason);
760 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 5", client);
761 AddMenuItem(menu, "5", sMenuReason);
762 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 6", client);
763 AddMenuItem(menu, "6", sMenuReason);
764 Format(sMenuReason, sizeof(sMenuReason), "%T", "CT Ban Reason 7", client);
765 AddMenuItem(menu, "7", sMenuReason);
766
767 DisplayMenu(menu, client, MENU_TIME_FOREVER);
768}
769
770public MenuHandler_CTBanReasonList(Handle:menu, MenuAction:action, param1, param2)
771{
772 if (action == MenuAction_End)
773 {
774 CloseHandle(menu);
775 }
776 else if (action == MenuAction_Cancel)
777 {
778 if (param2 == MenuCancel_ExitBack && gH_TopMenu != INVALID_HANDLE)
779 {
780 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
781 }
782 }
783 else if (action == MenuAction_Select)
784 {
785 decl String:sBanChoice[10];
786 GetMenuItem(menu, param2, sBanChoice, sizeof(sBanChoice));
787 new iBanReason = StringToInt(sBanChoice);
788 new iTimeToBan = gA_CTBanTimeLength[param1];
789 new iTargetIndex = GetClientOfUserId(gA_CTBanTargetUserId[param1]);
790
791 decl String:sBanned[3];
792 GetClientCookie(iTargetIndex, g_CT_Cookie, sBanned, sizeof(sBanned));
793 new banFlag = StringToInt(sBanned);
794 if (!banFlag)
795 {
796 PerformCTBan(iTargetIndex, param1, iTimeToBan, iBanReason);
797 }
798 else
799 {
800 PrintToChat(param1, CHAT_BANNER, "Already CT Banned", iTargetIndex);
801 }
802 }
803}
804
805public MenuHandler_CTBanPlayerList(Handle:menu, MenuAction:action, param1, param2)
806{
807 if (action == MenuAction_End)
808 {
809 CloseHandle(menu);
810 }
811 else if (action == MenuAction_Cancel)
812 {
813 if (param2 == MenuCancel_ExitBack && gH_TopMenu != INVALID_HANDLE)
814 {
815 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
816 }
817 }
818 else if (action == MenuAction_Select)
819 {
820 decl String:info[32];
821 new userid, target;
822
823 GetMenuItem(menu, param2, info, sizeof(info));
824 userid = StringToInt(info);
825
826 if ((target = GetClientOfUserId(userid)) == 0)
827 {
828 PrintToChat(param1, "[SM] %t", "Player no longer available");
829 }
830 else if (!CanUserTarget(param1, target))
831 {
832 PrintToChat(param1, "[SM] %t", "Unable to target");
833 }
834 else
835 {
836 gA_CTBanTargetUserId[param1] = userid;
837 DisplayCTBanTimeMenu(param1, userid);
838 }
839 }
840}
841
842public MenuHandler_CTBanTimeList(Handle:menu, MenuAction:action, param1, param2)
843{
844 if (action == MenuAction_End)
845 {
846 CloseHandle(menu);
847 }
848 else if (action == MenuAction_Cancel)
849 {
850 if (param2 == MenuCancel_ExitBack && gH_TopMenu != INVALID_HANDLE)
851 {
852 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
853 }
854 }
855 else if (action == MenuAction_Select)
856 {
857 decl String:info[32];
858 GetMenuItem(menu, param2, info, sizeof(info));
859 new iTimeToBan = StringToInt(info);
860 gA_CTBanTimeLength[param1] = iTimeToBan;
861 DisplayCTBanReasonMenu(param1);
862 }
863}
864
865public OnPluginEnd()
866{
867 for(new client = 1; client <= MaxClients; client++)
868 {
869 if(g_Handles[client] != INVALID_HANDLE)
870 {
871 CloseHandle(g_Handles[client]);
872 g_Handles[client] = INVALID_HANDLE;
873 }
874 }
875}
876
877public OnClientPostAdminCheck(client)
878{
879 if (GetConVarBool(gH_Cvar_Enabled))
880 {
881 g_Handles[client] = INVALID_HANDLE;
882 CreateTimer(0.0, CheckBanCookies, client, TIMER_FLAG_NO_MAPCHANGE);
883 }
884}
885
886public OnClientDisconnect(client)
887{
888 decl String:sDisconnectSteamID[32];
889 GetClientAuthId(client, AuthId_Steam2, sDisconnectSteamID, sizeof(sDisconnectSteamID));
890
891 if(g_Handles[client] != INVALID_HANDLE)
892 {
893 CloseHandle(g_Handles[client]);
894 g_Handles[client] = INVALID_HANDLE;
895 }
896
897 // add information to rage ban list
898 decl String:sName[MAX_TARGET_LENGTH];
899 GetClientName(client, sName, sizeof(sName));
900
901 // add information to array
902 // if information isn't already in the arrays then add it
903 if (FindStringInArray(gA_DSteamIDs, sDisconnectSteamID) == -1)
904 {
905 PushArrayString(gA_DNames, sName);
906 PushArrayString(gA_DSteamIDs, sDisconnectSteamID);
907
908 if (GetArraySize(gA_DNames) >= 7)
909 {
910 RemoveFromArray(gA_DNames, 0);
911 RemoveFromArray(gA_DSteamIDs, 0);
912 }
913 }
914
915 // check if they were in the timed array
916 new iBannedArrayIndex = FindValueInArray(gA_TimedBanLocalList, client);
917 if (iBannedArrayIndex != -1)
918 {
919 // remove them from the local array
920 RemoveFromArray(gA_TimedBanLocalList, iBannedArrayIndex);
921
922 // make a datapack for the next query
923 new Handle:ClientDisconnectPack = CreateDataPack();
924 WritePackCell(ClientDisconnectPack, client);
925 WritePackString(ClientDisconnectPack, sDisconnectSteamID);
926
927 #if USESQL == 1
928 // update steam array
929 decl String:query[255];
930 Format(query, sizeof(query), "SELECT ctbantime FROM %s WHERE steamid = '%s'", g_sTimesTableName, sDisconnectSteamID);
931 SQL_TQuery(gH_BanDatabase, DB_Callback_ClientDisconnect, query, ClientDisconnectPack);
932
933 #else
934
935 new iSteamArrayIndex = FindStringInArray(gA_TimedBanSteamList, sDisconnectSteamID);
936 if (iSteamArrayIndex != -1)
937 {
938 if (gA_LocalTimeRemaining[client] <= 0)
939 {
940 RemoveFromArray(gA_TimedBanSteamList, iSteamArrayIndex);
941 }
942 else
943 {
944 SetArrayCell(gA_TimedBanSteamList, iSteamArrayIndex, gA_LocalTimeRemaining[client], 22);
945 }
946 }
947 #endif
948 }
949}
950
951public DB_Callback_ClientDisconnect(Handle:owner, Handle:hndl, const String:error[], any:thePack)
952{
953 if (hndl == INVALID_HANDLE)
954 {
955 LogError("Error with query on client disconnect: %s", error);
956 CloseHandle(thePack);
957 }
958 else
959 {
960 ResetPack(thePack);
961 new client = ReadPackCell(thePack);
962 decl String:sAuthID[22];
963 ReadPackString(thePack, sAuthID, sizeof(sAuthID));
964
965 new iRowCount = SQL_GetRowCount(hndl);
966 if (iRowCount)
967 {
968 #if DEBUG == 1
969 SQL_FetchRow(hndl);
970 new iBanTimeRemaining = SQL_FetchInt(hndl, 0);
971
972 if (IsClientInGame(client))
973 {
974 LogMessage("SQL: %N disconnected with %i time remaining on ban", client, iBanTimeRemaining);
975 }
976 else
977 {
978 LogMessage("SQL: %i client index disconnected with %i time remaining on ban", client, iBanTimeRemaining);
979 }
980 #endif
981
982 if (gA_LocalTimeRemaining[client] <= 0)
983 {
984 // remove steam array
985 decl String:query[255];
986 Format(query, sizeof(query), "DELETE FROM %s WHERE steamid = '%s'", g_sTimesTableName, sAuthID);
987 SQL_TQuery(gH_BanDatabase, DB_Callback_DisconnectAction, query);
988 Format(query, sizeof(query), "UPDATE %s SET timeleft=-1 WHERE perp_steamid = '%s' AND timeleft >= 0", g_sLogTableName, sAuthID);
989 SQL_TQuery(gH_BanDatabase, DB_Callback_DisconnectAction, query);
990 }
991 else
992 {
993 // update the time
994 decl String:query[255];
995 Format(query, sizeof(query), "UPDATE %s SET ctbantime = %d WHERE steamid = '%s'", g_sTimesTableName, gA_LocalTimeRemaining[client], sAuthID);
996 SQL_TQuery(gH_BanDatabase, DB_Callback_DisconnectAction, query);
997 Format(query, sizeof(query), "UPDATE %s SET timeleft = %d WHERE perp_steamid = '%s' AND timeleft >= 0", g_sLogTableName, gA_LocalTimeRemaining[client], sAuthID);
998 SQL_TQuery(gH_BanDatabase, DB_Callback_DisconnectAction, query);
999 }
1000 }
1001 }
1002}
1003
1004public DB_Callback_DisconnectAction(Handle:owner, Handle:hndl, const String:error[], any:data)
1005{
1006 if (hndl == INVALID_HANDLE)
1007 {
1008 LogError("Error with updating/deleting record after client disconnect: %s", error);
1009 }
1010}
1011
1012public Action:CheckBanCookies(Handle:timer, any: client)
1013{
1014 if (AreClientCookiesCached(client))
1015 {
1016 ProcessBanCookies(client);
1017 }
1018 else if(IsClientInGame(client))
1019 {
1020 CreateTimer(5.0, CheckBanCookies, client, TIMER_FLAG_NO_MAPCHANGE);
1021 }
1022}
1023
1024ProcessBanCookies(client)
1025{
1026 if(client && IsClientInGame(client))
1027 {
1028 decl String:cookie[32];
1029 GetClientCookie(client, g_CT_Cookie, cookie, sizeof(cookie));
1030
1031 if (StrEqual(cookie, "1"))
1032 {
1033 // check to see if they joined CT
1034 if (GetClientTeam(client) == CS_TEAM_CT)
1035 {
1036 if (IsPlayerAlive(client))
1037 {
1038 // strip their weapons so they cannot gunplant after death
1039 new wepIdx;
1040 for (new i; i < 4; i++)
1041 {
1042 if ((wepIdx = GetPlayerWeaponSlot(client, i)) != -1)
1043 {
1044 RemovePlayerItem(client, wepIdx);
1045 AcceptEntityInput(wepIdx, "Kill");
1046 }
1047 }
1048
1049 ForcePlayerSuicide(client);
1050 }
1051
1052 ChangeClientTeam(client, CS_TEAM_T);
1053 PrintToChat(client, CHAT_BANNER, "Enforcing CT Ban");
1054 }
1055 }
1056 }
1057}
1058
1059public Action:Command_UnCTBan(client, args)
1060{
1061 if (args < 1)
1062 {
1063 ReplyToCommand(client, "[SM] Usage: sm_unctban <player>");
1064 }
1065 else
1066 {
1067 decl String:target[64];
1068 GetCmdArg(1, target, sizeof(target));
1069
1070 decl String:clientName[MAX_TARGET_LENGTH], target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
1071 target_count = ProcessTargetString(target, client, target_list, MAXPLAYERS, 0, clientName, sizeof(clientName), tn_is_ml);
1072 // make sure we have exactly one target here.. we don't want to CT ban lots of people
1073 if (target_count != 1)
1074 {
1075 ReplyToTargetError(client, target_count);
1076 }
1077 else
1078 {
1079 // check if the cookies are ready
1080 if (AreClientCookiesCached(target_list[0]))
1081 {
1082 Remove_CTBan(client, target_list[0]);
1083 }
1084 else
1085 {
1086 ReplyToCommand(client, CHAT_BANNER, "Cookie Status Unavailable");
1087 }
1088 }
1089 }
1090
1091 return Plugin_Handled;
1092}
1093
1094Remove_CTBan(adminIndex, targetIndex, bExpired=false)
1095{
1096 decl String:isBanned[3];
1097 GetClientCookie(targetIndex, g_CT_Cookie, isBanned, sizeof(isBanned));
1098 new banFlag = StringToInt(isBanned);
1099
1100 if (banFlag)
1101 {
1102 decl String:targetSteam[32];
1103 GetClientAuthId(targetIndex, AuthId_Steam2, targetSteam, sizeof(targetSteam));
1104
1105 #if USESQL == 1
1106 decl String:logQuery[350];
1107 Format(logQuery, sizeof(logQuery), "UPDATE %s SET timeleft=-1 WHERE perp_steamid = '%s' and timeleft >= 0", g_sLogTableName, targetSteam);
1108 #if DEBUG == 1
1109 LogMessage("log query: %s", logQuery);
1110 #endif
1111 SQL_TQuery(gH_BanDatabase, DB_Callback_RemoveCTBan, logQuery, targetIndex);
1112 #endif
1113
1114 LogMessage("%N has removed the CT ban on %N (%s).", adminIndex, targetIndex, targetSteam);
1115
1116 if (!bExpired)
1117 {
1118 ShowActivity2(adminIndex, "[SM] ", "%t", "CT Ban Removed", targetIndex);
1119 }
1120 else
1121 {
1122 ShowActivity2(adminIndex, "[SM] ", "%t", "CT Ban Auto Removed", targetIndex);
1123 }
1124
1125 // delete from the timedban database if there was one
1126 decl String:query[255];
1127 Format(query, sizeof(query), "DELETE FROM %s WHERE steamid = '%s'", g_sTimesTableName, targetSteam);
1128 SQL_TQuery(gH_BanDatabase, DB_Callback_RemoveCTBan, query, targetIndex);
1129 }
1130
1131 // error on side of caution and just set cookie to 0 regardless of what it was
1132 SetClientCookie(targetIndex, g_CT_Cookie, "0");
1133}
1134
1135public DB_Callback_RemoveCTBan(Handle:owner, Handle:hndl, const String:error[], any:client)
1136{
1137 if (hndl == INVALID_HANDLE)
1138 {
1139 LogError("Error handling steamID after CT ban removal: %s", error);
1140 }
1141 else
1142 {
1143 #if DEBUG == 1
1144 if (IsClientInGame(client))
1145 {
1146 LogMessage("CTBan on %N was removed in SQL", client);
1147 }
1148 else
1149 {
1150 LogMessage("CTBan on --- was removed in SQL");
1151 }
1152 #endif
1153 }
1154}
1155
1156public Action:Command_CTBan(client, args)
1157{
1158 if (args < 1)
1159 {
1160 ReplyToCommand(client, "[SM - CT Ban] You have to select a player first!");
1161 }
1162
1163 else if (args < 2)
1164 {
1165 ReplyToCommand(client, "[SM - CT Ban] You have to set a time for the CT ban!");
1166 }
1167
1168 else if (args < 3)
1169 {
1170 ReplyToCommand(client, "[SM - CT Ban] You have to type a reason!");
1171 }
1172
1173 else
1174 {
1175 new numArgs = GetCmdArgs();
1176 decl String:target[64];
1177 GetCmdArg(1, target, sizeof(target));
1178 decl String:sBanTime[16];
1179 GetCmdArg(2, sBanTime, sizeof(sBanTime));
1180 new iBanTime = StringToInt(sBanTime);
1181 new String:sReasonStr[200];
1182 decl String:sArgPart[200];
1183 for (new arg = 3; arg <= numArgs; arg++)
1184 {
1185 GetCmdArg(arg, sArgPart, sizeof(sArgPart));
1186 Format(sReasonStr, sizeof(sReasonStr), "%s %s", sReasonStr, sArgPart);
1187 }
1188
1189 decl String:clientName[MAX_TARGET_LENGTH], target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
1190 target_count = ProcessTargetString(target, client, target_list, MAXPLAYERS, 0, clientName, sizeof(clientName), tn_is_ml);
1191 // make sure we have exactly one target here.. we don't want to CT ban lots of people
1192 if ((target_count != 1))
1193 {
1194 ReplyToTargetError(client, target_count);
1195 }
1196 else
1197 {
1198 if(target_list[0] && IsClientInGame(target_list[0]))
1199 {
1200 // check if the cookies are ready
1201 if (AreClientCookiesCached(target_list[0]))
1202 {
1203 decl String:isBanned[3];
1204 GetClientCookie(target_list[0], g_CT_Cookie, isBanned, sizeof(isBanned));
1205 new banFlag = StringToInt(isBanned);
1206 if (banFlag)
1207 {
1208 ReplyToCommand(client, CHAT_BANNER, "Already CT Banned", target_list[0]);
1209 }
1210 else
1211 {
1212 PerformCTBan(target_list[0], client, iBanTime, _, sReasonStr);
1213 }
1214 }
1215 else
1216 {
1217 ReplyToCommand(client, CHAT_BANNER, "Cookie Status Unavailable");
1218 }
1219 }
1220 }
1221 }
1222 return Plugin_Handled;
1223}
1224
1225PerformCTBan(client, adminclient, banTime=0, reason=0, String:manualReason[]="")
1226{
1227 // set cookie to ban
1228 SetClientCookie(client, g_CT_Cookie, "1");
1229
1230 decl String:targetSteam[32];
1231 GetClientAuthId(client, AuthId_Steam2, targetSteam, sizeof(targetSteam));
1232
1233 // check if they're on CT team
1234 if (GetClientTeam(client) == CS_TEAM_CT)
1235 {
1236 if (IsPlayerAlive(client))
1237 {
1238 // strip their weapons so they cannot gunplant after death
1239 new wepIdx;
1240 for (new i; i < 4; i++)
1241 {
1242 if ((wepIdx = GetPlayerWeaponSlot(client, i)) != -1)
1243 {
1244 RemovePlayerItem(client, wepIdx);
1245 AcceptEntityInput(wepIdx, "Kill");
1246 }
1247 }
1248
1249 ForcePlayerSuicide(client);
1250 }
1251 ChangeClientTeam(client, CS_TEAM_T);
1252 }
1253
1254 decl String:sReason[128];
1255 if (strlen(manualReason) > 0)
1256 {
1257 Format(sReason, sizeof(sReason), "%s", manualReason);
1258 }
1259 // or else they picked a reason # from the admin menu
1260 else
1261 {
1262 switch (reason)
1263 {
1264 case 1:
1265 {
1266 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 1", adminclient);
1267 }
1268 case 2:
1269 {
1270 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 2", adminclient);
1271 }
1272 case 3:
1273 {
1274 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 3", adminclient);
1275 }
1276 case 4:
1277 {
1278 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 4", adminclient);
1279 }
1280 case 5:
1281 {
1282 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 5", adminclient);
1283 }
1284 case 6:
1285 {
1286 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 6", adminclient);
1287 }
1288 case 7:
1289 {
1290 Format(sReason, sizeof(sReason), "%T", "CT Ban Reason 7", adminclient);
1291 }
1292 default:
1293 {
1294 Format(sReason, sizeof(sReason), "No reason given.");
1295 }
1296 }
1297 }
1298
1299 new timestamp = GetTime();
1300
1301 if(adminclient && IsClientInGame(adminclient))
1302 {
1303 decl String:adminSteam[32];
1304 GetClientAuthId(adminclient, AuthId_Steam2, adminSteam, sizeof(adminSteam));
1305
1306 #if USESQL == 1
1307 decl String:logQuery[350];
1308 decl String:temp[300];
1309 new String:escapedPerpName[300];
1310 new String:escapedAdminName[300];
1311 new String:escapedReason[300];
1312 Format(temp, sizeof(temp), "%N", client);
1313 SQL_EscapeString(gH_BanDatabase, temp, escapedPerpName, sizeof(escapedPerpName));
1314 Format(temp, sizeof(temp), "%N", adminclient);
1315 SQL_EscapeString(gH_BanDatabase, temp, escapedAdminName, sizeof(escapedAdminName));
1316 Format(temp, sizeof(temp), "%s", sReason);
1317 SQL_EscapeString(gH_BanDatabase, temp, escapedReason, sizeof(escapedReason));
1318 Format(logQuery, sizeof(logQuery), "INSERT INTO %s (timestamp, perp_steamid, perp_name, admin_steamid, admin_name, bantime, timeleft, reason) VALUES (%d, '%s', '%s', '%s', '%s', %d, %d, '%s')", g_sLogTableName, timestamp, targetSteam, escapedPerpName, adminSteam, escapedAdminName, banTime, banTime, escapedReason);
1319 #if DEBUG == 1
1320 LogMessage("log query: %s", logQuery);
1321 #endif
1322 SQL_TQuery(gH_BanDatabase, DB_Callback_CTBan, logQuery, client);
1323 #endif
1324 LogMessage("%N (%s) has issued a CT ban on %N (%s) for %d minutes for %s.", adminclient, adminSteam, client, targetSteam, banTime, sReason);
1325 }
1326 else
1327 {
1328 #if USESQL == 1
1329 decl String:logQuery[350];
1330 decl String:temp[300];
1331 new String:escapedPerpName[300];
1332 new String:escapedReason[300];
1333 Format(temp, sizeof(temp), "%N", client);
1334 SQL_EscapeString(gH_BanDatabase, temp, escapedPerpName, sizeof(escapedPerpName));
1335 Format(temp, sizeof(temp), "%s", sReason);
1336 SQL_EscapeString(gH_BanDatabase, temp, escapedReason, sizeof(escapedReason));
1337 Format(logQuery, sizeof(logQuery), "INSERT INTO %s (timestamp, perp_steamid, perp_name, admin_steamid, admin_name, bantime, reason) VALUES (%d, '%s', '%s', 'STEAM_0:1:1', 'Console', %d, %d, '%s')", g_sLogTableName, timestamp, targetSteam, escapedPerpName, banTime, banTime, escapedReason);
1338 #if DEBUG == 1
1339 LogMessage("log query: %s", logQuery);
1340 #endif
1341 SQL_TQuery(gH_BanDatabase, DB_Callback_CTBan, logQuery, client);
1342 #endif
1343 LogMessage("Console has issued a CT ban on %N (%s) for %d.", client, targetSteam, banTime);
1344 }
1345
1346 // check if there is a time
1347 if (banTime > 0)
1348 {
1349 ShowActivity2(adminclient, "[SM] ", "%t", "Temporary CT Ban", client, banTime, sReason);
1350 PrintToChat(client, "\x01[\x04CT Ban\x01] You have been CT banned for \x02%d \x01minutes spent alive because of: \x02%s.", banTime, sReason);
1351 PrintToChat(client, "\x01[\x04CT Ban\x01] Please refer to \x02https://extreme-network.net/sourcebans \x01for further details.");
1352 // save in local quick-access array
1353 PushArrayCell(gA_TimedBanLocalList, client);
1354 gA_LocalTimeRemaining[client] = banTime;
1355
1356 #if USESQL == 1
1357 // save in long-term database (already guaranteed to run only once per steam ID)
1358 decl String:query[255];
1359 Format(query, sizeof(query), "INSERT INTO %s (steamid, ctbantime) VALUES ('%s', %d)", g_sTimesTableName, targetSteam, banTime);
1360 #if DEBUG == 1
1361 LogMessage("ctban query: %s", query);
1362 #endif
1363 SQL_TQuery(gH_BanDatabase, DB_Callback_CTBan, query, client);
1364
1365 #else
1366
1367 new iSteamArrayIndex = PushArrayString(gA_TimedBanSteamList, targetSteam);
1368 SetArrayCell(gA_TimedBanSteamList, iSteamArrayIndex, banTime, 22);
1369 #endif
1370 }
1371
1372 else
1373 {
1374 ShowActivity2(adminclient, "[SM] ", "%t", "Permanent CT Ban", client, sReason);
1375 }
1376}
1377
1378public DB_Callback_CTBan(Handle:owner, Handle:hndl, const String:error[], any:client)
1379{
1380 if (hndl == INVALID_HANDLE)
1381 {
1382 LogError("Error writing CTBan to Timed Ban database: %s", error);
1383 }
1384 else
1385 {
1386 #if DEBUG == 1
1387 if (IsClientInGame(client))
1388 {
1389 LogMessage("SQL CTBan: Updated database with CT Ban for %N", client);
1390 }
1391 #endif
1392 }
1393}
1394
1395public Action:Command_IsCTBanned(client, args)
1396{
1397 if ((args < 1) || !args)
1398 {
1399 ReplyToCommand(client, "[SM] Usage: sm_isbanned <player>");
1400 }
1401 else
1402 {
1403 decl String:target[64];
1404 GetCmdArg(1, target, sizeof(target));
1405
1406 decl String:clientName[MAX_TARGET_LENGTH], target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
1407 target_count = ProcessTargetString(target, client, target_list, MAXPLAYERS, 0, clientName, sizeof(clientName), tn_is_ml);
1408 // make sure we have exactly one target here
1409 if (target_count != 1)
1410 {
1411 ReplyToTargetError(client, target_count);
1412 }
1413 else
1414 {
1415 if(target_list[0] && IsClientInGame(target_list[0]))
1416 {
1417 if (AreClientCookiesCached(target_list[0]))
1418 {
1419 decl String:isBanned[3];
1420 GetClientCookie(target_list[0], g_CT_Cookie, isBanned, sizeof(isBanned));
1421 new banFlag = StringToInt(isBanned);
1422 if (banFlag)
1423 {
1424 // find the time if any
1425 if (gA_LocalTimeRemaining[target_list[0]] <= 0)
1426 {
1427 ReplyToCommand(client, CHAT_BANNER, "Permanent CT Ban", target_list[0]);
1428 }
1429 else
1430 {
1431 ReplyToCommand(client, CHAT_BANNER, "Is Banned Check", target_list[0], gA_LocalTimeRemaining[target_list[0]]);
1432 }
1433 }
1434 else
1435 {
1436 ReplyToCommand(client, CHAT_BANNER, "Not CT Banned", target_list[0]);
1437 }
1438 }
1439 else
1440 {
1441 ReplyToCommand(client, CHAT_BANNER, "Cookie Status Unavailable");
1442 }
1443 }
1444 else
1445 {
1446 ReplyToCommand(client, CHAT_BANNER, "Unable to target");
1447 }
1448 }
1449 }
1450
1451 return Plugin_Handled;
1452}
1453
1454public Action:Command_CheckJoin(client, const String:command[], args)
1455{
1456 // Check to see if we should continue (not a listen server, is in game, not a bot, if cookies are cached, and we're enabled)
1457 if(!client || !IsClientInGame(client) || IsFakeClient(client) || !AreClientCookiesCached(client) || !GetConVarBool(gH_Cvar_Enabled))
1458 {
1459 return Plugin_Continue;
1460 }
1461
1462 // Get the target team
1463 decl String:teamString[3];
1464 GetCmdArg(1, teamString, sizeof(teamString));
1465 new Target_Team = StringToInt(teamString);
1466
1467 decl String:sCookie[5];
1468 GetClientCookie(client, g_CT_Cookie, sCookie, sizeof(sCookie));
1469 new iBanStatus = StringToInt(sCookie);
1470
1471 // check for an active ban to send a mesage
1472 if ((Target_Team == CS_TEAM_SPECTATOR || Target_Team == CS_TEAM_T) && iBanStatus)
1473 {
1474 // display them a message about the ban
1475 new iTimeBanned = GetClientCookieTime(client, g_CT_Cookie);
1476 decl String:sTimeBanned[150];
1477 FormatTime(sTimeBanned, sizeof(sTimeBanned), NULL_STRING, iTimeBanned);
1478 decl String:sJoinBanMsg[100];
1479 GetConVarString(gH_Cvar_JoinBanMessage, sJoinBanMsg, sizeof(sJoinBanMsg));
1480 PrintHintText(client, "%t", "Last CT Banned On", sTimeBanned, sJoinBanMsg);
1481 }
1482 // otherwise they joined CT or auto-select and are banned
1483 else if (iBanStatus)
1484 {
1485 if(strcmp(gS_SoundPath, ""))
1486 {
1487 decl String:buffer[PLATFORM_MAX_PATH + 5];
1488 Format(buffer, sizeof(buffer), "play %s", gS_SoundPath);
1489 ClientCommand(client, buffer);
1490 }
1491 PrintCenterText(client, "%t", "Enforcing CT Ban");
1492 PrintToChat(client, "%t", "Enforcing CT Ban");
1493 UTIL_TeamMenu(client);
1494 return Plugin_Stop;
1495 }
1496
1497 return Plugin_Continue;
1498}
1499
1500// This helper procedure will re-display the team join menu
1501// and is equivalent to what ClientCommand(client, "chooseteam") did in the past
1502UTIL_TeamMenu(client)
1503{
1504 new clients[1];
1505 new Handle:bf;
1506 clients[0] = client;
1507 bf = StartMessage("VGUIMenu", clients, 1);
1508
1509 if (GetUserMessageType() == UM_Protobuf)
1510 {
1511 PbSetString(bf, "name", "team");
1512 PbSetBool(bf, "show", true);
1513 }
1514 else
1515 {
1516 BfWriteString(bf, "team"); // panel name
1517 BfWriteByte(bf, 1); // bShow
1518 BfWriteByte(bf, 0); // count
1519 }
1520
1521 EndMessage();
1522
1523}
1524
1525// figure out if we can use the handy native SetAuthIdCookie
1526bool:IsSetAuthIdNativePresent()
1527{
1528 if (GetFeatureStatus(FeatureType_Native, "SetAuthIdCookie") == FeatureStatus_Available)
1529 {
1530 return true;
1531 }
1532 return false;
1533}
1534
1535public int Native_IsBannedFromCT( Handle plugin, int numParams )
1536{
1537 int client = GetNativeCell( 1 );
1538
1539 if ( IsClientInGame( client ) )
1540 {
1541 char sCookie[ 5 ];
1542 GetClientCookie( client, g_CT_Cookie, sCookie, sizeof( sCookie ) );
1543 int iBanStatus = StringToInt( sCookie );
1544
1545 return iBanStatus;
1546 }
1547
1548 else
1549 {
1550 ThrowNativeError( 0, "Client %d is not connected.", client );
1551 }
1552
1553 return view_as<int>( false );
1554}