· 9 years ago · Nov 18, 2016, 03:56 AM
1/*
2 [CS:S] CT Ban
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 stay CT banned for even longer.)
13 - The timed CT bans are stored in a SQL table for stateful access.
14 - The plugin logs CT ban information separately from your regular logs in a file called CTBans.log in your regular addons/sourcemod/logs directory.
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 default driver.
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 Settings:
40 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.
41 sm_ctban_soundfile, <path>: The path to the soundfile to play when denying a team-change request. Set to "" to disable.
42 sm_ctban_joinbanmsg, <message>: This message is appended to a time-stamp when a CT banned user joins the server.
43
44 Special Thanks:
45 Azelphur for the idea of using bitmasks to improve efficiency and snippets of cross-mod code.
46 Kigen for the idea of CT banning based on time spent alive.
47 FLOOR_MASTER for the logging snippet borrowed from the PermaMute plugin.
48
49 Future Considerations:
50 More configurability in selecting databases
51
52 Change Log:
53 1.4.1
54
55*/
56
57// to do
58// COMPLETE REWORK with bitmasks
59// let people disable teams with teamnames (sm_teamban_
60// add table prefixes
61// specify SQL database name
62
63#pragma semicolon 1
64#define CHAT_BANNER "\x03[CTBan] \x01%t"
65
66#include <sourcemod>
67#include <clientprefs>
68#include <sdktools>
69#include <adminmenu>
70#include <cstrike>
71
72#define PLUGIN_VERSION "1.4.4"
73
74// compile directives:
75// (set DEBUG and OCAOFF to 1 for best debugging results)
76// (setting USESQL to 0 is not recommended)
77#define DEBUG 0
78#define OCAOFF 0
79#define USESQL 1
80
81new Handle:g_CT_Cookie = INVALID_HANDLE;
82new Handle:g_CT_Cookie_Reason = INVALID_HANDLE;
83new Handle:gH_Cvar_Enabled = INVALID_HANDLE;
84new Handle:g_Handles[MAXPLAYERS+1];
85new Handle:gH_TopMenu = INVALID_HANDLE;
86new Handle:gH_Cvar_SoundName = INVALID_HANDLE;
87new String:gS_SoundPath[PLATFORM_MAX_PATH];
88new Handle:gH_Cvar_JoinBanMessage = INVALID_HANDLE;
89
90new Handle:gA_DNames = INVALID_HANDLE;
91new Handle:gA_DSteamIDs = INVALID_HANDLE;
92new Handle:gH_CP_DataBase = INVALID_HANDLE;
93new Handle:gH_TimedBanDataBase = INVALID_HANDLE;
94new g_iCookieIndex;
95
96new Handle:gA_TimedBanLocalList = INVALID_HANDLE;
97new gA_LocalTimeRemaining[MAXPLAYERS+1];
98#if USESQL == 0
99new Handle:gA_TimedBanSteamList = INVALID_HANDLE;
100#endif
101new gA_CTBanTargetUserId[MAXPLAYERS+1];
102new gA_CTBanTimeLength[MAXPLAYERS+1];
103
104public Plugin:myinfo =
105{
106 name = "CT Ban",
107 author = "databomb",
108 description = "Allows admins to ban players from joining the CT team.",
109 version = PLUGIN_VERSION,
110 url = "vintagejailbreak.org"
111};
112
113public OnPluginStart()
114{
115 CreateConVar("sm_ctban_version", PLUGIN_VERSION, "CT Ban Version", FCVAR_SPONLY|FCVAR_DONTRECORD|FCVAR_REPLICATED|FCVAR_NOTIFY);
116 gH_Cvar_Enabled = CreateConVar("sm_ctban_enable","1","Enables CT bans cookie handling", FCVAR_PLUGIN);
117 gH_Cvar_SoundName = CreateConVar("sm_ctban_soundfile", "buttons/button11.wav", "The name of the sound to play when an action is denied",FCVAR_PLUGIN);
118 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);
119
120 g_CT_Cookie = RegClientCookie("Banned_From_CT", "Tells if you are restricted from joining the CT team", CookieAccess_Protected);
121 g_CT_Cookie_Reason = RegClientCookie("Banned_From_CT_Reason", "Reason of your team ban", CookieAccess_Protected);
122
123 HookEvent("player_spawn", Event_PlayerSpawn);
124
125 RegAdminCmd("sm_ctban", Command_CTBan, ADMFLAG_SLAY, "sm_ctban <player> <optional: time> <optional: reason>- Bans a player from being a CT.");
126 RegAdminCmd("sm_isbanned", Command_IsCTBanned, ADMFLAG_CHAT, "sm_isbanned <player> - Lets you know if a player is banned from CT team.");
127 RegAdminCmd("sm_removectban", Command_UnCTBan, ADMFLAG_SLAY, "sm_removectban <player> - Unrestricts a player from being a CT.");
128 RegAdminCmd("sm_unctban", Command_UnCTBan, ADMFLAG_SLAY, "sm_unctban <player> - Unrestricts a player from being a CT.");
129 RegAdminCmd("sm_rageban", Command_RageBan, ADMFLAG_SLAY, "sm_rageban <player> - Allows you to ban those who rage quit.");
130
131 LoadTranslations("ctban.phrases");
132 LoadTranslations("common.phrases");
133
134 // create arrays for the rage bans
135 gA_DNames = CreateArray(MAX_TARGET_LENGTH);
136 gA_DSteamIDs = CreateArray(22);
137 g_iCookieIndex = 0;
138
139 // Hook this to block joins when player is banned
140 AddCommandListener(Command_CheckJoin, "jointeam");
141
142 // create local array for timed bans
143 // block 0: client index
144 gA_TimedBanLocalList = CreateArray(2);
145 for (new idx = 1; idx <= MaxClients; idx++)
146 {
147 gA_LocalTimeRemaining[idx] = 0;
148 gA_CTBanTargetUserId[idx] = 0;
149 }
150 #if USESQL == 0
151 // steam array structure:
152 // blocks 0-21: steamID string
153 // block 22: ban time remaining
154 gA_TimedBanSteamList = CreateArray(23);
155 #endif
156
157 // periodic timer to handle timed bans
158 CreateTimer(60.0, CheckTimedCTBans, _, TIMER_REPEAT);
159
160 /* Account for late loading */
161 new Handle:topmenu;
162 if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
163 {
164 OnAdminMenuReady(topmenu);
165 }
166}
167
168public Action:Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
169{
170 new client = GetClientOfUserId(GetEventInt(event, "userid"));
171 decl String:sSteamID[32];
172 GetClientAuthString(client, sSteamID, sizeof(sSteamID));
173 decl String:query[255];
174 Format(query, sizeof(query), "SELECT ctbantime, reason FROM CTBans WHERE steamid = '%s' AND active=1", sSteamID);
175 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_OnClientAuthed, query, _:client);
176 return Plugin_Continue;
177}
178
179// consider if someone does a 'retry' and clientprefs is accessinsg on its ClientConnectCallback and this is also trying to set a value
180public OnClientAuthorized(client, const String:sSteamID[])
181{
182 #if OCAOFF == 0
183 // check if the Steam ID is in the Rage Ban list
184 new iNeedle = FindStringInArray(gA_DSteamIDs, sSteamID);
185 if (iNeedle != -1)
186 {
187 RemoveFromArray(gA_DNames, iNeedle);
188 RemoveFromArray(gA_DSteamIDs, iNeedle);
189 #if DEBUG == 1
190 LogMessage("removed %N from Rage Ban list for re-connecting to the server", client);
191 #endif
192 }
193 #endif
194
195 #if USESQL == 1
196 // check if the Steam ID is in the Timed Ban list
197 decl String:query[255];
198 Format(query, sizeof(query), "SELECT ctbantime, reason FROM CTBans WHERE steamid = '%s' AND active=1", sSteamID);
199 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_OnClientAuthed, query, _:client);
200
201 #else
202
203 new iSteamArrayIndex = FindStringInArray(gA_TimedBanSteamList, sSteamID);
204 if (iSteamArrayIndex != -1)
205 {
206 gA_LocalTimeRemaining[client] = GetArrayCell(gA_TimedBanSteamList, iSteamArrayIndex, 22);
207 #if DEBUG == 1
208 LogMessage("%N joined with %i time remaining on ban", client, gA_LocalTimeRemaining[client]);
209 #endif
210 }
211 #endif
212}
213
214public DB_Callback_OnClientAuthed(Handle:owner, Handle:hndl, const String:error[], any:client)
215{
216 if (hndl == INVALID_HANDLE)
217 {
218 LogError("Error in OnClientAuthorized query: %s", error);
219 }
220 else
221 {
222 new iRowCount = SQL_GetRowCount(hndl);
223 #if DEBUG == 1
224 LogMessage("SQL Auth: %d row count", iRowCount);
225 #endif
226 if (iRowCount)
227 {
228 SQL_FetchRow(hndl);
229 new iBanTimeRemaining = SQL_FetchInt(hndl, 0);
230
231 decl String:reason[256];
232 SQL_FetchString(hndl, 1, reason, sizeof(reason));
233
234 decl String:cookie[32];
235 GetClientCookie(client, g_CT_Cookie, cookie, sizeof(cookie));
236
237 if (!StrEqual(cookie, "1"))
238 {
239 SetClientCookie(client, g_CT_Cookie, "1");
240 SetClientCookie(client, g_CT_Cookie_Reason, reason);
241 }
242
243 if (GetClientTeam(client) == CS_TEAM_CT)
244 {
245 if (IsPlayerAlive(client))
246 {
247 new wepIdx;
248 for (new i; i < 4; i++)
249 {
250 if ((wepIdx = GetPlayerWeaponSlot(client, i)) != -1)
251 {
252 RemovePlayerItem(client, wepIdx);
253 AcceptEntityInput(wepIdx, "Kill");
254 }
255 }
256
257 ForcePlayerSuicide(client);
258 }
259 ChangeClientTeam(client, CS_TEAM_T);
260 }
261
262 if(iBanTimeRemaining > 0)
263 {
264 #if DEBUG == 1
265 LogMessage("SQL Auth: %N joined with %i time remaining on ban", client, iBanTimeRemaining);
266 #endif
267 // update local time
268 PushArrayCell(gA_TimedBanLocalList, client);
269 gA_LocalTimeRemaining[client] = iBanTimeRemaining;
270 }
271 else
272 {
273 gA_LocalTimeRemaining[client] = 0;
274 }
275 }
276 else
277 {
278 decl String:cookie[32];
279 GetClientCookie(client, g_CT_Cookie, cookie, sizeof(cookie));
280
281 if (StrEqual(cookie, "1"))
282 {
283 SetClientCookie(client, g_CT_Cookie, "0");
284 SetClientCookie(client, g_CT_Cookie_Reason, "");
285 }
286 }
287 }
288}
289
290public AdminMenu_RageBan(Handle:topmenu, TopMenuAction:action, TopMenuObject:object_id, param, String:buffer[], maxlength)
291{
292 if (action == TopMenuAction_DisplayOption)
293 {
294 Format(buffer, maxlength, "Rage Ban");
295 }
296 else if (action == TopMenuAction_SelectOption)
297 {
298 DisplayRageBanMenu(param, GetArraySize(gA_DNames));
299 }
300}
301
302void:DisplayRageBanMenu(Client, ArraySize)
303{
304 if (ArraySize == 0)
305 {
306 PrintToChat(Client, CHAT_BANNER, "No Targets");
307 }
308 else
309 {
310 new Handle:menu = CreateMenu(MenuHandler_RageBan);
311
312 SetMenuTitle(menu, "%T", "Rage Ban Menu Title", Client);
313 SetMenuExitBackButton(menu, true);
314
315 for (new ArrayIndex = 0; ArrayIndex < ArraySize; ArrayIndex++)
316 {
317 decl String:sName[MAX_TARGET_LENGTH];
318 GetArrayString(gA_DNames, ArrayIndex, sName, sizeof(sName));
319 decl String:sSteamID[22];
320 GetArrayString(gA_DSteamIDs, ArrayIndex, sSteamID, sizeof(sSteamID));
321 AddMenuItem(menu, sSteamID, sName);
322 }
323
324 DisplayMenu(menu, Client, MENU_TIME_FOREVER);
325 }
326}
327
328public MenuHandler_RageBan(Handle:menu, MenuAction:action, param1, param2)
329{
330 if (action == MenuAction_End)
331 {
332 CloseHandle(menu);
333 }
334 else if (action == MenuAction_Cancel)
335 {
336 if ((param2 == MenuCancel_ExitBack) && (gH_TopMenu != INVALID_HANDLE))
337 {
338 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
339 }
340 }
341 else if (action == MenuAction_Select)
342 {
343 decl String:sInfoString[22];
344 GetMenuItem(menu, param2, sInfoString, sizeof(sInfoString));
345
346 // determine if they're in the clientprefs SQL database yet
347 if (gH_CP_DataBase != INVALID_HANDLE)
348 {
349 decl String:query[255];
350 Format(query, sizeof(query), "SELECT value FROM sm_cookie_cache WHERE player = '%s' and cookie_id = '%i'", sInfoString, g_iCookieIndex);
351 new Handle:TheDataPack = CreateDataPack();
352 // authID
353 WritePackString(TheDataPack, sInfoString);
354 // admin who banned (client index)
355 WritePackCell(TheDataPack, param1);
356 // array index to CTBan
357 WritePackCell(TheDataPack, param2);
358 SQL_TQuery(gH_CP_DataBase, CP_Callback_CheckBan, query, TheDataPack);
359 }
360
361 #if DEBUG == 1
362 PrintToChat(param1, CHAT_BANNER, "Ready to CT Ban", sInfoString);
363 #endif
364 }
365}
366
367public CP_Callback_CheckBan(Handle:owner, Handle:hndl, const String:error[], any:stringPack)
368{
369 if (hndl == INVALID_HANDLE)
370 {
371 LogError("CT Ban query had a failure: %s", error);
372 CloseHandle(stringPack);
373 }
374 else
375 {
376 ResetPack(stringPack);
377 decl String:authID[22];
378 ReadPackString(stringPack, authID, sizeof(authID));
379 new iAdminIndex = ReadPackCell(stringPack);
380 new iArrayBanIndex = ReadPackCell(stringPack);
381 CloseHandle(stringPack);
382
383 new iTimeStamp = GetTime();
384
385 new iRowCount = SQL_GetRowCount(hndl);
386 if (iRowCount)
387 {
388 #if DEBUG == 1
389 SQL_FetchRow(hndl);
390 new iCTBanStatus = SQL_FetchInt(hndl, 0);
391 LogMessage("CTBan status on player is currently %i. Will do UPDATE on %s", iCTBanStatus, authID);
392 #endif
393
394 decl String:query[255];
395 Format(query, sizeof(query), "UPDATE sm_cookie_cache SET value = '1', timestamp = %i WHERE player = '%s' AND cookie_id = '%i'", iTimeStamp, authID, g_iCookieIndex);
396 #if DEBUG == 1
397 LogMessage("Query to run: %s", query);
398 #endif
399 SQL_TQuery(gH_CP_DataBase, CP_Callback_IssueBan, query);
400 }
401 else
402 {
403 #if DEBUG == 1
404 LogMessage("couldn't find steamID in database, need to INSERT");
405 #endif
406
407 decl String:query[255];
408 Format(query, sizeof(query), "INSERT INTO sm_cookie_cache (player, cookie_id, value, timestamp) VALUES ('%s', %i, '1', %i)", authID, g_iCookieIndex, iTimeStamp);
409 #if DEBUG == 1
410 LogMessage("Query to run: %s", query);
411 #endif
412 SQL_TQuery(gH_CP_DataBase, CP_Callback_IssueBan, query);
413 }
414
415 // log this info
416 decl String:sTargetName[MAX_TARGET_LENGTH];
417 GetArrayString(gA_DNames, iArrayBanIndex, sTargetName, sizeof(sTargetName));
418 decl String:adminSteamID[22];
419 GetClientAuthString(iAdminIndex, adminSteamID, sizeof(adminSteamID));
420 LogBanChange("%N (%s) has issued a rage ban on %s (%s) indefinitely.", iAdminIndex, adminSteamID, sTargetName, authID);
421
422 ShowActivity2(iAdminIndex, "[SM] ", "%t", "Rage Ban", sTargetName);
423
424 // clear the position from array
425 RemoveFromArray(gA_DNames, iArrayBanIndex);
426 RemoveFromArray(gA_DSteamIDs, iArrayBanIndex);
427 #if DEBUG == 1
428 LogMessage("Removed %i index from rage ban menu.", iArrayBanIndex);
429 #endif
430 }
431}
432
433public CP_Callback_IssueBan(Handle:owner, Handle:hndl, const String:error[], any:data)
434{
435 if (hndl == INVALID_HANDLE)
436 {
437 LogError("Error writing to database: %s", error);
438 }
439 else
440 {
441 #if DEBUG == 1
442 LogMessage("succesfully wrote to the database");
443 #endif
444 }
445}
446
447public Action:Command_RageBan(client, args)
448{
449 new iArraySize = GetArraySize(gA_DNames);
450 if (iArraySize == 0)
451 {
452 ReplyToCommand(client, CHAT_BANNER, "No Targets");
453 return Plugin_Handled;
454 }
455
456 if (!args)
457 {
458 if (client)
459 {
460 DisplayRageBanMenu(client, iArraySize);
461 }
462 else
463 {
464 ReplyToCommand(client, CHAT_BANNER, "Feature Not Available On Console");
465 }
466 return Plugin_Handled;
467 }
468 else
469 {
470 ReplyToCommand(client, "[SM] Usage: sm_rageban");
471 }
472
473 return Plugin_Handled;
474}
475
476public Action:CheckTimedCTBans(Handle:timer)
477{
478 // check if anyone has a time
479 new iTimeArraySize = GetArraySize(gA_TimedBanLocalList);
480
481 // credit for this idea goes to Kigen
482 for (new idx = 0; idx < iTimeArraySize; idx++)
483 {
484 new iBannedClientIndex = GetArrayCell(gA_TimedBanLocalList, idx);
485 if (IsClientInGame(iBannedClientIndex))
486 {
487 if (IsPlayerAlive(iBannedClientIndex))
488 {
489 gA_LocalTimeRemaining[iBannedClientIndex]--;
490 #if DEBUG == 1
491 LogMessage("found alive time banned client with %i remaining", gA_LocalTimeRemaining[iBannedClientIndex]);
492 #endif
493 // check if we should remove the CT ban
494 if (gA_LocalTimeRemaining[iBannedClientIndex] <= 0)
495 {
496 // remove CT ban
497 RemoveFromArray(gA_TimedBanLocalList, idx);
498 iTimeArraySize--;
499 Remove_CTBan(0, iBannedClientIndex, true);
500 #if DEBUG == 1
501 LogMessage("removed CT ban on %N", iBannedClientIndex);
502 #endif
503 }
504 }
505 }
506 }
507}
508
509public OnConfigsExecuted()
510{
511 SQL_TConnect(CP_Callback_Connect, "clientprefs");
512
513 SQL_TConnect(DB_Callback_Connect, "default");
514}
515
516public DB_Callback_Connect(Handle:owner, Handle:hndl, const String:error[], any:data)
517{
518 if (hndl == INVALID_HANDLE)
519 {
520 LogError("Default database database connection failure: %s", error);
521 SetFailState("Error while connecting to default database. Exiting.");
522 }
523 else
524 {
525 gH_TimedBanDataBase = hndl;
526
527 // create database if not already there
528 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_Create, "CREATE TABLE IF NOT EXISTS CTBans (name VARCHAR(64), steamid VARCHAR(22), ctbantime INT(16), reason VARCHAR(256), admin VARCHAR(64), date INT(11), active tinyint(1), PRIMARY KEY (steamid))");
529
530 }
531}
532
533public DB_Callback_Create(Handle:owner, Handle:hndl, const String:error[], any:data)
534{
535 if (hndl == INVALID_HANDLE)
536 {
537 LogError("Error establishing table creation: %s", error);
538 SetFailState("Unable to ascertain creation of table in default database. Exiting.");
539 }
540}
541
542public CP_Callback_Connect(Handle:owner, Handle:hndl, const String:error[], any:data)
543{
544 if (hndl == INVALID_HANDLE)
545 {
546 LogError("Clientprefs database connection failure: %s", error);
547 SetFailState("Error while connecting to clientprefs database. Exiting.");
548 }
549 else
550 {
551 gH_CP_DataBase = hndl;
552
553 // find the Banned_From_CT Cookie id #
554 SQL_TQuery(gH_CP_DataBase, CP_Callback_FindCookie, "SELECT id FROM sm_cookies WHERE name = 'Banned_From_CT'");
555 }
556}
557
558public CP_Callback_FindCookie(Handle:owner, Handle:hndl, const String:error[], any:data)
559{
560 if (hndl == INVALID_HANDLE)
561 {
562 LogError("Cookie query failure: %s", error);
563 }
564 else
565 {
566 new iRowCount = SQL_GetRowCount(hndl);
567 if (iRowCount)
568 {
569 SQL_FetchRow(hndl);
570 new CookieIDIndex = SQL_FetchInt(hndl, 0);
571 #if DEBUG == 1
572 LogMessage("found cookie index as %i", CookieIDIndex);
573 #endif
574 g_iCookieIndex = CookieIDIndex;
575 }
576 else
577 {
578 LogError("Could not find the cookie index. Rageban functionality disabled.");
579 }
580 }
581}
582
583public OnMapStart()
584{
585 // pre-cache deny sound
586 decl String:buffer[PLATFORM_MAX_PATH];
587 GetConVarString(gH_Cvar_SoundName, gS_SoundPath, sizeof(gS_SoundPath));
588 if(strcmp(gS_SoundPath, ""))
589 {
590 PrecacheSound(gS_SoundPath, true);
591 Format(buffer, sizeof(buffer), "sound/%s", gS_SoundPath);
592 AddFileToDownloadsTable(buffer);
593 }
594}
595
596public OnAdminMenuReady(Handle:topmenu)
597{
598 /* Block us from being called twice */
599 if (topmenu == gH_TopMenu)
600 {
601 return;
602 }
603
604 /* Save the Handle */
605 gH_TopMenu = topmenu;
606
607 /* Build the "Player Commands" category */
608 new TopMenuObject:frequent_commands = FindTopMenuCategory(gH_TopMenu, "ts_commands");
609
610 if (frequent_commands != INVALID_TOPMENUOBJECT)
611 {
612 AddToTopMenu(gH_TopMenu,
613 "sm_ctban",
614 TopMenuObject_Item,
615 AdminMenu_CTBan,
616 frequent_commands,
617 "sm_ctban",
618 ADMFLAG_SLAY);
619 }
620
621 /* Build the "Player Commands" category */
622 new TopMenuObject:player_commands = FindTopMenuCategory(gH_TopMenu, ADMINMENU_PLAYERCOMMANDS);
623
624 if (player_commands != INVALID_TOPMENUOBJECT)
625 {
626 AddToTopMenu(gH_TopMenu,
627 "sm_rageban",
628 TopMenuObject_Item,
629 AdminMenu_RageBan,
630 player_commands,
631 "sm_rageban",
632 ADMFLAG_SLAY);
633
634 if (frequent_commands == INVALID_TOPMENUOBJECT)
635 {
636 AddToTopMenu(gH_TopMenu,
637 "sm_ctban",
638 TopMenuObject_Item,
639 AdminMenu_CTBan,
640 player_commands,
641 "sm_ctban",
642 ADMFLAG_SLAY);
643 }
644 }
645}
646
647public AdminMenu_CTBan(Handle:topmenu,
648 TopMenuAction:action,
649 TopMenuObject:object_id,
650 param,
651 String:buffer[],
652 maxlength)
653{
654 if (action == TopMenuAction_DisplayOption)
655 {
656 Format(buffer, maxlength, "CT Ban");
657 }
658 else if (action == TopMenuAction_SelectOption)
659 {
660 DisplayCTBanPlayerMenu(param);
661 }
662}
663
664void:DisplayCTBanPlayerMenu(client)
665{
666 new Handle:menu = CreateMenu(MenuHandler_CTBanPlayerList);
667
668 SetMenuTitle(menu, "%T", "CT Ban Menu Title", client);
669 SetMenuExitBackButton(menu, true);
670
671 AddTargetsToMenu(menu, client, true, false);
672
673 DisplayMenu(menu, client, MENU_TIME_FOREVER);
674}
675
676void:DisplayCTBanTimeMenu(client, targetUserId)
677{
678 new Handle:menu = CreateMenu(MenuHandler_CTBanTimeList);
679
680 SetMenuTitle(menu, "%T", "CT Ban Length Menu", client, GetClientOfUserId(targetUserId));
681 SetMenuExitBackButton(menu, true);
682
683 AddMenuItem(menu, "0", "Permanent");
684 AddMenuItem(menu, "5", "5 Minutes");
685 AddMenuItem(menu, "10", "10 Minutes");
686 AddMenuItem(menu, "30", "30 Minutes");
687 AddMenuItem(menu, "60", "1 Hour");
688 AddMenuItem(menu, "120", "2 Hours");
689 AddMenuItem(menu, "240", "4 Hours");
690
691 DisplayMenu(menu, client, MENU_TIME_FOREVER);
692}
693
694void:DisplayCTBanReasonMenu(client)
695{
696 new Handle:menu = CreateMenu(MenuHandler_CTBanReasonList);
697
698 SetMenuTitle(menu, "%T", "CT Ban Reason Menu", client, GetClientOfUserId(gA_CTBanTargetUserId[client]));
699 SetMenuExitBackButton(menu, true);
700
701 new Handle:file = OpenFile("addons/sourcemod/data/ctban_reasons.txt", "r");
702
703 decl String:sMenuReason[256];
704
705 while(ReadFileLine(file, sMenuReason, sizeof(sMenuReason)))
706 {
707 ReplaceString(sMenuReason, sizeof(sMenuReason), "\x0d", "", false);
708 ReplaceString(sMenuReason, sizeof(sMenuReason), "\x0a", "", false);
709 AddMenuItem(menu, sMenuReason, sMenuReason);
710 }
711
712 CloseHandle(file);
713
714 DisplayMenu(menu, client, MENU_TIME_FOREVER);
715}
716
717public MenuHandler_CTBanReasonList(Handle:menu, MenuAction:action, param1, param2)
718{
719 if (action == MenuAction_End)
720 {
721 CloseHandle(menu);
722 }
723 else if (action == MenuAction_Cancel)
724 {
725 if (param2 == MenuCancel_ExitBack && gH_TopMenu != INVALID_HANDLE)
726 {
727 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
728 }
729 }
730 else if (action == MenuAction_Select)
731 {
732 decl String:sBanChoice[256];
733 GetMenuItem(menu, param2, sBanChoice, sizeof(sBanChoice));
734 new iTimeToBan = gA_CTBanTimeLength[param1];
735 new iTargetIndex = GetClientOfUserId(gA_CTBanTargetUserId[param1]);
736
737 decl String:sBanned[3];
738 GetClientCookie(iTargetIndex, g_CT_Cookie, sBanned, sizeof(sBanned));
739 new banFlag = StringToInt(sBanned);
740 if (!banFlag)
741 {
742 PerformCTBan(iTargetIndex, param1, iTimeToBan, sBanChoice);
743 }
744 else
745 {
746 PrintToChat(param1, CHAT_BANNER, "Already CT Banned", iTargetIndex);
747 }
748 }
749}
750
751public MenuHandler_CTBanPlayerList(Handle:menu, MenuAction:action, param1, param2)
752{
753 if (action == MenuAction_End)
754 {
755 CloseHandle(menu);
756 }
757 else if (action == MenuAction_Cancel)
758 {
759 if (param2 == MenuCancel_ExitBack && gH_TopMenu != INVALID_HANDLE)
760 {
761 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
762 }
763 }
764 else if (action == MenuAction_Select)
765 {
766 decl String:info[32];
767 new userid, target;
768
769 GetMenuItem(menu, param2, info, sizeof(info));
770 userid = StringToInt(info);
771
772 if ((target = GetClientOfUserId(userid)) == 0)
773 {
774 PrintToChat(param1, "[SM] %t", "Player no longer available");
775 }
776 else if (!CanUserTarget(param1, target))
777 {
778 PrintToChat(param1, "[SM] %t", "Unable to target");
779 }
780 else
781 {
782 gA_CTBanTargetUserId[param1] = userid;
783 DisplayCTBanTimeMenu(param1, userid);
784 }
785 }
786}
787
788public MenuHandler_CTBanTimeList(Handle:menu, MenuAction:action, param1, param2)
789{
790 if (action == MenuAction_End)
791 {
792 CloseHandle(menu);
793 }
794 else if (action == MenuAction_Cancel)
795 {
796 if (param2 == MenuCancel_ExitBack && gH_TopMenu != INVALID_HANDLE)
797 {
798 DisplayTopMenu(gH_TopMenu, param1, TopMenuPosition_LastCategory);
799 }
800 }
801 else if (action == MenuAction_Select)
802 {
803 decl String:info[32];
804 GetMenuItem(menu, param2, info, sizeof(info));
805 new iTimeToBan = StringToInt(info);
806 gA_CTBanTimeLength[param1] = iTimeToBan;
807 DisplayCTBanReasonMenu(param1);
808 }
809}
810
811public OnPluginEnd()
812{
813 for(new client = 1; client <= MaxClients; client++)
814 {
815 if(g_Handles[client] != INVALID_HANDLE)
816 {
817 CloseHandle(g_Handles[client]);
818 g_Handles[client] = INVALID_HANDLE;
819 }
820 }
821}
822
823public OnClientPostAdminCheck(client)
824{
825 if (GetConVarBool(gH_Cvar_Enabled))
826 {
827 g_Handles[client] = INVALID_HANDLE;
828 CreateTimer(0.0, CheckBanCookies, client, TIMER_FLAG_NO_MAPCHANGE);
829 }
830}
831
832public OnClientDisconnect(client)
833{
834 decl String:sDisconnectSteamID[22];
835 GetClientAuthString(client, sDisconnectSteamID, sizeof(sDisconnectSteamID));
836
837 if(g_Handles[client] != INVALID_HANDLE)
838 {
839 CloseHandle(g_Handles[client]);
840 g_Handles[client] = INVALID_HANDLE;
841 }
842
843 // add information to rage ban list
844 decl String:sName[MAX_TARGET_LENGTH];
845 GetClientName(client, sName, sizeof(sName));
846
847 // add information to array
848 // if information isn't already in the arrays then add it
849 if (FindStringInArray(gA_DSteamIDs, sDisconnectSteamID) == -1)
850 {
851 PushArrayString(gA_DNames, sName);
852 PushArrayString(gA_DSteamIDs, sDisconnectSteamID);
853
854 if (GetArraySize(gA_DNames) >= 7)
855 {
856 RemoveFromArray(gA_DNames, 0);
857 RemoveFromArray(gA_DSteamIDs, 0);
858 }
859 }
860
861 // check if they were in the timed array
862 new iBannedArrayIndex = FindValueInArray(gA_TimedBanLocalList, client);
863 if (iBannedArrayIndex != -1)
864 {
865 // remove them from the local array
866 RemoveFromArray(gA_TimedBanLocalList, iBannedArrayIndex);
867
868 // make a datapack for the next query
869 new Handle:ClientDisconnectPack = CreateDataPack();
870 WritePackCell(ClientDisconnectPack, client);
871 WritePackString(ClientDisconnectPack, sDisconnectSteamID);
872
873 #if USESQL == 1
874 // update steam array
875 decl String:query[255];
876 Format(query, sizeof(query), "SELECT ctbantime FROM CTBans WHERE steamid = '%s'", sDisconnectSteamID);
877 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_ClientDisconnect, query, ClientDisconnectPack);
878
879 #else
880
881 new iSteamArrayIndex = FindStringInArray(gA_TimedBanSteamList, sDisconnectSteamID);
882 if (iSteamArrayIndex != -1)
883 {
884 if (gA_LocalTimeRemaining[client] <= 0)
885 {
886 RemoveFromArray(gA_TimedBanSteamList, iSteamArrayIndex);
887 }
888 else
889 {
890 SetArrayCell(gA_TimedBanSteamList, iSteamArrayIndex, gA_LocalTimeRemaining[client], 22);
891 }
892 }
893 #endif
894 }
895}
896
897public DB_Callback_ClientDisconnect(Handle:owner, Handle:hndl, const String:error[], any:thePack)
898{
899 if (hndl == INVALID_HANDLE)
900 {
901 LogError("Error with query on client disconnect: %s", error);
902 CloseHandle(thePack);
903 }
904 else
905 {
906 ResetPack(thePack);
907 new client = ReadPackCell(thePack);
908 decl String:sAuthID[22];
909 ReadPackString(thePack, sAuthID, sizeof(sAuthID));
910
911 new iRowCount = SQL_GetRowCount(hndl);
912 if (iRowCount)
913 {
914 #if DEBUG == 1
915 SQL_FetchRow(hndl);
916 new iBanTimeRemaining = SQL_FetchInt(hndl, 0);
917
918 if (IsClientInGame(client))
919 {
920 LogMessage("SQL: %N disconnected with %i time remaining on ban", client, iBanTimeRemaining);
921 }
922 else
923 {
924 LogMessage("SQL: %i client index disconnected with %i time remaining on ban", client, iBanTimeRemaining);
925 }
926 #endif
927
928 if (gA_LocalTimeRemaining[client] <= 0)
929 {
930 // remove steam array
931 decl String:query[255];
932 Format(query, sizeof(query), "UPDATE CTBans SET active=0, ctbantime=0 WHERE steamid = '%s'", sAuthID);
933 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_DisconnectAction, query);
934 }
935 else
936 {
937 // update the time
938 decl String:query[255];
939 Format(query, sizeof(query), "UPDATE CTBans SET ctbantime = %d WHERE steamid = '%s'", gA_LocalTimeRemaining[client], sAuthID);
940 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_DisconnectAction, query);
941 }
942 }
943 }
944}
945
946public DB_Callback_DisconnectAction(Handle:owner, Handle:hndl, const String:error[], any:data)
947{
948 if (hndl == INVALID_HANDLE)
949 {
950 LogError("Error with updating/deleting record after client disconnect: %s", error);
951 }
952}
953
954public Action:CheckBanCookies(Handle:timer, any: client)
955{
956 if (AreClientCookiesCached(client))
957 {
958 ProcessBanCookies(client);
959 }
960 else if(IsClientInGame(client))
961 {
962 CreateTimer(5.0, CheckBanCookies, client, TIMER_FLAG_NO_MAPCHANGE);
963 }
964}
965
966void:ProcessBanCookies(client)
967{
968 if(client && IsClientInGame(client))
969 {
970 decl String:cookie[32];
971 GetClientCookie(client, g_CT_Cookie, cookie, sizeof(cookie));
972
973 if (StrEqual(cookie, "1"))
974 {
975 // check to see if they joined CT
976 if (GetClientTeam(client) == CS_TEAM_CT)
977 {
978 if (IsPlayerAlive(client))
979 {
980 // strip their weapons so they cannot gunplant after death
981 new wepIdx;
982 for (new i; i < 4; i++)
983 {
984 if ((wepIdx = GetPlayerWeaponSlot(client, i)) != -1)
985 {
986 RemovePlayerItem(client, wepIdx);
987 AcceptEntityInput(wepIdx, "Kill");
988 }
989 }
990
991 ForcePlayerSuicide(client);
992 }
993
994 ChangeClientTeam(client, CS_TEAM_T);
995 decl String:sReason[256];
996 GetClientCookie(client, g_CT_Cookie_Reason, sReason, sizeof(sReason));
997 new String:sLength[16] = "Never";
998 if(gA_LocalTimeRemaining[client]>0)
999 {
1000 Format(sLength, sizeof(sLength), "%d days", gA_LocalTimeRemaining[client]/(24*60*60));
1001 }
1002 PrintToChat(client, CHAT_BANNER, "Enforcing CT Ban", sReason, sLength);
1003 }
1004 }
1005 }
1006}
1007
1008public Action:Command_UnCTBan(client, args)
1009{
1010 if (args < 1)
1011 {
1012 ReplyToCommand(client, "[SM] Usage: sm_unctban <player>");
1013 }
1014 else
1015 {
1016 decl String:target[64];
1017 GetCmdArg(1, target, sizeof(target));
1018
1019 decl String:clientName[MAX_TARGET_LENGTH], target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
1020 target_count = ProcessTargetString(target, client, target_list, MAXPLAYERS, 0, clientName, sizeof(clientName), tn_is_ml);
1021 // make sure we have exactly one target here.. we don't want to CT ban lots of people
1022 if (target_count != 1)
1023 {
1024 ReplyToTargetError(client, target_count);
1025 }
1026 else
1027 {
1028 // check if the cookies are ready
1029 if (AreClientCookiesCached(target_list[0]))
1030 {
1031 Remove_CTBan(client, target_list[0]);
1032 }
1033 else
1034 {
1035 ReplyToCommand(client, CHAT_BANNER, "Cookie Status Unavailable");
1036 }
1037 }
1038 }
1039
1040 return Plugin_Handled;
1041}
1042
1043void:Remove_CTBan(adminIndex, targetIndex, bExpired=false)
1044{
1045 decl String:isBanned[3];
1046 GetClientCookie(targetIndex, g_CT_Cookie, isBanned, sizeof(isBanned));
1047 new banFlag = StringToInt(isBanned);
1048
1049 if (banFlag)
1050 {
1051 decl String:targetSteam[22];
1052 GetClientAuthString(targetIndex, targetSteam, sizeof(targetSteam));
1053 LogBanChange("%N has removed the CT ban on %N (%s).", adminIndex, targetIndex, targetSteam);
1054
1055 if (!bExpired)
1056 {
1057 ShowActivity2(adminIndex, "[SM] ", "%t", "CT Ban Removed", targetIndex);
1058 }
1059 else
1060 {
1061 ShowActivity2(adminIndex, "[SM] ", "%t", "CT Ban Auto Removed", targetIndex);
1062 }
1063
1064 // delete from the timedban database if there was one
1065 decl String:query[255];
1066 Format(query, sizeof(query), "UPDATE CTBans SET active=0, ctbantime=0 WHERE steamid = '%s'", targetSteam);
1067 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_RemoveCTBan, query, targetIndex);
1068 }
1069
1070 // error on side of caution and just set cookie to 0 regardless of what it was
1071 SetClientCookie(targetIndex, g_CT_Cookie, "0");
1072}
1073
1074public DB_Callback_RemoveCTBan(Handle:owner, Handle:hndl, const String:error[], any:client)
1075{
1076 if (hndl == INVALID_HANDLE)
1077 {
1078 LogError("Error handling steamID after CT ban removal: %s", error);
1079 }
1080 else
1081 {
1082 #if DEBUG == 1
1083 LogMessage("CTBan on %N was removed in SQL", client);
1084 #endif
1085 }
1086}
1087
1088public Action:Command_CTBan(client, args)
1089{
1090 if (args < 1)
1091 {
1092 ReplyToCommand(client, "[SM] Usage: sm_ctban <player> <time> <reason>");
1093 }
1094 else
1095 {
1096 decl String:target[64];
1097 GetCmdArg(1, target, sizeof(target));
1098 decl String:sBanTime[16];
1099 GetCmdArg(2, sBanTime, sizeof(sBanTime));
1100 new iBanTime = StringToInt(sBanTime);
1101 decl String:sReason[256];
1102 GetCmdArg(3, sReason, sizeof(sReason));
1103
1104 decl String:clientName[MAX_TARGET_LENGTH], target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
1105 target_count = ProcessTargetString(target, client, target_list, MAXPLAYERS, 0, clientName, sizeof(clientName), tn_is_ml);
1106 // make sure we have exactly one target here.. we don't want to CT ban lots of people
1107 if ((target_count != 1))
1108 {
1109 ReplyToTargetError(client, target_count);
1110 }
1111 else
1112 {
1113 if(target_list[0] && IsClientInGame(target_list[0]))
1114 {
1115 // check if the cookies are ready
1116 if (AreClientCookiesCached(target_list[0]))
1117 {
1118 decl String:isBanned[3];
1119 GetClientCookie(target_list[0], g_CT_Cookie, isBanned, sizeof(isBanned));
1120 new banFlag = StringToInt(isBanned);
1121 if (banFlag)
1122 {
1123 ReplyToCommand(client, CHAT_BANNER, "Already CT Banned", target_list[0]);
1124 }
1125 else
1126 {
1127 PerformCTBan(target_list[0], client, iBanTime, sReason);
1128 }
1129 }
1130 else
1131 {
1132 ReplyToCommand(client, CHAT_BANNER, "Cookie Status Unavailable");
1133 }
1134 }
1135 }
1136 }
1137 return Plugin_Handled;
1138}
1139
1140void:PerformCTBan(client, adminclient, banTime=0, String:sReason[])
1141{
1142 // set cookie to ban
1143 SetClientCookie(client, g_CT_Cookie, "1");
1144 SetClientCookie(client, g_CT_Cookie_Reason, sReason);
1145
1146 decl String:targetSteam[22];
1147 GetClientAuthString(client, targetSteam, sizeof(targetSteam));
1148
1149 // check if they're on CT team
1150 if (GetClientTeam(client) == CS_TEAM_CT)
1151 {
1152 if (IsPlayerAlive(client))
1153 {
1154 // strip their weapons so they cannot gunplant after death
1155 new wepIdx;
1156 for (new i; i < 4; i++)
1157 {
1158 if ((wepIdx = GetPlayerWeaponSlot(client, i)) != -1)
1159 {
1160 RemovePlayerItem(client, wepIdx);
1161 AcceptEntityInput(wepIdx, "Kill");
1162 }
1163 }
1164
1165 ForcePlayerSuicide(client);
1166 }
1167 ChangeClientTeam(client, CS_TEAM_T);
1168 }
1169
1170 if(adminclient && IsClientInGame(adminclient))
1171 {
1172 decl String:adminSteam[32];
1173 GetClientAuthString(adminclient, adminSteam, sizeof(adminSteam));
1174
1175 LogBanChange("%N (%s) has issued a CT ban on %N (%s) for %d minutes for %s.", adminclient, adminSteam, client, targetSteam, banTime, sReason);
1176 }
1177 else
1178 {
1179 LogBanChange("Console has issued a CT ban on %N (%s) for %d.", client, targetSteam, banTime);
1180 }
1181
1182 // check if there is a time
1183 if (banTime > 0)
1184 {
1185 ShowActivity2(adminclient, "[SM] ", "%t", "Temporary CT Ban", client, banTime, sReason);
1186 // save in local quick-access array
1187 PushArrayCell(gA_TimedBanLocalList, client);
1188 gA_LocalTimeRemaining[client] = banTime;
1189
1190 #if USESQL == 1
1191 // save in long-term database (already guaranteed to run only once per steam ID)
1192 decl String:query[255];
1193
1194 new String:auth[32];
1195 GetClientAuthString(adminclient, auth, sizeof(auth));
1196
1197 Format(query, sizeof(query), "INSERT INTO CTBans (name, steamid, ctbantime, reason, admin, date, active) VALUES ('%N', '%s', %d, '%s', '%s', %d, 1)", client, targetSteam, banTime, sReason, auth, GetTime());
1198 #if DEBUG == 1
1199 LogMessage("ctban query: %s", query);
1200 #endif
1201 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_CTBan, query, client);
1202
1203 #else
1204
1205 new iSteamArrayIndex = PushArrayString(gA_TimedBanSteamList, targetSteam);
1206 SetArrayCell(gA_TimedBanSteamList, iSteamArrayIndex, banTime, 22);
1207 #endif
1208 }
1209 else
1210 {
1211 decl String:query[255];
1212 new String:auth[32];
1213 GetClientAuthString(adminclient, auth, sizeof(auth));
1214
1215 Format(query, sizeof(query), "INSERT INTO CTBans (name, steamid, ctbantime, reason, admin, date, active) VALUES ('%N', '%s', %d, '%s', '%s', %d, 1)", client, targetSteam, banTime, sReason, auth, GetTime());
1216 SQL_TQuery(gH_TimedBanDataBase, DB_Callback_CTBan, query, client);
1217 ShowActivity2(adminclient, "[SM] ", "%t", "Permanent CT Ban", client, sReason);
1218 }
1219}
1220
1221public DB_Callback_CTBan(Handle:owner, Handle:hndl, const String:error[], any:client)
1222{
1223 if (hndl == INVALID_HANDLE)
1224 {
1225 LogError("Error writing CTBan to Timed Ban database: %s", error);
1226 }
1227 else
1228 {
1229 #if DEBUG == 1
1230 LogMessage("SQL CTBan: Updated database with CT Ban for %N", client);
1231 #endif
1232 }
1233}
1234
1235public Action:Command_IsCTBanned(client, args)
1236{
1237 if ((args < 1) || !args)
1238 {
1239 ReplyToCommand(client, "[SM] Usage: sm_isbanned <player>");
1240 }
1241 else
1242 {
1243 decl String:target[64];
1244 GetCmdArg(1, target, sizeof(target));
1245
1246 decl String:clientName[MAX_TARGET_LENGTH], target_list[MAXPLAYERS], target_count, bool:tn_is_ml;
1247 target_count = ProcessTargetString(target, client, target_list, MAXPLAYERS, 0, clientName, sizeof(clientName), tn_is_ml);
1248 // make sure we have exactly one target here
1249 if (target_count != 1)
1250 {
1251 ReplyToTargetError(client, target_count);
1252 }
1253 else
1254 {
1255 if(target_list[0] && IsClientInGame(target_list[0]))
1256 {
1257 if (AreClientCookiesCached(target_list[0]))
1258 {
1259 decl String:isBanned[3];
1260 GetClientCookie(target_list[0], g_CT_Cookie, isBanned, sizeof(isBanned));
1261 new banFlag = StringToInt(isBanned);
1262 if (banFlag)
1263 {
1264 // find the time if any
1265 if (gA_LocalTimeRemaining[target_list[0]] <= 0)
1266 {
1267 ReplyToCommand(client, CHAT_BANNER, "Permanent CT Ban", target_list[0]);
1268 }
1269 else
1270 {
1271 ReplyToCommand(client, CHAT_BANNER, "Temporary CT Ban", target_list[0], gA_LocalTimeRemaining[target_list[0]]);
1272 }
1273 }
1274 else
1275 {
1276 ReplyToCommand(client, CHAT_BANNER, "Not CT Banned", target_list[0]);
1277 }
1278 }
1279 else
1280 {
1281 ReplyToCommand(client, CHAT_BANNER, "Cookie Status Unavailable");
1282 }
1283 }
1284 else
1285 {
1286 ReplyToCommand(client, CHAT_BANNER, "Unable to target");
1287 }
1288 }
1289 }
1290
1291 return Plugin_Handled;
1292}
1293
1294// from FLOOR_MASTER's PermaMute plugin
1295void:LogBanChange(const String:format[], any:...)
1296{
1297 decl String:f_sBuffer[256], String:f_sPath[256];
1298 VFormat(f_sBuffer, sizeof(f_sBuffer), format, 2);
1299 BuildPath(Path_SM, f_sPath, sizeof(f_sPath), "logs/CTBans.log");
1300 LogMessage("%s", f_sBuffer);
1301 LogToFileEx(f_sPath, "%s", f_sBuffer);
1302}
1303
1304public Action:Command_CheckJoin(client, const String:command[], args)
1305{
1306 // 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)
1307 if(!client || !IsClientInGame(client) || IsFakeClient(client) || !AreClientCookiesCached(client) || !GetConVarBool(gH_Cvar_Enabled))
1308 {
1309 return Plugin_Continue;
1310 }
1311
1312 // Get the target team
1313 decl String:teamString[3];
1314 GetCmdArg(1, teamString, sizeof(teamString));
1315 new Target_Team = StringToInt(teamString);
1316
1317 decl String:sCookie[5];
1318 GetClientCookie(client, g_CT_Cookie, sCookie, sizeof(sCookie));
1319 decl String:sReason[256];
1320 GetClientCookie(client, g_CT_Cookie_Reason, sReason, sizeof(sReason));
1321 new iBanStatus = StringToInt(sCookie);
1322
1323 // check for an active ban to send a mesage
1324 if ((Target_Team == CS_TEAM_SPECTATOR || Target_Team == CS_TEAM_T) && iBanStatus)
1325 {
1326 // display them a message about the ban
1327 new iTimeBanned = GetClientCookieTime(client, g_CT_Cookie);
1328 decl String:sTimeBanned[150];
1329 FormatTime(sTimeBanned, sizeof(sTimeBanned), NULL_STRING, iTimeBanned);
1330 decl String:sJoinBanMsg[100];
1331 GetConVarString(gH_Cvar_JoinBanMessage, sJoinBanMsg, sizeof(sJoinBanMsg));
1332 PrintHintText(client, "%t", "Last CT Banned On", sTimeBanned, sReason, sJoinBanMsg);
1333 }
1334 // otherwise they joined CT or auto-select and are banned
1335 else if (iBanStatus)
1336 {
1337 if(strcmp(gS_SoundPath, ""))
1338 {
1339 decl String:buffer[PLATFORM_MAX_PATH + 5];
1340 Format(buffer, sizeof(buffer), "play %s", gS_SoundPath);
1341 ClientCommand(client, buffer);
1342 }
1343
1344 new String:sLength[16] = "Never";
1345 if(gA_LocalTimeRemaining[client]>0)
1346 {
1347 if(gA_LocalTimeRemaining[client] < 60)
1348 Format(sLength, sizeof(sLength), "%d minutes", gA_LocalTimeRemaining[client]);
1349 else if(gA_LocalTimeRemaining[client] < 3600)
1350 Format(sLength, sizeof(sLength), "%d hours", gA_LocalTimeRemaining[client]/(60));
1351 else
1352 Format(sLength, sizeof(sLength), "%d days", gA_LocalTimeRemaining[client]/(24*60));
1353 }
1354
1355 PrintCenterText(client, "%t", "Enforcing CT Ban", sReason, sLength);
1356 UTIL_TeamMenu(client);
1357 return Plugin_Stop;
1358 }
1359
1360 return Plugin_Continue;
1361}
1362
1363// This helper procedure will re-display the team join menu
1364// and is equivalent to what ClientCommand(client, "chooseteam") did in the past
1365UTIL_TeamMenu(client)
1366{
1367 new clients[1];
1368 new Handle:bf;
1369 clients[0] = client;
1370
1371 bf = StartMessage("VGUIMenu", clients, 1);
1372 BfWriteString(bf, "team"); // panel name
1373 BfWriteByte(bf, 1); // bShow
1374 BfWriteByte(bf, 0); // count
1375 EndMessage();
1376}