From ef2c81e2d63b09f7d42aa7c97d2487a66c34cd62 Mon Sep 17 00:00:00 2001 From: BotoX Date: Sat, 5 Mar 2016 00:34:03 +0100 Subject: [PATCH] added new plugins: KnifeAlert, immunityreserveslots (entWatch support), sourcebans+sourcecomms (fixed) added sm_forceinputplayer to ForceInputs added ZE_FFVII_Mako_Reactor_V6_B08 savelevel config fixed runtime error in voiceannounce_ex and ExtraCommands --- ExtraCommands/scripting/ExtraCommands.sp | 8 +- ForceInputs/scripting/ForceInputs.sp | 56 +- KnifeAlert/scripting/KnifeAlert.sp | 117 + KnifeAlert/scripting/include/morecolors.inc | 1 + ...6.cfg => ZE_FFVII_Mako_Reactor_V6_B08.cfg} | 22 +- .../savelevel/ze_lila_panic_escape_v3_1.cfg | 92 + .../sourcemod/plugin.immunityreserveslots.cfg | 113 + immunityreserveslots/configs/irs_kicklist.ini | 5 + .../scripting/immunityreserveslots_connect.sp | 1271 +++++++ .../scripting/include/connect.inc | 22 + .../scripting/include/donator.inc | 106 + .../scripting/include/entWatch.inc | 1 + includes/entWatch.inc | 150 + .../configs/sourcebans/sb_admin_groups.cfg | 0 sourcebans/configs/sourcebans/sb_admins.cfg | 0 sourcebans/configs/sourcebans/sourcebans.cfg | 84 + sourcebans/configs/sourcebans/sourcecomms.cfg | 66 + sourcebans/scripting/include/sourcebans.inc | 62 + sourcebans/scripting/include/sourcecomms.inc | 103 + sourcebans/scripting/sbchecker.sp | 383 ++ sourcebans/scripting/sourcebans.sp | 2570 +++++++++++++ sourcebans/scripting/sourcecomms.sp | 3205 +++++++++++++++++ .../translations/sourcebans.phrases.txt | 127 + .../translations/sourcecomms.phrases.txt | 946 +++++ .../scripting/voiceannounce_ex.sp | 4 +- 25 files changed, 9484 insertions(+), 30 deletions(-) create mode 100644 KnifeAlert/scripting/KnifeAlert.sp create mode 120000 KnifeAlert/scripting/include/morecolors.inc rename SaveLevel/configs/savelevel/{ze_FFVII_Mako_Reactor_v6.cfg => ZE_FFVII_Mako_Reactor_V6_B08.cfg} (67%) create mode 100644 SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg create mode 100644 immunityreserveslots/cfg/sourcemod/plugin.immunityreserveslots.cfg create mode 100644 immunityreserveslots/configs/irs_kicklist.ini create mode 100644 immunityreserveslots/scripting/immunityreserveslots_connect.sp create mode 100644 immunityreserveslots/scripting/include/connect.inc create mode 100644 immunityreserveslots/scripting/include/donator.inc create mode 120000 immunityreserveslots/scripting/include/entWatch.inc create mode 100644 includes/entWatch.inc create mode 100644 sourcebans/configs/sourcebans/sb_admin_groups.cfg create mode 100644 sourcebans/configs/sourcebans/sb_admins.cfg create mode 100644 sourcebans/configs/sourcebans/sourcebans.cfg create mode 100644 sourcebans/configs/sourcebans/sourcecomms.cfg create mode 100644 sourcebans/scripting/include/sourcebans.inc create mode 100644 sourcebans/scripting/include/sourcecomms.inc create mode 100644 sourcebans/scripting/sbchecker.sp create mode 100644 sourcebans/scripting/sourcebans.sp create mode 100644 sourcebans/scripting/sourcecomms.sp create mode 100644 sourcebans/translations/sourcebans.phrases.txt create mode 100644 sourcebans/translations/sourcecomms.phrases.txt diff --git a/ExtraCommands/scripting/ExtraCommands.sp b/ExtraCommands/scripting/ExtraCommands.sp index db0a6c7c..9ab3478e 100644 --- a/ExtraCommands/scripting/ExtraCommands.sp +++ b/ExtraCommands/scripting/ExtraCommands.sp @@ -151,13 +151,13 @@ public Event_WeaponFire(Handle:hEvent, String:name[], bool:dontBroadcast) { new toAdd = 1; new String:weaponClassname[128]; - GetEntityClassname(weapon, weaponClassname, 128); + GetEntityClassname(weapon, weaponClassname, sizeof(weaponClassname)); if(StrEqual(weaponClassname, "weapon_glock", true) || StrEqual(weaponClassname, "weapon_famas", true)) { - if(GetEntProp(weapon, Prop_Data, "m_bBurstMode", 4, 0)) + if(GetEntProp(weapon, Prop_Send, "m_bBurstMode")) { - switch (GetEntProp(weapon, Prop_Send, "m_iClip1", 4, 0)) + switch (GetEntProp(weapon, Prop_Send, "m_iClip1")) { case 1: { @@ -243,7 +243,7 @@ public Action:Command_Kevlar(client, args) new amount = 0; decl String:arg2[20]; GetCmdArg(2, arg2, sizeof(arg2)); - if(StringToIntEx(arg2, amount) == 0 || amount <= 0) + if(StringToIntEx(arg2, amount) == 0 || amount < 0) { ReplyToCommand(client, "[SM] Invalid Value"); return Plugin_Handled; diff --git a/ForceInputs/scripting/ForceInputs.sp b/ForceInputs/scripting/ForceInputs.sp index 416d12de..7f501fd1 100644 --- a/ForceInputs/scripting/ForceInputs.sp +++ b/ForceInputs/scripting/ForceInputs.sp @@ -14,9 +14,9 @@ public Plugin:myinfo = { name = "ForceInput", - author = "zaCade", + author = "zaCade + BotoX", description = "Allows admins to force inputs on entities. (ent_fire)", - version = "1.2", + version = "1.3", url = "" }; @@ -26,6 +26,52 @@ public Plugin:myinfo = public OnPluginStart() { RegAdminCmd("sm_forceinput", Command_ForceInput, ADMFLAG_ROOT); + RegAdminCmd("sm_forceinputplayer", Command_ForceInputPlayer, ADMFLAG_ROOT); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: +//---------------------------------------------------------------------------------------------------- +public Action:Command_ForceInputPlayer(client, args) +{ + if (GetCmdArgs() < 2) + { + ReplyToCommand(client, "[SM] Usage: sm_forceinputplayer [parameter]"); + return Plugin_Handled; + } + + new String:sArguments[3][256]; + GetCmdArg(1, sArguments[0], sizeof(sArguments[])); + GetCmdArg(2, sArguments[1], sizeof(sArguments[])); + GetCmdArg(3, sArguments[2], sizeof(sArguments[])); + + decl String:target_name[MAX_TARGET_LENGTH]; + decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml; + + if((target_count = ProcessTargetString( + sArguments[0], + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_ALIVE, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + ReplyToTargetError(client, target_count); + return Plugin_Handled; + } + + for(new i = 0; i < target_count; i++) + { + if (sArguments[2][0]) + SetVariantString(sArguments[2]); + + AcceptEntityInput(target_list[i], sArguments[1], target_list[i], target_list[i]); + ReplyToCommand(client, "[SM] Input succesfull."); + } + + return Plugin_Handled; } //---------------------------------------------------------------------------------------------------- @@ -46,7 +92,7 @@ public Action:Command_ForceInput(client, args) if (StrEqual(sArguments[0], "!self")) { - if (strlen(sArguments[2])) + if (sArguments[2][0]) SetVariantString(sArguments[2]); AcceptEntityInput(client, sArguments[1], client, client); @@ -66,7 +112,7 @@ public Action:Command_ForceInput(client, args) { if (IsValidEntity(entity) || IsValidEdict(entity)) { - if (strlen(sArguments[2])) + if (sArguments[2][0]) SetVariantString(sArguments[2]); AcceptEntityInput(entity, sArguments[1], client, client); @@ -88,7 +134,7 @@ public Action:Command_ForceInput(client, args) if (StrEqual(sClassname, sArguments[0], false) || StrEqual(sTargetname, sArguments[0], false)) { - if (strlen(sArguments[2])) + if (sArguments[2][0]) SetVariantString(sArguments[2]); AcceptEntityInput(entity, sArguments[1], client, client); diff --git a/KnifeAlert/scripting/KnifeAlert.sp b/KnifeAlert/scripting/KnifeAlert.sp new file mode 100644 index 00000000..504998fa --- /dev/null +++ b/KnifeAlert/scripting/KnifeAlert.sp @@ -0,0 +1,117 @@ +#pragma semicolon 1 + +#include +#include "morecolors.inc" + +#pragma newdecls required + +Handle g_hCVar_NotificationTime = INVALID_HANDLE; +char g_sAttackerSID[MAXPLAYERS + 1][32]; +int g_iNotificationTime[MAXPLAYERS + 1]; + +public Plugin myinfo = +{ + name = "Knife Notifications", + author = "Obus + BotoX", + description = "Notify administrators when zombies have been knifed by humans.", + version = "2.1", + url = "" +}; + +public void OnPluginStart() +{ + g_hCVar_NotificationTime = CreateConVar("sm_knifenotifytime", "5", "Amount of time to pass before a knifed zombie is considered \"not knifed\" anymore.", 0, true, 0.0, true, 60.0); + + if(!HookEventEx("player_hurt", Event_PlayerHurt, EventHookMode_Pre)) + SetFailState("[Knife-Notifications] Failed to hook \"player_hurt\" event."); +} + +public int GetClientFromSteamID(const char[] auth) +{ + char clientAuth[32]; + + for(int client = 1; client <= MaxClients; client++) + { + if(!IsClientAuthorized(client)) + continue; + + GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + + if(StrEqual(auth, clientAuth)) + return client; + } + + return -1; +} + +public Action Event_PlayerHurt(Handle hEvent, const char[] name, bool dontBroadcast) +{ + int victim; + int attacker; + char sWepName[64]; + char sAtkSID[32]; + char sVictSID[32]; + GetEventString(hEvent, "weapon", sWepName, sizeof(sWepName)); + + if((victim = GetClientOfUserId(GetEventInt(hEvent, "userid"))) == 0) + return; + + if((attacker = GetClientOfUserId(GetEventInt(hEvent, "attacker"))) == 0) + return; + + if(!IsClientInGame(victim) || !IsPlayerAlive(victim)) + return; + + if(!IsClientInGame(attacker) || !IsPlayerAlive(attacker)) + return; + + if(victim != attacker && GetClientTeam(victim) == 2 && GetClientTeam(attacker) == 3) + { + if(StrEqual(sWepName, "knife")) + { + int damage = GetEventInt(hEvent, "dmg_health"); + + if(damage < 35) + return; + + GetClientAuthId(attacker, AuthId_Steam2, sAtkSID, sizeof(sAtkSID)); + GetClientAuthId(attacker, AuthId_Steam2, g_sAttackerSID[victim], sizeof(g_sAttackerSID[])); + GetClientAuthId(victim, AuthId_Steam2, sVictSID, sizeof(sVictSID)); + LogMessage("%L knifed %L", attacker, victim); + + g_iNotificationTime[victim] = (GetTime() + GetConVarInt(g_hCVar_NotificationTime)); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientConnected(i) && IsClientInGame(i) && (IsClientSourceTV(i) || GetAdminFlag(GetUserAdmin(i), Admin_Generic))) + CPrintToChat(i, "{green}[SM] {blue}%N {default}knifed {red}%N", attacker, victim); + } + } + } + else if(victim != attacker && GetClientTeam(attacker) == 2 && GetClientTeam(victim) == 3) + { + int pOldKnifer; + pOldKnifer = GetClientFromSteamID(g_sAttackerSID[attacker]); + + if(g_iNotificationTime[attacker] > GetTime() && (victim != pOldKnifer)) + { + char sAtkAttackerName[MAX_NAME_LENGTH]; + GetClientAuthId(attacker, AuthId_Steam2, sAtkSID, sizeof(sAtkSID)); + + if(pOldKnifer != -1) + { + GetClientName(pOldKnifer, sAtkAttackerName, sizeof(sAtkAttackerName)); + LogMessage("%L killed %L (Recently knifed by %L)", attacker, victim, pOldKnifer); + } + else + LogMessage("%L killed %L (Recently knifed by a disconnected player [%s])", attacker, victim, g_sAttackerSID[attacker]); + + for(int i = 1; i <= MaxClients; i++) + { + if(IsClientConnected(i) && IsClientInGame(i) && (IsClientSourceTV(i) || GetAdminFlag(GetUserAdmin(i), Admin_Generic))) + CPrintToChat(i, "{green}[SM] {red}%N {green}(%s){default} killed {blue}%N{default} - knifed by {blue}%s {green}(%s)", + attacker, sAtkSID, victim, (pOldKnifer != -1) ? sAtkAttackerName : "a disconnected player", g_sAttackerSID[attacker]); + } + } + } +} diff --git a/KnifeAlert/scripting/include/morecolors.inc b/KnifeAlert/scripting/include/morecolors.inc new file mode 120000 index 00000000..cd0d80e6 --- /dev/null +++ b/KnifeAlert/scripting/include/morecolors.inc @@ -0,0 +1 @@ +../../../includes/morecolors.inc \ No newline at end of file diff --git a/SaveLevel/configs/savelevel/ze_FFVII_Mako_Reactor_v6.cfg b/SaveLevel/configs/savelevel/ZE_FFVII_Mako_Reactor_V6_B08.cfg similarity index 67% rename from SaveLevel/configs/savelevel/ze_FFVII_Mako_Reactor_v6.cfg rename to SaveLevel/configs/savelevel/ZE_FFVII_Mako_Reactor_V6_B08.cfg index ef40a5e5..7cf596f7 100644 --- a/SaveLevel/configs/savelevel/ze_FFVII_Mako_Reactor_v6.cfg +++ b/SaveLevel/configs/savelevel/ZE_FFVII_Mako_Reactor_V6_B08.cfg @@ -13,7 +13,7 @@ "restore" { "AddOutput" "OnUser1 leveling_counter,Add,1,0,-1" - "m_iFrags" "100" + "m_iFrags" "10" } } "2" @@ -29,7 +29,7 @@ "restore" { "AddOutput" "OnUser1 leveling_counter,Add,2,0,-1" - "m_iFrags" "200" + "m_iFrags" "20" } } "3" @@ -45,23 +45,7 @@ "restore" { "AddOutput" "OnUser1 leveling_counter,Add,3,0,-1" - "m_iFrags" "300" - } - } - "4" - { - "name" "Level 4" - "match" - { - "math" - { - "m_OnUser1" "leveling_counter,Add,4" - } - } - "restore" - { - "AddOutput" "OnUser1 leveling_counter,Add,4,0,-1" - "m_iFrags" "400" + "m_iFrags" "30" } } } diff --git a/SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg b/SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg new file mode 100644 index 00000000..afc586a6 --- /dev/null +++ b/SaveLevel/configs/savelevel/ze_lila_panic_escape_v3_1.cfg @@ -0,0 +1,92 @@ +"levels" +{ + "0" + { + "name" "Level 0" + "restore" + { + "m_iName" "" + "m_iFrags" "0" + } + } + "1" + { + "name" "Level 1" + "match" + { + "props" + { + "m_iName" "1" + } + } + "restore" + { + "m_iName" "1" + "m_iFrags" "100" + } + } + "2" + { + "name" "Level 2" + "match" + { + "props" + { + "m_iName" "2" + } + } + "restore" + { + "m_iName" "2" + "m_iFrags" "200" + } + } + "3" + { + "name" "Level 3" + "match" + { + "props" + { + "m_iName" "3" + } + } + "restore" + { + "m_iName" "3" + "m_iFrags" "300" + } + } + "4" + { + "name" "Level 4" + "match" + { + "props" + { + "m_iName" "4" + } + } + "restore" + { + "m_iName" "4" + "m_iFrags" "400" + } + } + "5" + { + "name" "Level 5" + "match" + { + "props" + { + "m_iName" "5" + } + } + "restore" + { + "m_iName" "5" + "m_iFrags" "500" + } + } +} diff --git a/immunityreserveslots/cfg/sourcemod/plugin.immunityreserveslots.cfg b/immunityreserveslots/cfg/sourcemod/plugin.immunityreserveslots.cfg new file mode 100644 index 00000000..b2cf498a --- /dev/null +++ b/immunityreserveslots/cfg/sourcemod/plugin.immunityreserveslots.cfg @@ -0,0 +1,113 @@ +// This file was auto-generated by SourceMod (v1.4.7) +// ConVars for plugin "immunityreserveslots_connect.smx" + + +// Allow direct connecting reserve slot users to connect without having to type in the password on a password protected server with the Connect extension (0 - disable, 1 - enable, 2 - as with 1 but allow all connecting clients to connect). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "2.000000" +sm_irs_autopassword "0" + +// The immunity value to give to donators, if required for immunity checks within IRS. +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "99.000000" +sm_irs_donator_immunity "0" + +// When enabled along with the donators plugin, donaotrs will be allowed to connect (0 - disable, 1 - allow donators to connect). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "1.000000" +sm_irs_donator_support "0" + +// The maximum amount of players that can connect to the server and kick a low immunity reserve slot player (0 - no limit, more than 0 to set the max limit). +// - +// Default: "0" +// Minimum: "0.000000" +sm_irs_highimmunitylimit "0" + +// This value and over are players who are considered to have high immunity (0 - disable, more than 0 to set the high immunity value). +// - +// Default: "0" +// Minimum: "0.000000" +sm_irs_highimmunityvalue "0" + +// Enable immunity check (0 - disable, 1 - immunity check if server is full of reserves, 2 - as with 1 but also allow players with high enough immunity and no reserve flag to stay connected). +// - +// Default: "1" +// Minimum: "0.000000" +// Maximum: "2.000000" +sm_irs_immunity "1" + +// This tries to kick people in such a way to prevent autobalance (0 - disable, 1 - enable). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "1.000000" +sm_irs_keepbalance "0" + +// Path to kick list file ("default" - uses a file in the sourcemod config folder called irs_kicklist.ini, anything else uses what you enter e.g. "cfg/kicklist.cfg"). +// - +// Default: "default" +sm_irs_kicklist_file "default" + +// Enable kick list mode (0 - disable, normal reserve slot operation, 1 - only use a kick list to kick specific clients, 2 - as with 1 but allow any connecting client to kick people from the server). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "2.000000" +sm_irs_kicklist_mode "0" + +// Message to display when a client is kicked for a normal reserve slot ("default" - uses translation phrase, anything else uses what you enter). +// - +// Default: "default" +sm_irs_kickreason "default" + +// Message to display when a client is kicked for a reserve slot based on immunity ("default" - uses translation phrase, anything else uses what you enter). +// - +// Default: "default" +sm_irs_kickreason_immunity "default" + +// The delay, in seconds, to kick spectators (0 - instant, any other value gives spectators a grace of xx seconds until they can be kicked). +// - +// Default: "0" +// Minimum: "0.000000" +sm_irs_kickspecdelay "0" + +// When enabled spectators are always kicked first before anyone else (0 - disable, all players are taken into account for kicking, 1 - enable). +// - +// Default: "1" +// Minimum: "0.000000" +// Maximum: "1.000000" +sm_irs_kickspecfirst "1" + +// Who to kick when a valid player is found (0 - random, 1 - highest ping, 2 - highest time, 3 - lowest time). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "3.000000" +sm_irs_kicktype "0" + +// Enable logging (0 - disable, 1 - enable highly verbose logs, 2 - only log the disconnected and connecting users in regular SM logs). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "2.000000" +sm_irs_log "0" + +// Message to display when a client gets rejected for a reserve slot when they have reserve rights with the Connect extension ("default" - uses translation phrase, anything else uses what you enter). +// - +// Default: "default" +sm_irs_rejectreason "default" + +// Enable reject reason with the Connect extension (0 - disable, 1 - enable). +// - +// Default: "0" +// Minimum: "0.000000" +// Maximum: "1.000000" +sm_irs_rejectreason_enable "0" + + diff --git a/immunityreserveslots/configs/irs_kicklist.ini b/immunityreserveslots/configs/irs_kicklist.ini new file mode 100644 index 00000000..b6f6d969 --- /dev/null +++ b/immunityreserveslots/configs/irs_kicklist.ini @@ -0,0 +1,5 @@ +// Just enter someone's steamid and they're added to the system +// e.g. (without being commented out, obviously) +// STEAM_0:1:12345678987654321 +// STEAM_0:1:98765432123456789 +// etc. \ No newline at end of file diff --git a/immunityreserveslots/scripting/immunityreserveslots_connect.sp b/immunityreserveslots/scripting/immunityreserveslots_connect.sp new file mode 100644 index 00000000..c0e78782 --- /dev/null +++ b/immunityreserveslots/scripting/immunityreserveslots_connect.sp @@ -0,0 +1,1271 @@ +/** + * ================================================================================== + * Immunity Reserve Slots Change Log + * ================================================================================== + * + * 0.1 + * - Initial release. + * + * 0.2 + * - Added lowest time option to kick types. + * - Added cvars to control kick message for normal kick and immunity kick. + * - Small optimizations. + * + * 0.3 + * - Fixed some logging bugs. + * - Fixed a major bug in the immunity check and optimised regular check. + * + * 0.3.1 + * - Fixed a very small bug in the logging code to do with detecting spectators. + * + * 0.3.2 + * - Added option to only log who gets kicked. + * + * 0.3.3 + * - Added cvar to control whether or not spectators get kicked first before other + * players or not (defaults to enabled). + * + * 0.3.4 + * - Added a cvar option to limit the maximum amount of players with high immunity + * to kick low immunity players when the server is full (if more than the value + * are connected at the time no further connections will be allowed). + * - A bit of code clean up. + * + * 1.0 + * - Made final. + * - Added full translation support. + * - Changed the way the reason cvars work, no config changes needed for old users. + * + * 1.0.1 + * - Added cvar "sm_irs_keepbalance", this will try and keep team balance best as + * possible when kicking players as not to trigger an autobalance. + * - Drastic recoding for better performance. + * - Added use of AskPluginLoad2() for improved late load detection (this makes the + * plugin require SM 1.3 now). + * - Added detection of accidental installation of the default test plugin for + * CBaseServer tools to avoid any possible problems. + * - New verbose logs, now create their own log files instead of spamming regular + * logs. Also improved verbosity. + * - Fixed a small leak which happened if any logging was disabled. + * - Spec check now uses a team check instead as in some games it could "incorrectly" + * flag users as spectating when they were dead. Also flags unassigned players now. + * + * 1.0.2 + * - Fixed incorrect statement in balance check code. + * - Small optimisations. + * - Added command check back in for "sm_reskick_immunity" as some servers might + * expect this as a valid way to grant default immunity if reading the official + * SM wiki. + * - Added the regular SM reserve slot plugin to the plugin checks on startup. + * + * 1.0.3 + * - Made the default action for detected reserve plugins to move to the disabled + * folder after unload rather than stopping the plugin. + * - Changed translation phrase on plugin error to make more sense. + * + * 1.0.4 + * - Made use of GetURandomFloat for random mode. + * - Improved logging code. + * + * 2.0 + * - Added Connect extension support. + * - Added cvars to configure the reject message on a full server + * (sm_irs_rejectreason_enable and sm_irs_rejectreason). + * + * 2.0.1 + * - Added auto password cvar so reserve clients can automatically connect to + * password protected servers, can also be set so any connecting client can + * connect e.g. for temporary purposes (sm_irs_autopassword). + * - Fixed bug in keep balance code where it wouldn't kick spectators if there were + * any when the cvar for kick spectators first was disabled. + * - Added kick list mode, this allows anyone to connect to the server (configurable) + * but goes through a list of steam id's for who should get kicked vs who can + * connect (sm_irs_kicklist_mode and sm_irs_kicklist_file). + * - Removed sm_rescheck_mmunity command check thing I still had in there where it + * wouldn't have mattered anyway (i.e. I missed it). + * + * 2.0.2 + * - Recoded kick list mode for less disk I/O. + * - Fixed a bug if kick list mode was set to 2 which could create a possible rare + * looping slot scenario. + * - Added command to reload the kick list if users want to update the list as soon + * as possible (sm_irs_kicklist_reload), list now also updates automatically on + * map change properly. + * - Cleaned up some code. + * + * 2.0.3 + * - Fixed CloseHandle() bug. + * + * 2.0.4 + * - Fix for too many clients connecting in MVM games. Connect only. + * + * 2.0.5 + * - Fix for non-TF2 games throwing errors on map start. Connect only. + * + * 2.0.6 + * - Code cleanup. + * - Added support for Connect 1.2.0+. + * + * 2.0.7 + * - Added donator plugin support + * (see: http://forums.alliedmods.net/showthread.php?t=145542). + * + * 2.0.8 + * - Added cvar to control when to kick spectators (sm_irs_kickspecdelay, set to 0 + * instantly kicks spectators, anything else gives them a grace of x secs before + * being kicked). + * ================================================================================== + */ + +#include +#include + +#undef REQUIRE_PLUGIN +#include +#include +#define REQUIRE_PLUGIN + +#define PLUGIN_VERSION "2.0.9" + +// Toggle build here. +#define EXT_CBASE 0 +#define EXT_CONNECT 1 + +// Anti-Jamster protection scheme. +#if EXT_CONNECT && EXT_CBASE +#define EXT_CBASE 0 +#endif + +#if EXT_CBASE +#include +#endif + +#if EXT_CONNECT +#define MAX_CLIENTS_MVM 6 +#include +#endif + +new TEAM1; +new TEAM2; +new SPEC; + +new bool:g_HighImmunityPlayers[MAXPLAYERS+1]; +new bool:b_lateLoad; +new bool:b_loaded; +new bool:b_useDonator; +new bool:b_useEntWatch; +new bool:b_canKickSpec[MAXPLAYERS+1]; + +new g_HIPCount; + +new Handle:cvar_KickType = INVALID_HANDLE; +new Handle:cvar_Spec = INVALID_HANDLE; +new Handle:cvar_SpecKickDelay = INVALID_HANDLE; +new Handle:cvar_Logging = INVALID_HANDLE; +new Handle:cvar_Immunity = INVALID_HANDLE; +new Handle:cvar_KickReasonImmunity = INVALID_HANDLE; +new Handle:cvar_KickReason = INVALID_HANDLE; +new Handle:cvar_HighImmunityLimit = INVALID_HANDLE; +new Handle:cvar_HighImmunityValue = INVALID_HANDLE; +new Handle:cvar_KeepBalance = INVALID_HANDLE; +new Handle:cvar_KickListMode = INVALID_HANDLE; +new Handle:cvar_KickListFile = INVALID_HANDLE; +new Handle:cvar_Donator = INVALID_HANDLE; +new Handle:cvar_DonatorImmunity = INVALID_HANDLE; + +new Handle:arr_KickListIDs = INVALID_HANDLE; + +new Handle:t_KickSpecClient[MAXPLAYERS+1] = INVALID_HANDLE; + +#if EXT_CONNECT +new bool:isMVM = false; +new Handle:cvar_AutoPassword = INVALID_HANDLE; +new Handle:cvar_RejectReason = INVALID_HANDLE; +new Handle:cvar_RejectReasonEnable = INVALID_HANDLE; +new Handle:cvar_GameTypeMVM = INVALID_HANDLE; +#endif + +new String:g_LogFilePath[PLATFORM_MAX_PATH]; + +public Plugin:myinfo = +{ + #if EXT_CBASE + name = "Immunity Reserve Slots [CBASESERVER]", + #endif + #if EXT_CONNECT + name = "Immunity Reserve Slots [CONNECT]", + #endif + author = "Jamster", + description = "Immunity based reserve slots for CBaseServer Tools and Connect extensions", + version = PLUGIN_VERSION, + url = "http://www.sourcemod.net/" +}; + +public OnPluginStart() +{ + LoadTranslations("immunityreserveslots.phrases"); + decl String:desc[255]; + + arr_KickListIDs = CreateArray(32); + b_loaded = false; + + #if EXT_CONNECT + + Format(desc, sizeof(desc), "%t", "irs_autopassword"); + cvar_AutoPassword = CreateConVar("sm_irs_autopassword", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 2.0); + + Format(desc, sizeof(desc), "%t", "irs_rejectreason_enable"); + cvar_RejectReasonEnable = CreateConVar("sm_irs_rejectreason_enable", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 1.0); + + Format(desc, sizeof(desc), "%t", "irs_rejectreason"); + cvar_RejectReason = CreateConVar("sm_irs_rejectreason", "default", desc, FCVAR_PLUGIN); + + cvar_GameTypeMVM = FindConVar("tf_gamemode_mvm"); + + #endif + + Format(desc, sizeof(desc), "%t", "irs_version"); + CreateConVar("sm_irs_version", PLUGIN_VERSION, desc, FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY|FCVAR_DONTRECORD); + + Format(desc, sizeof(desc), "%t", "irs_kicktype"); + cvar_KickType = CreateConVar("sm_irs_kicktype", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 3.0); + + Format(desc, sizeof(desc), "%t", "irs_kickreason"); + cvar_KickReason = CreateConVar("sm_irs_kickreason", "default", desc, FCVAR_PLUGIN); + + Format(desc, sizeof(desc), "%t", "irs_kickreason_immunity"); + cvar_KickReasonImmunity = CreateConVar("sm_irs_kickreason_immunity", "default", desc, FCVAR_PLUGIN); + + Format(desc, sizeof(desc), "%t", "irs_kicklist_file"); + cvar_KickListFile = CreateConVar("sm_irs_kicklist_file", "default", desc, FCVAR_PLUGIN); + + Format(desc, sizeof(desc), "%t", "irs_kicklist_mode"); + cvar_KickListMode = CreateConVar("sm_irs_kicklist_mode", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 2.0); + + Format(desc, sizeof(desc), "%t", "irs_log"); + cvar_Logging = CreateConVar("sm_irs_log", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 2.0); + + Format(desc, sizeof(desc), "%t", "irs_immunity"); + cvar_Immunity = CreateConVar("sm_irs_immunity", "1", desc, FCVAR_PLUGIN, true, 0.0, true, 2.0); + + Format(desc, sizeof(desc), "%t", "irs_kickspecfirst"); + cvar_Spec = CreateConVar("sm_irs_kickspecfirst", "1", desc, FCVAR_PLUGIN, true, 0.0, true, 1.0); + + Format(desc, sizeof(desc), "%t", "irs_kickspecdelay"); + cvar_SpecKickDelay = CreateConVar("sm_irs_kickspecdelay", "0", desc, FCVAR_PLUGIN, true, 0.0); + + Format(desc, sizeof(desc), "%t", "irs_donator_support"); + cvar_Donator = CreateConVar("sm_irs_donator_support", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 1.0); + + Format(desc, sizeof(desc), "%t", "irs_donator_immunity"); + cvar_DonatorImmunity = CreateConVar("sm_irs_donator_immunity", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 99.0); + + Format(desc, sizeof(desc), "%t", "irs_highimmunitylimit"); + cvar_HighImmunityLimit = CreateConVar("sm_irs_highimmunitylimit", "0", desc, FCVAR_PLUGIN, true, 0.0); + + Format(desc, sizeof(desc), "%t", "irs_highimmunityvalue"); + cvar_HighImmunityValue = CreateConVar("sm_irs_highimmunityvalue", "0", desc, FCVAR_PLUGIN, true, 0.0); + + Format(desc, sizeof(desc), "%t", "irs_keepbalance"); + cvar_KeepBalance = CreateConVar("sm_irs_keepbalance", "0", desc, FCVAR_PLUGIN, true, 0.0, true, 1.0); + + Format(desc, sizeof(desc), "%t", "irs_kicklist_reload"); + RegServerCmd("sm_irs_kicklist_reload", Command_KickListReload, desc); + + HookConVarChange(cvar_KickListFile, KickListFileChanged); + HookConVarChange(cvar_KickListMode, KickListConVarChanged); + + AddCommandListener(listen_join_team, "jointeam"); + AddCommandListener(listen_join_team, "spectate"); + + AutoExecConfig(true, "plugin.immunityreserveslots"); +} + +public OnConfigsExecuted() +{ + LoadKickList(); + + #if EXT_CONNECT + + if (cvar_GameTypeMVM != INVALID_HANDLE && GetConVarInt(cvar_GameTypeMVM)) + { + isMVM = true; + } + else + { + isMVM = false; + } + + #endif + + b_loaded = true; + + if (GetConVarInt(cvar_Donator) && !b_useDonator) + { + LogError("%t", "IRS Donator Plugin Error"); + } +} + +public OnMapEnd() +{ + b_loaded = false; +} + +public KickListFileChanged(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + if (!StrEqual(oldValue, newValue, false) && b_loaded) + { + LoadKickList(); + } +} + +public KickListConVarChanged(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + if (newValue[0] != 0 && b_loaded) + { + LoadKickList(); + } +} + +public Action:Command_KickListReload(client) +{ + if (LoadKickList()) + { + ReplyToCommand(client, "%t", "IRS Kick List Reloaded"); + } + return Plugin_Handled; +} + +LoadKickList() +{ + ClearArray(arr_KickListIDs); + if (GetConVarInt(cvar_KickListMode)) + { + decl String:path[PLATFORM_MAX_PATH]; + GetConVarString(cvar_KickListFile, path, sizeof(path)); + if (StrEqual(path, "default", false)) + { + BuildPath(Path_SM, path, sizeof(path), "configs/irs_kicklist.ini"); + } + + new Handle:h_path = OpenFile(path, "r"); + if (h_path == INVALID_HANDLE) + { + LogError("%t", "IRS Kick List Path Error", path); + } + else + { + decl String:line[32]; + while (!IsEndOfFile(h_path)) + { + ReadFileLine(h_path, line, sizeof(line)); + TrimString(line); + // Yep, I ain't checking if STEAMID's are valid, I'm raw like that. + if (line[0] != '/' && line[1] != '/' && line[0] != '\0' && line[0] != '*') + { + PushArrayString(arr_KickListIDs, line); + } + } + CloseHandle(h_path); + return true; + } + } + + return false; +} + +public OnAllPluginsLoaded() +{ + b_useDonator = LibraryExists("donator.core"); + b_useEntWatch = LibraryExists("entWatch"); + + new Handle:h_Plugin; + new Handle:arr_Plugins = CreateArray(64); + decl String:plugin[64]; + + PushArrayString(arr_Plugins, "cbaseservertest.smx"); + PushArrayString(arr_Plugins, "cbsext_reserves.smx"); + PushArrayString(arr_Plugins, "reservedslots.smx"); + PushArrayString(arr_Plugins, "immunityreserveslots.smx"); + + #if EXT_CBASE + + PushArrayString(arr_Plugins, "immunityreserveslots_connect.smx"); + + #endif + + #if EXT_CONNECT + + PushArrayString(arr_Plugins, "immunityreserveslots_cbase.smx"); + + #endif + + new index = GetArraySize(arr_Plugins); + + for (new i=0; i countTEAM2) + { + useTeam = TEAM1; + } + else + { + useTeam = TEAM2; + } + + if (Logging == 1) + { + if (useKeepBalance) + { + decl String:TeamName[32]; + GetTeamName(useTeam, TeamName, sizeof(TeamName)); + LogToFileEx(g_LogFilePath, "Balance check: Team \"%s\" has the most players (%02d | %02d)", TeamName, countTEAM1, countTEAM2); + } + else + { + LogToFileEx(g_LogFilePath, "Balance check: Teams are the same size"); + } + } + } + + // Look at how lazy I am. + if (GetArraySize(arr_KickListIDs)) + { + useKickList = true; + } + else + { + useKickList = false; + } + + for (new i=1; i<=MaxClients; i++) + { + if (!IsClientConnected(i)) + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "%02d: NOT CONNECTED", i); + } + continue; + } + + if (IsFakeClient(i)) + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "%02d: BOT", i); + } + continue; + } + + if (IsClientInGame(i)) + { + clientTeam[i] = GetClientTeam(i); + } + + decl String:PlayerAuth[32]; + GetClientAuthString(i, PlayerAuth, sizeof(PlayerAuth)); + new AdminId:PlayerAdmin = FindAdminByIdentity(AUTHMETHOD_STEAM, PlayerAuth) + ClientImmunity[i] = GetAdminImmunityLevel(PlayerAdmin); + + if (b_useDonator && Donator) + { + if (IsPlayerDonator(i)) + { + ClientDonator[i] = true; + } + if (ClientImmunity[i] == 0) + { + ClientImmunity[i] = DonatorImmunityValue; + } + } + + // Kick list check, if the player isn't found then they're excluded. + if (useKickList) + { + if (FindStringInArray(arr_KickListIDs, PlayerAuth) == -1) + { + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + } + + // Removed the check for root as it seems this doesn't matter any more in modern SM versions (1.3+), or it never did and I'm terrible. + if (GetAdminFlag(PlayerAdmin, Admin_Reservation) || ClientDonator[i]) + { + if (Immunity && ClientImmunity[i] < LowestImmunityLevel) + { + LowestImmunityLevel = ClientImmunity[i]; + } + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + + if (Immunity == 2 && ClientImmunity[i] > 0) + { + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + + if (b_useEntWatch && entWatch_HasSpecialItem(i)) + { + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + + if (IsClientInGame(i)) + { + switch (KickType) + { + case 0: + value = GetURandomFloat(); + case 1: + value = GetClientAvgLatency(i, NetFlow_Outgoing); + case 2: + value = GetClientTime(i); + case 3: + value = GetClientTime(i); + } + + if (KickType == 3 && !HighestValue) + { + HighestValue = value; + } + + if ((clientTeam[i] == SPEC || clientTeam[i] == 0) && (SpecKick || useKeepBalance)) + { + if (SpecKickDelay && b_canKickSpec[i] || !SpecKickDelay) + { + if (KickType == 3 && !HighestSpecValue) + { + HighestSpecValue = value; + } + + if (KickType == 3 && value <= HighestSpecValue) + { + HighestSpecValue = value; + HighestSpecValueId = i; + } + else if (KickType != 3 && value >= HighestSpecValue) + { + HighestSpecValue = value; + HighestSpecValueId = i; + } + } + } + else if (KickType == 3 && value <= HighestValue) + { + HighestValue = value; + HighestValueId = i; + } + else if (KickType != 3 && value >= HighestValue) + { + HighestValue = value; + HighestValueId = i; + } + + if (useKeepBalance && clientTeam[i] == useTeam) + { + if (KickType == 3 && !HighestBalanceValue) + { + HighestBalanceValue = value; + } + + if (KickType == 3 && value <= HighestBalanceValue) + { + HighestBalanceValue = value; + HighestBalanceValueId = i; + } + else if (KickType != 3 && value >= HighestBalanceValue) + { + HighestBalanceValue = value; + HighestBalanceValueId = i; + } + } + } + + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i], value); + } + } + + if (Logging == 1) + { + decl String:ConnectingClientAdminName[32]; + if (ConnectingClientAdminID != INVALID_ADMIN_ID) + { + GetAdminUsername(ConnectingClientAdminID, ConnectingClientAdminName, sizeof(ConnectingClientAdminName)); + } + else if (isDonator) + { + Format(ConnectingClientAdminName, sizeof(ConnectingClientAdminName), "DONATOR"); + } + else + { + Format(ConnectingClientAdminName, sizeof(ConnectingClientAdminName), "ADMIN_NAME_ERROR"); + } + LogToFileEx(g_LogFilePath, "Connecting player \"%s\" (cfg: \"%s\") [%02d]", ConnectingClientName, ConnectingClientAdminName, ConnectingClientImmunity); + LogToFileEx(g_LogFilePath, "Lowest immunity: %02d", LowestImmunityLevel); + LogToFileEx(g_LogFilePath, "High immunity player count: %d", g_HIPCount); + LogToFileEx(g_LogFilePath, "Max player count: %d", MaxClients); + } + + // Two Loops Supremacy + if (Immunity && !HighestValueId && !HighestSpecValueId) + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "All players immune, running extra immunity check"); + } + + immunityKick = true; + + for (new i=1; i<=MaxClients; i++) + { + if (!IsClientConnected(i)) + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "%02d: NOT CONNECTED", i); + } + continue; + } + + if (IsFakeClient(i)) + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "%02d: BOT", i); + } + continue; + } + + if (useKickList) + { + decl String:PlayerAuth[32]; + GetClientAuthString(i, PlayerAuth, sizeof(PlayerAuth)); + if (FindStringInArray(arr_KickListIDs, PlayerAuth) == -1) + { + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + } + + if (ClientImmunity[i] > LowestImmunityLevel) + { + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + + if (ClientImmunity[i] >= ConnectingClientImmunity) + { + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i]); + } + continue; + } + + if (IsClientInGame(i)) + { + switch (KickType) + { + case 0: + value = GetURandomFloat(); + case 1: + value = GetClientAvgLatency(i, NetFlow_Outgoing); + case 2: + value = GetClientTime(i); + case 3: + value = GetClientTime(i); + } + + if (KickType == 3 && !HighestValue) + { + HighestValue = value; + } + + if ((clientTeam[i] == SPEC || clientTeam[i] == 0) && (SpecKick || useKeepBalance)) + { + if (SpecKickDelay && b_canKickSpec[i] || !SpecKickDelay) + { + if (KickType == 3 && !HighestSpecValue) + { + HighestSpecValue = value; + } + + if (KickType == 3 && value <= HighestSpecValue) + { + HighestSpecValue = value; + HighestSpecValueId = i; + } + else if (KickType != 3 && value >= HighestSpecValue) + { + HighestSpecValue = value; + HighestSpecValueId = i; + } + } + } + else if (KickType == 3 && value <= HighestValue) + { + HighestValue = value; + HighestValueId = i; + } + else if (KickType != 3 && value >= HighestValue) + { + HighestValue = value; + HighestValueId = i; + } + + if (useKeepBalance && clientTeam[i] == useTeam) + { + if (KickType == 3 && !HighestBalanceValue) + { + HighestBalanceValue = value; + } + + if (KickType == 3 && value <= HighestBalanceValue) + { + HighestBalanceValue = value; + HighestBalanceValueId = i; + } + else if (KickType != 3 && value >= HighestBalanceValue) + { + HighestBalanceValue = value; + HighestBalanceValueId = i; + } + } + } + + if (Logging == 1) + { + IRS_LogClient(i, clientTeam[i], ClientImmunity[i], value); + } + } + } + + new KickTarget; + + if (HighestSpecValueId) + { + KickTarget = HighestSpecValueId; + } + else if (HighestBalanceValueId) + { + KickTarget = HighestBalanceValueId; + } + else + { + KickTarget = HighestValueId; + } + + if (KickTarget) + { + decl String:KickName[32]; + decl String:KickAuthid[32]; + GetClientName(KickTarget, KickName, sizeof(KickName)); + GetClientAuthString(KickTarget, KickAuthid, sizeof(KickAuthid)); + + if (!immunityKick) + { + decl String:Reason[255]; + GetConVarString(cvar_KickReason, Reason, sizeof(Reason)); + if (StrEqual(Reason, "default", false)) + { + Format(Reason, sizeof(Reason), "%t", "IRS Kick Reason"); + } + KickClientEx(KickTarget, "%s", Reason); + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "\"%s\" was kicked", KickName); + } + if (Logging == 2) + { + LogMessage("%t", "IRS Kick Log", ConnectingClientName, ConnectingClientAuthID, KickName, KickAuthid); + } + } + else + { + new HighImmunityLimit = GetConVarInt(cvar_HighImmunityLimit); + if (HighImmunityLimit && g_HIPCount >= HighImmunityLimit) + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "Too many high immunity players connected (%d players)", g_HIPCount); + } + return false; + } + decl String:Reason[255]; + GetConVarString(cvar_KickReasonImmunity, Reason, sizeof(Reason)); + if (StrEqual(Reason, "default", false)) + { + Format(Reason, sizeof(Reason), "%t", "IRS Kick Reason Immunity"); + } + KickClientEx(KickTarget, "%s", Reason); + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "\"%s\" was kicked (Low immunity)", KickName); + } + if (Logging == 2) + { + LogMessage("%t", "IRS Kick Log", ConnectingClientName, ConnectingClientAuthID, KickName, KickAuthid); + } + } + return true; + } + else + { + if (Logging == 1) + { + LogToFileEx(g_LogFilePath, "No valid client found to kick"); + } + } + + return false; +} + +public OnClientPostAdminCheck(client) +{ + if (IsFakeClient(client)) + { + return; + } + + // I do this here as it makes sure the client is actually connected, I don't want to add anyone too early. + new HighImmunityValue = GetConVarInt(cvar_HighImmunityValue); + if (GetConVarInt(cvar_HighImmunityLimit) && HighImmunityValue && GetAdminImmunityLevel(GetUserAdmin(client)) >= HighImmunityValue) + { + g_HighImmunityPlayers[client] = true; + g_HIPCount++; + } + + new Float:KickSpecDelay = GetConVarFloat(cvar_SpecKickDelay); + if (KickSpecDelay) + { + CheckKickSpecDelay(KickSpecDelay, client); + } +} + +public OnClientDisconnect(client) +{ + if (IsFakeClient(client)) + { + return; + } + + if (!GetConVarInt(cvar_HighImmunityLimit)) + { + return; + } + + if (g_HighImmunityPlayers[client]) + { + g_HighImmunityPlayers[client] = false; + g_HIPCount--; + } + + if (t_KickSpecClient[client] != INVALID_HANDLE) + { + KillTimer(t_KickSpecClient[client]); + t_KickSpecClient[client] = INVALID_HANDLE; + b_canKickSpec[client] = false; + } +} + +public Action:listen_join_team(client, const String:command[], argc) +{ + new Float:KickSpecDelay = GetConVarFloat(cvar_SpecKickDelay); + + if (!KickSpecDelay || IsFakeClient(client)) + { + return Plugin_Continue; + } + + if (StrEqual(command, "jointeam", false) || StrEqual(command, "spectate", false)) + { + CheckKickSpecDelay(KickSpecDelay, client); + } + + return Plugin_Continue; +} + +CheckKickSpecDelay(const Float:delay, const client) +{ + new clientTeam = GetClientTeam(client); + if ((clientTeam != TEAM1 || clientTeam != TEAM2) && t_KickSpecClient[client] == INVALID_HANDLE) + { + t_KickSpecClient[client] = CreateTimer(delay, t_KickSpecClientTimer, client); + } + else if ((clientTeam == TEAM1 || clientTeam == TEAM2) && t_KickSpecClient[client] != INVALID_HANDLE) + { + KillTimer(t_KickSpecClient[client]); + t_KickSpecClient[client] = INVALID_HANDLE; + } +} + +public Action:t_KickSpecClientTimer(Handle:timer, any:client) +{ + if (IsClientInGame(client)) + { + b_canKickSpec[client] = true; + } + + t_KickSpecClient[client] = INVALID_HANDLE; + return Plugin_Handled; +} + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + if (late) + { + b_lateLoad = true + } + + // I think insurgency is the only game that has different team indexes. I'll probably never ever check either, I'm terrible. + decl String:game[32]; + GetGameFolderName(game, sizeof(game)); + if (StrEqual(game, "insurgency")) + { + TEAM1 = 1; + TEAM2 = 2; + SPEC = 3; + } + else + { + SPEC = 1; + TEAM1 = 2; + TEAM2 = 3; + } + + MarkNativeAsOptional("IsPlayerDonator"); + MarkNativeAsOptional("FindDonatorBySteamId"); + + return APLRes_Success; +} \ No newline at end of file diff --git a/immunityreserveslots/scripting/include/connect.inc b/immunityreserveslots/scripting/include/connect.inc new file mode 100644 index 00000000..445a1f68 --- /dev/null +++ b/immunityreserveslots/scripting/include/connect.inc @@ -0,0 +1,22 @@ +#if defined _connect_included +#endinput +#endif +#define _connect_included + +forward bool:OnClientPreConnectEx(const String:name[], String:password[255], const String:ip[], const String:steamID[], String:rejectReason[255]); + +public Extension:__ext_Connect = +{ + name = "Connect", + file = "connect.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +} diff --git a/immunityreserveslots/scripting/include/donator.inc b/immunityreserveslots/scripting/include/donator.inc new file mode 100644 index 00000000..558502ef --- /dev/null +++ b/immunityreserveslots/scripting/include/donator.inc @@ -0,0 +1,106 @@ +#if defined _donator_included_ + #endinput +#endif +#define _donator_included_ + +#define DONATOR_API_VERSON 1.1 + +functag DonatorMenuCallback DonatorMenu:public(client); + +/** + * Register a menu item. + * + * @param name Name of the menu item. + * @param func Callback for menu items. + * @return Menu item ID. + */ +native Donator_RegisterMenuItem(const String:name[], DonatorMenuCallback:callback); + +/** + * Unregister a menu item. + * + * @param name Name of the menu item. + * @param func Callback for menu items. + * @return Bool + */ +native Donator_UnregisterMenuItem(iItemId); + +/** + * Get a clients donator level, -1 if invalid + * + * @param iClient Client + * @return Donator level + */ +native GetDonatorLevel(iClient); + +/** + * Sets a clients donator level + * + * @param iClient Client + * @param iLevel Donator level + * @return Nothing + */ +native SetDonatorLevel(iClient, iLevel); + +/** + * Returns True if a client is a donator, -1 if invalid + * + * @param iClient Client + * @return bool + */ +native bool:IsPlayerDonator(iClient); + +/** + * Returns True if a steamid is a donator, -1 if invalid + * + * @param iClient Client + * @return bool + */ +native bool:FindDonatorBySteamId(const String:szSteamId[]); + +/** + * Returns a donators connect message + + * @param iClient Client + * @return Clients connect message + */ +native GetDonatorMessage(iClient, const String:szMessage[], iLength); + +/** + * Sets a donators connect message + * + * @param iClient Client + * @param szMessage Message to show on donator connect + * @return Nothing + */ +native SetDonatorMessage(iClient, const String:szMessage[]); + +/* +native SaveToDatabase(const String:szColumnName[], any:data); +native GetFromDatabase(const String:szColumnName[], any:data); +*/ + +/** + * Forwards when a donator connects. + * Note: This is before OnPostDonatorCheck - Cookies are not loaded here + * + * @param iClient Client + * @noreturn + */ +forward OnDonatorConnect(iClient); + +/** + * Forwards after OnPostAdminCheck for everyone. + * + * @param iClient Client + * @noreturn + */ +forward OnPostDonatorCheck(iClient); + +/** + * Forwards after the donators has been reladed with sm_reloaddonators. + * + * @param iClient Client + * @noreturn + */ +forward OnDonatorsChanged(); diff --git a/immunityreserveslots/scripting/include/entWatch.inc b/immunityreserveslots/scripting/include/entWatch.inc new file mode 120000 index 00000000..d797d428 --- /dev/null +++ b/immunityreserveslots/scripting/include/entWatch.inc @@ -0,0 +1 @@ +../../../includes/entWatch.inc \ No newline at end of file diff --git a/includes/entWatch.inc b/includes/entWatch.inc new file mode 100644 index 00000000..0e6a2f27 --- /dev/null +++ b/includes/entWatch.inc @@ -0,0 +1,150 @@ +#if defined _entWatch_include + #endinput +#endif +#define _entWatch_include + +/** + * Checks if a client is currently banned, if an integer variable is referenced the time of unban will be assigned to it. + * + * @param client Client index to check for ban + * @param iTimeStamp Pass an integer variable by reference and it will contain the UNIX timestamp when the player will be unbanned + * @return True if user is banned, false otherwise + * + * On error/errors: Invalid client index/client is not in game or client cookies are not yet loaded + */ +native bool:entWatch_IsClientBanned(client, &iTimeStamp); + +/** + * Bans a client from using special items. + * + * @param client Client index to ban + * @param IsTemporary If the ban should be temporary pass true here + * @param iLength Length of ban in minutes, pass 0 here for a permanent ban + * @return True on success, false otherwsie + * + * On error/errors: Invalid client index/client is not in game or client cookies are not yet loaded + */ +native bool:entWatch_BanClient(client, bool:bIsTemporary=false, iLength=0); + +/** + * Unbans a previously ebanned Client. + * + * @param client Client index to unban + * @return True on success, false otherwsie + * + * On error/errors: Invalid client index/client is not in game or client cookies are not yet loaded + */ +native bool:entWatch_UnbanClient(client); + +/** + * Checks if an entity is a special item. + * + * @param entity Entity index to check + * @return True if entity is a special item, false otherwsie + */ +native bool:entWatch_IsSpecialItem(entity); + +/** + * Checks if a client has a special item. + * + * @param client Client index to check + * @return True if client has a special item, false otherwsie + */ +native bool:entWatch_HasSpecialItem(client); + +/** + * Called when a client is e-banned by any means + * + * @param admin Admin index that issued the ban + * @param iLength Length of the ban in UNIX time + * @param client Client index that was banned + * + * @return None + */ +forward entWatch_OnClientBanned(admin, iLenght, client); + +/** + * Called when a client is e-unbanned by any means + * + * @param admin Admin index that removed the ban + * @param client Client index that was unbanned + * @return None + */ +forward entWatch_OnClientUnbanned(admin, client); + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetTargetName(entity, String:buffer[], size) +{ + return GetEntPropString(entity, Prop_Data, "m_iName", buffer, size); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetParentName(entity, String:buffer[], size) +{ + return GetEntPropString(entity, Prop_Data, "m_iParent", buffer, size); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetHammerID(entity) +{ + return GetEntProp(entity, Prop_Data, "m_iHammerID"); +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetClassName(entity, String:buffer[], size) +{ + GetEntPropString(entity, Prop_Data, "m_iClassname", buffer, size); + + if (buffer[0] == '\0') { + return false; + } + + return true; +} + +//---------------------------------------------------------------------------------------------------- +// Purpose: SMLib +//---------------------------------------------------------------------------------------------------- +stock Entity_GetEntityFromHammerID(hammerID) +{ + for (new i = 0; i < 4096; i++) + { + if (IsValidEntity(i) && Entity_GetHammerID(i) == hammerID) + { + if (IsValidEntity(i)) + return i; + } + } + + return -1; +} + +public SharedPlugin:__pl_entWatch = +{ + name = "entWatch", + file = "entWatch.smx", +#if defined REQUIRE_PLUGIN + required = 1 +#else + required = 0 +#endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_entWatch_SetNTVOptional() +{ + MarkNativeAsOptional("entWatch_IsClientBanned"); + MarkNativeAsOptional("entWatch_BanClient"); + MarkNativeAsOptional("entWatch_UnbanClient"); + MarkNativeAsOptional("entWatch_IsSpecialItem"); + MarkNativeAsOptional("entWatch_HasSpecialItem"); +} +#endif diff --git a/sourcebans/configs/sourcebans/sb_admin_groups.cfg b/sourcebans/configs/sourcebans/sb_admin_groups.cfg new file mode 100644 index 00000000..e69de29b diff --git a/sourcebans/configs/sourcebans/sb_admins.cfg b/sourcebans/configs/sourcebans/sb_admins.cfg new file mode 100644 index 00000000..e69de29b diff --git a/sourcebans/configs/sourcebans/sourcebans.cfg b/sourcebans/configs/sourcebans/sourcebans.cfg new file mode 100644 index 00000000..364c7e43 --- /dev/null +++ b/sourcebans/configs/sourcebans/sourcebans.cfg @@ -0,0 +1,84 @@ +/** +* sourcebans.cfg +* +* This file contains settings for the SourceBans Source Server Plugin +* @author SteamFriends Development Team +* @version 0.0.0.$Rev: 74 $ +* @copyright SteamFriends (www.steamfriends.com) +* @package SourceBans +*/ + +"SourceBans" +{ + "Config" + { + // Website address to tell where the player to go for unban, etc + "Website" "http://www.yourwebsite.net/" + + // Allow or disallow admins access to addban command + "Addban" "1" + + // Allow or disallow admins access to unban command + "Unban" "1" + + // The Tableprefix you set while installing the webpanel. (default: "sb") + "DatabasePrefix" "sb" + + // How many seconds to wait before retrying when a players ban fails to be checked. Min = 15.0 Max = 60.0 + "RetryTime" "45.0" + + // How often should we process the failed ban queue in minutes + "ProcessQueueTime" "5" + + // Should the plugin automaticaly add the server to sourcebans + // (servers without -ip being set on startup need this set to 0) + "AutoAddServer" "0" + + // Enable backing up config files after getting admins from database (1 = enabled, 0 = disabled) + "BackupConfigs" "1" + + // Enable admin part of the plugin (1 = enabled, 0 = disabled) + "EnableAdmins" "1" + + // Require the admin to login once into website + "RequireSiteLogin" "0" + + // This is the ID of this server (Check in the admin panel -> servers to find the ID of this server) + "ServerID" "-1" + } + + /* + * Generic menu options for if a reason isn't supplied in a ban + * Without a supplied reason the ban will never be written to the database + */ + "BanReasons" + { + "Hacking" "Hacking" + "Exploit" "General Exploit of Game/Map/Server" + "TK" "Team Killing" + "TF" "Team Flashing" + "CommSpam" "Spamming Mic/Chat" + "BadSpray" "Inappropriate Spray" + "BadLang" "Inappropriate Language" + "BadName" "Inappropriate Name" + "IgnoreAdmin" "Ignoring Admins" + "Stacking" "Team Stacking" + "Own Reason" "Own Reason" + } + + /* + * Submenu options for when "Hacking" is selected + * If "Hacking" is removed from the menu above this will not be accessable + */ + "HackingReasons" + { + "Aimbot" "Aimbot" + "Antirecoil" "Anti Recoil" + "Wallhack" "Wallhack" + "Spinhack" "Spinhack" + "Speedhack" "Speedhack" + "Multi-Hack" "Multi-Hack" + "No Smoke" "No Smoke" + "No Flash" "No Flash" + } +} \ No newline at end of file diff --git a/sourcebans/configs/sourcebans/sourcecomms.cfg b/sourcebans/configs/sourcebans/sourcecomms.cfg new file mode 100644 index 00000000..52732221 --- /dev/null +++ b/sourcebans/configs/sourcebans/sourcecomms.cfg @@ -0,0 +1,66 @@ +/** +* sourcecomms.cfg +* +* This file contains settings for the SourceComms Plugin +*/ + +"SourceComms" +{ + "Config" + { + "DefaultTime" "-1" // default time in minutes. if < 0 -> blocking for session. Permanent (0) - is not allowed! + "DisableUnblockImmunityCheck" "0" // 0, 1. If 1, player can be ungagged only by issuer admin, console or admin with special flag + // Also, If 0 player maybe unblocked by Admin with higher immunity level then issuer admin. + // Default value is 0 + "ConsoleImmunity" "100" // Immunity Level of server console. If not specified - 0. + "MaxLength" "0" // Max allowed punishment length (in minutes) for admins without ADMFLAG_CUSTOM2 (p). + // 0 disables restriction. Any value > 0 restricts permanent punishment. + "OnlyWhiteListServers" "0" // Set this option to 1 to applying on players punishments only from servers listed in WhiteList and this server. + // 0 applies on players punishments from any server. + } + + "CommsReasons" + { + //Generic menu options for if a reason isn't supplied in a block + //-------------------------------------------------------------// + // "Reason to store in DB" "Reason to display in menu" // + //-------------------------------------------------------------// + "Obscene language" "Obscene language" + "Insult players" "Insult players" + "Admin disrespect" "Admin disrespect" + "Inappropriate Language" "Inappropriate Language" + "Spam in chat/voice" "Spam" + "Trading" "Trading" + "Other" "Other" + "Advertisement" "Advertisement" + "Played music in voice" "Music in voice" + } + + "CommsTimes" + { + // Times to show in duration menu // + //-----------------------------------------------// + // "Time in minutes" "Time to display in menu" // + //-----------------------------------------------// + "-1" "Session" // If time < 0 -> blocking comms for player session + "30" "30 minutes" + "60" "60 minutes" + "120" "2 hours" + "360" "6 hours" + "720" "12 hours" + "1440" "24 hours" + "2880" "2 days" + "10080" "7 days" + "20160" "2 weeks" + "0" "Permanent" + } + + "ServersWhiteList" + { + //-----------------------------------------// + // "id" "ServerID from sourcebans.cfg" // + //-----------------------------------------// + "id" "0" // Web Punishments (from sourcebans web pages) +// "id" "3" // for example: uncommenting this line will add server with ServerID 3 to white list. + } +} \ No newline at end of file diff --git a/sourcebans/scripting/include/sourcebans.inc b/sourcebans/scripting/include/sourcebans.inc new file mode 100644 index 00000000..f65d2ae3 --- /dev/null +++ b/sourcebans/scripting/include/sourcebans.inc @@ -0,0 +1,62 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 Sarabveer Singh +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file incorporates work covered by the following copyright(s): +// +// SourceBans 1.4.11 +// Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#if defined _sourcebans_included +#endinput +#endif +#define _sourcebans_included + +public SharedPlugin:__pl_sourcebans = +{ + name = "SourceBans", + file = "sourcebans.smx", + #if defined REQUIRE_PLUGIN + required = 1 + #else + required = 0 + #endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_sourcebans_SetNTVOptional() +{ + MarkNativeAsOptional("SBBanPlayer"); +} +#endif + + +/********************************************************* + * Ban Player from server + * + * @param client The client index of the admin who is banning the client + * @param target The client index of the player to ban + * @param time The time to ban the player for (in minutes, 0 = permanent) + * @param reason The reason to ban the player from the server + * @noreturn + *********************************************************/ +native SBBanPlayer(client, target, time, String:reason[]); + +//Yarr! diff --git a/sourcebans/scripting/include/sourcecomms.inc b/sourcebans/scripting/include/sourcecomms.inc new file mode 100644 index 00000000..feb62d0a --- /dev/null +++ b/sourcebans/scripting/include/sourcecomms.inc @@ -0,0 +1,103 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 Sarabveer Singh +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file incorporates work covered by the following copyright(s): +// +// SourceComms 0.9.266 +// Copyright (C) 2013-2014 Alexandr Duplishchev +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#if defined _sourcecomms_included +#endinput +#endif +#define _sourcecomms_included + +/* Punishments types */ +enum bType { + bNot = 0, // Player chat or voice is not blocked + bSess, // ... blocked for player session (until reconnect) + bTime, // ... blocked for some time + bPerm // ... permanently blocked +} + +/** + * Sets a client's mute state. + * + * @param client Client index. + * @param muteState True to mute client, false to unmute. + * -------------------------------------Parameters below this line are used only for muteState=true------------------------------------- + * ----------------------------------for muteState=false these parameters are ignored (saveToDB=false)---------------------------------- + * @param muteLength Length of punishment in minutes. Value < 0 muting client for session. Permanent (0) is not allowed at this time. + * @param saveToDB If true, punishment will be saved in database. + * @param reason Reason for punishment. + * @return True if this caused a change in mute state, false otherwise. + */ +native bool:SourceComms_SetClientMute(client, bool:muteState, muteLength = -1, bool:saveToDB = false, const String:reason[] = "Muted through natives"); + +/** + * Sets a client's gag state. + * + * @param client Client index. + * @param gagState True to gag client, false to ungag. + * --------------------------------------Parameters below this line are used only for gagState=true-------------------------------------- + * -----------------------------------for gagState=false these parameters are ignored (saveToDB=false)----------------------------------- + * @param gagLength Length of punishment in minutes. Value < 0 gagging client for session. Permanent (0) is not allowed at this time. + * @param saveToDB If true, punishment will be saved in database. + * @param reason Reason for punishment. + * @return True if this caused a change in gag state, false otherwise. + */ +native bool:SourceComms_SetClientGag(client, bool:gagState, gagLength = -1, bool:saveToDB = false, const String:reason[] = "Gagged through natives"); + +/** + * Returns the client's mute type + * + * @param client The client index of the player to check mute status + * @return The client's current mute type index (see enum bType in the begin). + */ +native bType:SourceComms_GetClientMuteType(client); + + +/** + * Returns the client's gag type + * + * @param client The client index of the player to check gag status + * @return The client's current gag type index (see enum bType in the begin). + */ +native bType:SourceComms_GetClientGagType(client); + +public SharedPlugin:__pl_sourcecomms = +{ + name = "sourcecomms", + file = "sourcecomms.smx", + #if defined REQUIRE_PLUGIN + required = 1 + #else + required = 0 + #endif +}; + +public __pl_sourcecomms_SetNTVOptional() +{ + MarkNativeAsOptional("SourceComms_SetClientMute"); + MarkNativeAsOptional("SourceComms_SetClientGag"); + MarkNativeAsOptional("SourceComms_GetClientMuteType"); + MarkNativeAsOptional("SourceComms_GetClientGagType"); + +} diff --git a/sourcebans/scripting/sbchecker.sp b/sourcebans/scripting/sbchecker.sp new file mode 100644 index 00000000..6fad74cb --- /dev/null +++ b/sourcebans/scripting/sbchecker.sp @@ -0,0 +1,383 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 Sarabveer Singh +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file incorporates work covered by the following copyright(s): +// +// SourceBans Checker 1.0.2 +// Copyright (C) 2010-2013 Nicholas Hastings +// Licensed under GNU GPL version 3, or later. +// Page: +// +// ************************************************************************* +#include + +#define VERSION "(SB++) 1.5.5" +#define LISTBANS_USAGE "sm_listsbbans <#userid|name> - Lists a user's prior bans from Sourcebans" +#define INVALID_TARGET -1 + +new String:g_DatabasePrefix[10] = "sb"; +new Handle:g_ConfigParser; +new Handle:g_DB; + +ConVar ShortMessage; + +public Plugin:myinfo = +{ + name = "SourceBans Checker", + author = "psychonic, Ca$h Munny, Sarabveer(VEER™)", + description = "Notifies admins of prior bans from Sourcebans upon player connect.", + version = VERSION, + url = "http://www.nicholashastings.com" +}; + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + + CreateConVar("sbchecker_version", VERSION, "", FCVAR_NOTIFY); + + ShortMessage = CreateConVar("sb_short_message", "0", "Use shorter message for displying prev bans", _, true, 0.0, true, 1.0); + + RegAdminCmd("sm_listbans", OnListSourceBansCmd, ADMFLAG_BAN, LISTBANS_USAGE); + RegAdminCmd("sb_reload", OnReloadCmd, ADMFLAG_RCON, "Reload sourcebans config and ban reason menu options"); + + SQL_TConnect(OnDatabaseConnected, "sourcebans"); +} + +public OnMapStart() +{ + ReadConfig(); +} + +public Action:OnReloadCmd(client, args) +{ + ReadConfig(); + return Plugin_Handled; +} + +public OnDatabaseConnected(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + if (hndl == INVALID_HANDLE) + SetFailState("Failed to connect to SourceBans DB, %s", error); + + g_DB = hndl; +} + +public OnClientAuthorized(client, const String:auth[]) +{ + if (g_DB == INVALID_HANDLE) + return; + + /* Do not check bots nor check player with lan steamid. */ + if (auth[0] == 'B' || auth[9] == 'L') + return; + + decl String:query[512], String:ip[30]; + GetClientIP(client, ip, sizeof(ip)); + FormatEx(query, sizeof(query), "SELECT COUNT(bid) FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND ((length > '0' AND ends > UNIX_TIMESTAMP()) OR RemoveType IS NOT NULL)", g_DatabasePrefix, auth[8], ip); + + SQL_TQuery(g_DB, OnConnectBanCheck, query, GetClientUserId(client), DBPrio_Low); +} + +public OnConnectBanCheck(Handle:owner, Handle:hndl, const String:error[], any:userid) +{ + new client = GetClientOfUserId(userid); + + if (!client || hndl == INVALID_HANDLE || !SQL_FetchRow(hndl)) + return; + + new bancount = SQL_FetchInt(hndl, 0); + if (bancount > 0) + { + if (ShortMessage.BoolValue) + { + PrintToBanAdmins("\x04[SB]\x01Player \"%N\" has %d previous ban%s.", + client, bancount, ((bancount > 0) ? "s":"")); + } + else + { + PrintToBanAdmins("\x04[SourceBans]\x01 Warning: Player \"%N\" has %d previous ban%s on record.", + client, bancount, ((bancount > 0) ? "s":"")); + } + } +} + +public Action:OnListSourceBansCmd(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, LISTBANS_USAGE); + } + + if (g_DB == INVALID_HANDLE) + { + ReplyToCommand(client, "Error: Database not ready."); + return Plugin_Handled; + } + + decl String:targetarg[64]; + GetCmdArg(1, targetarg, sizeof(targetarg)); + + new target = FindTarget(client, targetarg, true, true); + if (target == INVALID_TARGET) + { + ReplyToCommand(client, "Error: Could not find a target matching '%s'.", targetarg); + return Plugin_Handled; + } + + decl String:auth[32]; + if (!GetClientAuthId(target, AuthId_Steam2, auth, sizeof(auth)) + || auth[0] == 'B' || auth[9] == 'L') + { + ReplyToCommand(client, "Error: Could not retrieve %N's steam id.", target); + return Plugin_Handled; + } + + decl String:query[1024], String:ip[30]; + GetClientIP(target, ip, sizeof(ip)); + FormatEx(query, sizeof(query), "SELECT created, %s_admins.user, ends, length, reason, RemoveType FROM %s_bans LEFT JOIN %s_admins ON %s_bans.aid = %s_admins.aid WHERE ((type = 0 AND %s_bans.authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND ((length > '0' AND ends > UNIX_TIMESTAMP()) OR RemoveType IS NOT NULL)", g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, auth[8], ip); + + decl String:targetName[MAX_NAME_LENGTH]; + GetClientName(target, targetName, sizeof(targetName)); + + new Handle:pack = CreateDataPack(); + WritePackCell(pack, (client == 0) ? 0 : GetClientUserId(client)); + WritePackString(pack, targetName); + + SQL_TQuery(g_DB, OnListBans, query, pack, DBPrio_Low); + + if (client == 0) + { + ReplyToCommand(client, "[SourceBans] Note: if you are using this command through an rcon tool, you will not see results."); + } + else + { + ReplyToCommand(client, "\x04[SourceBans]\x01 Look for %N's ban results in console.", target); + } + + return Plugin_Handled; +} + +public OnListBans(Handle:owner, Handle:hndl, const String:error[], any:pack) +{ + ResetPack(pack); + new clientuid = ReadPackCell(pack); + new client = GetClientOfUserId(clientuid); + decl String:targetName[MAX_NAME_LENGTH]; + ReadPackString(pack, targetName, sizeof(targetName)); + CloseHandle(pack); + + if (clientuid > 0 && client == 0) + return; + + if (hndl == INVALID_HANDLE) + { + PrintListResponse(clientuid, client, "[SourceBans] DB error while retrieving bans for %s:\n%s", targetName, error); + return; + } + + if (SQL_GetRowCount(hndl) == 0) + { + PrintListResponse(clientuid, client, "[SourceBans] No bans found for %s.", targetName); + return; + } + + PrintListResponse(clientuid, client, "[SourceBans] Listing bans for %s", targetName); + PrintListResponse(clientuid, client, "Ban Date Banned By Length End Date R Reason"); + PrintListResponse(clientuid, client, "-------------------------------------------------------------------------------"); + while (SQL_FetchRow(hndl)) + { + new String:createddate[11] = " "; + new String:bannedby[11] = " "; + new String:lenstring[11] = "N/A "; + new String:enddate[11] = "N/A "; + decl String:reason[28]; + new String:RemoveType[2] = " "; + + if (!SQL_IsFieldNull(hndl, 0)) + { + FormatTime(createddate, sizeof(createddate), "%Y-%m-%d", SQL_FetchInt(hndl, 0)); + } + + if (!SQL_IsFieldNull(hndl, 1)) + { + new size_bannedby = sizeof(bannedby); + SQL_FetchString(hndl, 1, bannedby, size_bannedby); + new len = SQL_FetchSize(hndl, 1); + if (len > size_bannedby - 1) + { + reason[size_bannedby - 4] = '.'; + reason[size_bannedby - 3] = '.'; + reason[size_bannedby - 2] = '.'; + } + else + { + for (new i = len; i < size_bannedby - 1; i++) + { + bannedby[i] = ' '; + } + } + } + + // NOT NULL + new size_lenstring = sizeof(lenstring); + new length = SQL_FetchInt(hndl, 3); + if (length == 0) + { + strcopy(lenstring, size_lenstring, "Permanent "); + } + else + { + new len = IntToString(length, lenstring, size_lenstring); + if (len < size_lenstring - 1) + { + // change the '\0' to a ' '. the original \0 at the end will still be there + lenstring[len] = ' '; + } + } + + if (!SQL_IsFieldNull(hndl, 2)) + { + FormatTime(enddate, sizeof(enddate), "%Y-%m-%d", SQL_FetchInt(hndl, 2)); + } + + // NOT NULL + new reason_size = sizeof(reason); + SQL_FetchString(hndl, 4, reason, reason_size); + new len = SQL_FetchSize(hndl, 4); + if (len > reason_size - 1) + { + reason[reason_size - 4] = '.'; + reason[reason_size - 3] = '.'; + reason[reason_size - 2] = '.'; + } + else + { + for (new i = len; i < reason_size - 1; i++) + { + reason[i] = ' '; + } + } + + if (!SQL_IsFieldNull(hndl, 5)) + { + SQL_FetchString(hndl, 5, RemoveType, sizeof(RemoveType)); + } + + PrintListResponse(clientuid, client, "%s %s %s %s %s %s", createddate, bannedby, lenstring, enddate, RemoveType, reason); + } +} + +PrintListResponse(userid, client, const String:format[], any:...) +{ + decl String:msg[192]; + VFormat(msg, sizeof(msg), format, 4); + + if (userid == 0) + { + PrintToServer("%s", msg); + } + else + { + PrintToConsole(client, "%s", msg); + } +} + +PrintToBanAdmins(const String:format[], any:...) +{ + decl String:msg[128]; + VFormat(msg, sizeof(msg), format, 2); + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) + && CheckCommandAccess(i, "sm_listsourcebans", ADMFLAG_BAN) + ) + { + PrintToChat(i, "%s", msg); + } + } +} + +stock ReadConfig() +{ + InitializeConfigParser(); + + if (g_ConfigParser == INVALID_HANDLE) + { + return; + } + + decl String:ConfigFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, ConfigFile, sizeof(ConfigFile), "configs/sourcebans/sourcebans.cfg"); + + if (FileExists(ConfigFile)) + { + InternalReadConfig(ConfigFile); + } + else + { + decl String:Error[PLATFORM_MAX_PATH + 64]; + FormatEx(Error, sizeof(Error), "FATAL *** ERROR *** can not find %s", ConfigFile); + SetFailState(Error); + } +} + +static InitializeConfigParser() +{ + if (g_ConfigParser == INVALID_HANDLE) + { + g_ConfigParser = SMC_CreateParser(); + SMC_SetReaders(g_ConfigParser, ReadConfig_NewSection, ReadConfig_KeyValue, ReadConfig_EndSection); + } +} + +static InternalReadConfig(const String:path[]) +{ + new SMCError:err = SMC_ParseFile(g_ConfigParser, path); + + if (err != SMCError_Okay) + { + decl String:buffer[64]; + PrintToServer("%s", SMC_GetErrorString(err, buffer, sizeof(buffer)) ? buffer : "Fatal parse error"); + } +} + +public SMCResult:ReadConfig_NewSection(Handle:smc, const String:name[], bool:opt_quotes) +{ + return SMCParse_Continue; +} + +public SMCResult:ReadConfig_KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) +{ + if (strcmp("DatabasePrefix", key, false) == 0) + { + strcopy(g_DatabasePrefix, sizeof(g_DatabasePrefix), value); + + if (g_DatabasePrefix[0] == '\0') + { + g_DatabasePrefix = "sb"; + } + } + + return SMCParse_Continue; +} + +public SMCResult:ReadConfig_EndSection(Handle:smc) +{ + return SMCParse_Continue; +} diff --git a/sourcebans/scripting/sourcebans.sp b/sourcebans/scripting/sourcebans.sp new file mode 100644 index 00000000..4698b540 --- /dev/null +++ b/sourcebans/scripting/sourcebans.sp @@ -0,0 +1,2570 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 Sarabveer Singh +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file incorporates work covered by the following copyright(s): +// +// SourceBans 1.4.11 +// Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#pragma semicolon 1 +#include +#include + +#undef REQUIRE_PLUGIN +#include +//#tryinclude + +#define SB_VERSION "1.5.5F" +#define SBR_VERSION "1.5.5" + +#if defined _updater_included +#define UPDATE_URL "https://sarabveer.github.io/SourceBans-Fork/updater/updatefile.txt" +#endif + +//GLOBAL DEFINES +#define YELLOW 0x01 +#define NAMECOLOR 0x02 +#define TEAMCOLOR 0x03 +#define GREEN 0x04 + +#define DISABLE_ADDBAN 1 +#define DISABLE_UNBAN 2 + +#define FLAG_LETTERS_SIZE 26 + +//#define DEBUG + +enum State/* ConfigState */ +{ + ConfigStateNone = 0, + ConfigStateConfig, + ConfigStateReasons, + ConfigStateHacking +} + +new g_BanTarget[MAXPLAYERS + 1] = { -1, ... }; +new g_BanTime[MAXPLAYERS + 1] = { -1, ... }; + +new State:ConfigState; +new Handle:ConfigParser; + +new Handle:hTopMenu = INVALID_HANDLE; + +new const String:Prefix[] = "[SourceBans] "; + +new String:ServerIp[24]; +new String:ServerPort[7]; +new String:DatabasePrefix[10] = "sb"; +new String:WebsiteAddress[128]; + +/* Admin Stuff*/ +new AdminCachePart:loadPart; +new bool:loadAdmins; +new bool:loadGroups; +new bool:loadOverrides; +new curLoading = 0; +new AdminFlag:g_FlagLetters[FLAG_LETTERS_SIZE]; + +/* Admin KeyValues */ +new String:groupsLoc[128]; +new String:adminsLoc[128]; +new String:overridesLoc[128]; + +/* Cvar handle*/ +new Handle:CvarHostIp; +new Handle:CvarPort; + +/* Database handle */ +new Handle:DB; +new Handle:SQLiteDB; + +/* Menu file globals */ +new Handle:ReasonMenuHandle; +new Handle:HackingMenuHandle; + +/* Datapack and Timer handles */ +new Handle:PlayerRecheck[MAXPLAYERS + 1] = { INVALID_HANDLE, ... }; +new Handle:PlayerDataPack[MAXPLAYERS + 1] = { INVALID_HANDLE, ... }; + +/* Player ban check status */ +new bool:PlayerStatus[MAXPLAYERS + 1]; + +/* Disable of addban and unban */ +new CommandDisable; +new bool:backupConfig = true; +new bool:enableAdmins = true; + +/* Require a lastvisited from SB site */ +new bool:requireSiteLogin = false; + +/* Log Stuff */ +new String:logFile[256]; + +/* Own Chat Reason */ +new g_ownReasons[MAXPLAYERS + 1] = { false, ... }; + +new Float:RetryTime = 15.0; +new ProcessQueueTime = 5; +new bool:LateLoaded; +new bool:AutoAdd; +new bool:g_bConnecting = false; + +new serverID = -1; + +public Plugin:myinfo = +{ + name = "SourceBans++", + author = "SourceBans Development Team, Sarabveer(VEER™)", + description = "Advanced ban management for the Source engine", + version = SBR_VERSION, + url = "https://sarabveer.github.io/SourceBans-Fork/" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +#else +public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) +#endif +{ + RegPluginLibrary("sourcebans"); + CreateNative("SBBanPlayer", Native_SBBanPlayer); + LateLoaded = late; + + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; + #else + return true; + #endif +} + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("plugin.basecommands"); + LoadTranslations("sourcebans.phrases"); + LoadTranslations("basebans.phrases"); + loadAdmins = loadGroups = loadOverrides = false; + + CvarHostIp = FindConVar("hostip"); + CvarPort = FindConVar("hostport"); + CreateConVar("sb_version", SB_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY); + CreateConVar("sbr_version", SBR_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY); + RegServerCmd("sm_rehash", sm_rehash, "Reload SQL admins"); + RegAdminCmd("sm_ban", CommandBan, ADMFLAG_BAN, "sm_ban <#userid|name> [reason]", "sourcebans"); + RegAdminCmd("sm_banip", CommandBanIp, ADMFLAG_BAN, "sm_banip