diff --git a/scripting/L4D2FFKickProtection.sp b/scripting/L4D2FFKickProtection.sp index ff0b3c2..aef8057 100644 --- a/scripting/L4D2FFKickProtection.sp +++ b/scripting/L4D2FFKickProtection.sp @@ -71,20 +71,21 @@ public Action VoteStart(int client, const char[] command, int argc) { if(strlen(option) > 1) { //empty userid/console can't call votes int target = GetClientOfUserId(StringToInt(option)); - if(target <= 0 || target >= MaxClients || !IsClientConnected(target)) return Plugin_Continue; //invalid, pass it through - if(client <= 0 || client >= MaxClients || !IsClientConnected(client)) return Plugin_Continue; //invalid, pass it through - AdminId callerAdmin = GetUserAdmin(client); + if(target <= 0 || target >= MaxClients || !IsClientInGame(target)) return Plugin_Continue; //invalid, pass it through AdminId targetAdmin = GetUserAdmin(target); + bool isCallerAdmin = GetUserFlagBits(client) != 0; + bool isTargetAdmin = GetUserFlagBits(target) != 0; + PrintToServer("Caller Admin: %b | Target admin: %b", isCallerAdmin, isTargetAdmin); //Only run if vote is against an admin - if(targetAdmin != INVALID_ADMIN_ID) { + if(isTargetAdmin) { for(int i = 1; i <= MaxClients; i++) { - if(target != i && IsClientConnected(i) && IsClientInGame(i) && GetUserAdmin(i) != INVALID_ADMIN_ID) { + if(target != i && IsClientInGame(i) && GetUserAdmin(i) != INVALID_ADMIN_ID) { PrintToChat(i, "%N attempted to vote-kick admin %N", client, target); } } // Kick player if they are not an admin and just recently joined. // Else, just tell the target - if(callerAdmin == INVALID_ADMIN_ID && GetTime() - iJoinTime[client] <= ANTI_ADMIN_KICK_MIN_TIME) { + if(!isCallerAdmin && GetTime() - iJoinTime[client] <= ANTI_ADMIN_KICK_MIN_TIME) { if(GetClientTeam(target) >= 2) { KickClient(client, "No."); PrintToChat(target, "%N has attempted to vote kick you and was kicked.", client); @@ -100,7 +101,7 @@ public Action VoteStart(int client, const char[] command, int argc) { PrintToServer("debug: admin immunity is %d. username: %s", targetAdmin.ImmunityLevel, option); PrintToServer("ADMIN VOTE KICK BLOCKED | Target=%N | Caller=%N", target, client); return Plugin_Handled; - } else if(callerAdmin != INVALID_ADMIN_ID) { + } else if(isCallerAdmin) { PrintToServer("Vote kick by admin, instantly passing"); for(int i = 1; i <= MaxClients; i++) { if(IsClientConnected(i) && !IsFakeClient(i) && GetClientTeam(i) == GetClientTeam(target)) { diff --git a/scripting/l4d2_perms.sp b/scripting/l4d2_perms.sp new file mode 100644 index 0000000..8c081d8 --- /dev/null +++ b/scripting/l4d2_perms.sp @@ -0,0 +1,187 @@ +#pragma semicolon 1 +#pragma newdecls required + +//#define DEBUG + +#define PLUGIN_VERSION "1.0" + +#include +#include +#include +#include +#include + +#define RESERVE_LEVELS 4 +char ReserveLevels[RESERVE_LEVELS][] = { + "Public", "Watch", "Admin Only", "Private" +}; + +StringMap g_steamIds; + + +ConVar cv_cheatsMode; +ConVar cv_reserveMode; +ConVar cv_reserveMessage; char g_reserveMessage[64]; + +bool g_ignoreModeChange; + +public Plugin myinfo = +{ + name = "L4D2 Perms", + author = "jackzmc", + description = "", + version = PLUGIN_VERSION, + url = "https://github.com/Jackzmc/sourcemod-plugins" +}; + +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { + return APLRes_Success; +} + +public void OnPluginStart() { + EngineVersion g_Game = GetEngineVersion(); + if(g_Game != Engine_Left4Dead && g_Game != Engine_Left4Dead2) { + SetFailState("This plugin is for L4D/L4D2 only."); + } + + g_steamIds = new StringMap(); + + cv_reserveMessage = CreateConVar("sm_perms_reserve_msg", "Sorry, server is reserved.", "The message sent to users when server is reserved."); + cv_reserveMessage.AddChangeHook(OnReserveMsgChanged); + + cv_reserveMode = CreateConVar("sm_perms_reserve_mode", "0", "The current reservation mode. \n 0 = None (public)\n 1 = Watch\n 2 = Admin Only\n 3 = Private", FCVAR_DONTRECORD, true, 0.0, true, float(RESERVE_LEVELS)); + cv_reserveMode.AddChangeHook(OnReserveModeChanged); + + cv_cheatsMode = CreateConVar("sm_perms_cheats_mode", "0", "When cheats are turned on, which reservation should be set?.\n 0 = None (public)\n 1 = Watch\n 2 = Admin Only\n 3 = Private", FCVAR_NONE, true, 0.0, true, float(RESERVE_LEVELS - 1)); + + ConVar sv_cheats = FindConVar("sv_cheats"); + sv_cheats.AddChangeHook(OnCheatsChanged); + + HookEvent("player_disconnect", Event_PlayerDisconnect); + + AutoExecConfig(true, "l4d2_perms"); + + + RegAdminCmd("sm_perm", Command_SetServerPermissions, ADMFLAG_KICK, "Sets the server's permissions."); + RegAdminCmd("sm_perms", Command_SetServerPermissions, ADMFLAG_KICK, "Sets the server's permissions."); +} + +void OnCheatsChanged(ConVar cvar, const char[] oldValue, const char[] newValue) { + if(cvar.IntValue > 0) { + ReserveMode mode = GetReserveMode(); + if(mode == Reserve_None || mode == Reserve_Watch) + SetReserveMode(view_as(cv_cheatsMode.IntValue), "cheats activated"); + } +} + +void OnReserveModeChanged(ConVar cvar, const char[] oldValue, const char[] newValue) { + if(g_ignoreModeChange) { + g_ignoreModeChange = false; + return; + } + if(cvar.IntValue >= 0 && cvar.IntValue < RESERVE_LEVELS) { + PrintChatToAdmins("Server access changed to %s", ReserveLevels[cvar.IntValue]); + } else { + // cvar.SetString(oldValue); + } +} + +void OnReserveMsgChanged(ConVar cvar, const char[] oldValue, const char[] newValue) { + cvar.GetString(g_reserveMessage, sizeof(g_reserveMessage)); +} + +public void OnClientConnected(int client) { + if(!IsFakeClient(client) && GetReserveMode() == Reserve_Watch) { + PrintChatToAdmins("%N is connecting", client); + } +} + +public void Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast) { + int client = GetClientOfUserId(event.GetInt("userid")); + if(client > 0) { + if(GetClientCount(false) == 0) { + // Clear when last player disconnected + SetReserveMode(Reserve_None); + } + } +} + +public void OnClientPostAdminCheck(int client) { + if(!IsFakeClient(client)) { + if(GetReserveMode() == Reserve_AdminOnly && GetUserAdmin(client) == INVALID_ADMIN_ID) { + char auth[32]; + GetClientAuthId(client, AuthId_Steam2, auth, sizeof(auth)); + if(!g_steamIds.ContainsKey(auth)) { + KickClient(client, "Sorry, server is reserved"); + return; + } + } + } +} + +public void OnClientAuthorized(int client, const char[] auth) { + if(IsFakeClient(client)) return; + if(GetReserveMode() == Reserve_Private) { + if(!g_steamIds.ContainsKey(auth)) { + KickClient(client, "Sorry, server is reserved"); + } + } + // Don't insert id here if admin only, let admin check do that + if(GetReserveMode() != Reserve_AdminOnly) { + g_steamIds.SetValue(auth, client); + } +} + + +Action Command_SetServerPermissions(int client, int args) { + if(args > 0) { + char arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + if(StrEqual(arg1, "public", false)) { + SetReserveMode(Reserve_None); + } else if(StrContains(arg1, "noti", false) > -1 || StrContains(arg1, "watch", false) > -1) { + SetReserveMode(Reserve_Watch); + } else if(StrContains(arg1, "admin", false) > -1) { + SetReserveMode(Reserve_AdminOnly); + for(int i = 1; i <= MaxClients; i++) { + if(IsClientInGame(i) && !IsFakeClient(i)) { + GetClientAuthId(i, AuthId_Steam2, arg1, sizeof(arg1)); + g_steamIds.SetValue(arg1, i); + } + } + } else if(StrEqual(arg1, "private", false)) { + for(int i = 1; i <= MaxClients; i++) { + if(IsClientInGame(i) && !IsFakeClient(i)) { + GetClientAuthId(i, AuthId_Steam2, arg1, sizeof(arg1)); + g_steamIds.SetValue(arg1, i); + } + } + SetReserveMode(Reserve_Private); + } else { + ReplyToCommand(client, "Usage: sm_reserve [public/notify/admin/private] or no arguments to view current reservation."); + return Plugin_Handled; + } + } else { + ReplyToCommand(client, "Server access level is currently %s", ReserveLevels[GetReserveMode()]); + } + return Plugin_Handled; +} + +ReserveMode GetReserveMode() { + return view_as(cv_reserveMode.IntValue); +} + +void SetReserveMode(ReserveMode mode, const char[] reason = "") { + ReserveMode curMode = GetReserveMode(); + // Clear allowed users when undoing private + if(curMode != mode && curMode == Reserve_Private) { + g_steamIds.Clear(); + } + + if(reason[0] != '\0') { + // Print custom message if a reason is passed: + g_ignoreModeChange = true; + PrintChatToAdmins("Server access changed to %s (%s)", ReserveLevels[mode], reason); + } + cv_reserveMode.IntValue = view_as(mode); +} \ No newline at end of file