#pragma semicolon 1 #pragma newdecls required #include #include #include #include #undef REQUIRE_PLUGIN #include #define REQUIRE_PLUGIN #include ConVar g_CVar_sv_enablebunnyhopping; ConVar g_CVar_zr_disablebunnyhopping; enum { LIMITED_NONE = 0, LIMITED_GENERAL = 1, LIMITED_PLUGIN = 2, // Temp LIMITED_ZOMBIE = 4 } bool g_bEnabled = false; bool g_bZombieEnabled = false; bool g_bInOnPlayerRunCmd = false; int g_ClientLimited[MAXPLAYERS + 1] = {LIMITED_NONE, ...}; int g_ActiveLimitedFlags = LIMITED_GENERAL | LIMITED_PLUGIN; StringMap g_ClientLimitedCache; Handle g_hCookie_BlockBhop; public Plugin myinfo = { name = "Selective Bunnyhop", author = "BotoX", description = "Disables bunnyhop on certain players/groups", version = "0.1" } public void OnPluginStart() { LoadTranslations("common.phrases"); g_CVar_sv_enablebunnyhopping = FindConVar("sv_enablebunnyhopping"); g_CVar_sv_enablebunnyhopping.Flags &= ~FCVAR_REPLICATED; g_CVar_sv_enablebunnyhopping.AddChangeHook(OnConVarChanged); g_bEnabled = g_CVar_sv_enablebunnyhopping.BoolValue; g_CVar_zr_disablebunnyhopping = CreateConVar("zr_disablebunnyhopping", "0", "Disable bhop for zombies.", FCVAR_NOTIFY); g_CVar_zr_disablebunnyhopping.AddChangeHook(OnConVarChanged); g_bZombieEnabled = g_CVar_zr_disablebunnyhopping.BoolValue; g_ClientLimitedCache = new StringMap(); g_hCookie_BlockBhop = RegClientCookie("blocking_bhop_cookie", "", CookieAccess_Private); HookEvent("round_start", Event_RoundStart, EventHookMode_PostNoCopy); RegAdminCmd("sm_bhop", Command_Bhop, ADMFLAG_GENERIC, "sm_bhop <#userid|name> <0|1>"); RegConsoleCmd("sm_bhopstatus", Command_Status, "sm_bhopstatus [#userid|name]"); /* Late load */ for(int i = 1; i <= MaxClients; i++) { if(!IsClientInGame(i) || IsFakeClient(i)) return; if(ZR_IsClientZombie(i)) AddLimitedFlag(i, LIMITED_ZOMBIE); if(AreClientCookiesCached(i)) OnClientCookiesCached(i); } UpdateLimitedFlags(); UpdateClients(); } public void OnClientCookiesCached(int client) { char sBuffer[16]; GetClientCookie(client, g_hCookie_BlockBhop, sBuffer, sizeof(sBuffer)); if (StrEqual(sBuffer, "1", false)) { AddLimitedFlag(client, LIMITED_GENERAL); g_ClientLimited[client] = 1; } } public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) { CreateNative("LimitBhop", Native_LimitBhop); CreateNative("IsBhopLimited", Native_IsBhopLimited); RegPluginLibrary("SelectiveBhop"); return APLRes_Success; } public void OnPluginEnd() { g_CVar_sv_enablebunnyhopping.BoolValue = g_bEnabled; g_CVar_sv_enablebunnyhopping.Flags |= FCVAR_REPLICATED|FCVAR_NOTIFY; for(int i = 1; i <= MaxClients; i++) { if(IsClientInGame(i) && !IsFakeClient(i)) { if(g_CVar_sv_enablebunnyhopping.BoolValue) g_CVar_sv_enablebunnyhopping.ReplicateToClient(i, "1"); else g_CVar_sv_enablebunnyhopping.ReplicateToClient(i, "0"); } } } public void OnMapEnd() { g_ClientLimitedCache.Clear(); } public void OnClientPutInServer(int client) { TransmitConVar(client); } public void OnClientDisconnect(int client) { // Remove temp int LimitedFlag = g_ClientLimited[client] & ~(LIMITED_ZOMBIE); if(LimitedFlag != LIMITED_NONE) { char sSteamID[64]; if(GetClientAuthId(client, AuthId_Engine, sSteamID, sizeof(sSteamID))) g_ClientLimitedCache.SetValue(sSteamID, LimitedFlag, true); } g_ClientLimited[client] = LIMITED_NONE; } public void OnClientPostAdminCheck(int client) { char sSteamID[64]; if(GetClientAuthId(client, AuthId_Engine, sSteamID, sizeof(sSteamID))) { int LimitedFlag; if(g_ClientLimitedCache.GetValue(sSteamID, LimitedFlag)) { AddLimitedFlag(client, LimitedFlag); g_ClientLimitedCache.Remove(sSteamID); } } } public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) { if(convar == g_CVar_sv_enablebunnyhopping) { if(g_bInOnPlayerRunCmd) return; g_bEnabled = convar.BoolValue; UpdateClients(); } else if(convar == g_CVar_zr_disablebunnyhopping) { g_bZombieEnabled = convar.BoolValue; UpdateLimitedFlags(); } } public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon, int &subtype, int &cmdnum, int &tickcount, int &seed, int mouse[2]) { if(!g_bEnabled) return Plugin_Continue; bool bEnableBunnyhopping = !(g_ClientLimited[client] & g_ActiveLimitedFlags); if(bEnableBunnyhopping == g_CVar_sv_enablebunnyhopping.BoolValue) return Plugin_Continue; if(!g_bInOnPlayerRunCmd) { g_CVar_sv_enablebunnyhopping.Flags &= ~FCVAR_NOTIFY; g_bInOnPlayerRunCmd = true; } g_CVar_sv_enablebunnyhopping.BoolValue = bEnableBunnyhopping; return Plugin_Continue; } public void OnRunThinkFunctionsPost(bool simulating) { if(g_bInOnPlayerRunCmd) { g_CVar_sv_enablebunnyhopping.BoolValue = g_bEnabled; g_CVar_sv_enablebunnyhopping.Flags |= FCVAR_NOTIFY; g_bInOnPlayerRunCmd = false; } } public void ZR_OnClientInfected(int client, int attacker, bool motherInfect, bool respawnOverride, bool respawn) { AddLimitedFlag(client, LIMITED_ZOMBIE); } public void ZR_OnClientHumanPost(int client, bool respawn, bool protect) { RemoveLimitedFlag(client, LIMITED_ZOMBIE); } public void ZR_OnClientRespawned(int client, ZR_RespawnCondition condition) { if(condition == ZR_Respawn_Human) RemoveLimitedFlag(client, LIMITED_ZOMBIE); else AddLimitedFlag(client, LIMITED_ZOMBIE); } public void Event_RoundStart(Event event, const char[] name, bool dontBroadcast) { RemoveLimitedFlag(-1, LIMITED_ZOMBIE); } void UpdateLimitedFlags() { int Flags = LIMITED_GENERAL; if(g_bZombieEnabled) Flags |= LIMITED_ZOMBIE; if(g_ActiveLimitedFlags != Flags) { g_ActiveLimitedFlags = Flags; UpdateClients(); } g_ActiveLimitedFlags = Flags; } stock void AddLimitedFlag(int client, int Flag) { if(client == -1) { for(int i = 1; i <= MaxClients; i++) _AddLimitedFlag(i, Flag); } else _AddLimitedFlag(client, Flag); } stock void _AddLimitedFlag(int client, int Flag) { bool bWasLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); g_ClientLimited[client] |= Flag; bool bIsLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); if(bIsLimited != bWasLimited) TransmitConVar(client); } stock void RemoveLimitedFlag(int client, int Flag) { if(client == -1) { for(int i = 1; i <= MaxClients; i++) _RemoveLimitedFlag(i, Flag); } else _RemoveLimitedFlag(client, Flag); } stock void _RemoveLimitedFlag(int client, int Flag) { bool bWasLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); g_ClientLimited[client] &= ~Flag; bool bIsLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); if(bIsLimited != bWasLimited) TransmitConVar(client); } stock void UpdateClients() { for(int i = 1; i <= MaxClients; i++) { if(IsClientInGame(i)) TransmitConVar(i); } } stock void TransmitConVar(int client) { if(!IsClientInGame(client) || IsFakeClient(client)) return; bool bIsLimited = view_as(g_ClientLimited[client] & g_ActiveLimitedFlags); if(g_bEnabled && !bIsLimited) g_CVar_sv_enablebunnyhopping.ReplicateToClient(client, "1"); else g_CVar_sv_enablebunnyhopping.ReplicateToClient(client, "0"); } public Action Command_Bhop(int client, int argc) { if(argc < 2) { ReplyToCommand(client, "[SM] Usage: sm_bhop <#userid|name> <0|1>"); return Plugin_Handled; } char sArg[64]; char sArg2[2]; char sTargetName[MAX_TARGET_LENGTH]; int iTargets[MAXPLAYERS]; int iTargetCount; bool bIsML; bool bValue; GetCmdArg(1, sArg, sizeof(sArg)); GetCmdArg(2, sArg2, sizeof(sArg2)); bValue = sArg2[0] == '1' ? true : false; if((iTargetCount = ProcessTargetString(sArg, client, iTargets, MAXPLAYERS, COMMAND_FILTER_NO_MULTI, sTargetName, sizeof(sTargetName), bIsML)) <= 0) { ReplyToTargetError(client, iTargetCount); return Plugin_Handled; } char sBuffer[16]; for(int i = 0; i < iTargetCount; i++) { if(bValue) { RemoveLimitedFlag(iTargets[i], LIMITED_GENERAL | LIMITED_PLUGIN); Format(sBuffer, sizeof(sBuffer), "%s", "0"); SetClientCookie(iTargets[i], g_hCookie_BlockBhop, sBuffer); } else { AddLimitedFlag(iTargets[i], LIMITED_GENERAL); Format(sBuffer, sizeof(sBuffer), "%s", "1"); SetClientCookie(iTargets[i], g_hCookie_BlockBhop, sBuffer); } } ShowActivity2(client, "\x01[SM] \x04", "\x01\x04%s\x01 bunnyhop on target \x04%s", bValue ? "Un-limited" : "Limited", sTargetName); if(iTargetCount > 1) LogAction(client, -1, "\"%L\" %s bunnyhop on target \"%s\"", client, bValue ? "Un-limited" : "Limited", sTargetName); else LogAction(client, iTargets[0], "\"%L\" %s bunnyhop on target \"%L\"", client, bValue ? "Un-limited" : "Limited", iTargets[0]); return Plugin_Handled; } public Action Command_Status(int client, int argc) { if (argc && CheckCommandAccess(client, "", ADMFLAG_BAN, true)) { char sArgument[64]; GetCmdArg(1, sArgument, sizeof(sArgument)); int target = -1; if((target = FindTarget(client, sArgument, true, false)) == -1) return Plugin_Handled; bool bLimited = view_as(g_ClientLimited[target] & (LIMITED_GENERAL | LIMITED_PLUGIN)); if(bLimited) { ReplyToCommand(client, "[SM] %N their bhop is currently: limited", target); return Plugin_Handled; } else { ReplyToCommand(client, "[SM] %N their bhop is currently: unlimited", target); return Plugin_Handled; } } else { bool bLimited = view_as(g_ClientLimited[client] & (LIMITED_GENERAL | LIMITED_PLUGIN)); if(bLimited) { ReplyToCommand(client, "[SM] your bhop is currently: limited"); return Plugin_Handled; } else { ReplyToCommand(client, "[SM] your bhop is currently: unlimited"); return Plugin_Handled; } } } public int Native_LimitBhop(Handle plugin, int numParams) { int client = GetNativeCell(1); bool bLimited = view_as(GetNativeCell(2)); if(client > MaxClients || client <= 0) { ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); return -1; } if(!IsClientInGame(client)) { ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); return -1; } if(bLimited) AddLimitedFlag(client, LIMITED_PLUGIN); else RemoveLimitedFlag(client, LIMITED_PLUGIN); return 0; } public int Native_IsBhopLimited(Handle plugin, int numParams) { int client = GetNativeCell(1); if(client > MaxClients || client <= 0) { ThrowNativeError(SP_ERROR_NATIVE, "Client is not valid."); return -1; } if(!IsClientInGame(client)) { ThrowNativeError(SP_ERROR_NATIVE, "Client is not in-game."); return -1; } int LimitedFlag = g_ClientLimited[client] & LIMITED_PLUGIN; return LimitedFlag != LIMITED_NONE; }