Files
sm-plugins/Couch/scripting/CouchBan.sp
T
2026-09-29 02:24:47 +01:00

247 lines
6.1 KiB
SourcePawn

#include <sourcemod>
#include <Couch>
#pragma semicolon 1
#pragma newdecls required
#define PREFIX "\x01[SM] \x04"
// So commands fail with a message instead of an unbound-native error while the core is unloaded.
bool g_bCoreLoaded;
// Account ids banned this map. This is the ban itself, so leaving and rejoining does not clear it.
ArrayList g_alBannedAccounts;
// Slot mirror. Also carries bans of unauthenticated clients (account id 0), which last the session only.
bool g_bBanned[MAXPLAYERS + 1];
public Plugin myinfo =
{
name = "Couch Ban",
author = "marisakirisame.net",
description = "Bans players from the ZE couch until the end of the map",
version = "2.0",
url = "https://marisakirisame.net"
};
// ---------------------------------------------------------------------------------------------
// Plugin lifecycle
// ---------------------------------------------------------------------------------------------
public void OnPluginStart()
{
LoadTranslations("common.phrases"); // FindTarget's replies.
g_alBannedAccounts = new ArrayList();
RegAdminCmd("sm_couchban", Command_CouchBan, ADMFLAG_BAN, "sm_couchban <#userid|name> - bans a player from the couch until the end of the map");
RegAdminCmd("sm_cban", Command_CouchBan, ADMFLAG_BAN, "Alias of sm_couchban");
RegAdminCmd("sm_couchunban", Command_CouchUnban, ADMFLAG_BAN, "sm_couchunban <#userid|name> - lifts a couch ban");
RegAdminCmd("sm_cunban", Command_CouchUnban, ADMFLAG_BAN, "Alias of sm_couchunban");
}
public void OnAllPluginsLoaded()
{
g_bCoreLoaded = LibraryExists("Couch");
// After a reload of this plugin the core still knows who is blocked.
if (!g_bCoreLoaded)
return;
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || !Couch_IsClientBlocked(i))
continue;
g_bBanned[i] = true;
RememberAccount(i);
}
}
public void OnLibraryAdded(const char[] name)
{
if (!StrEqual(name, "Couch"))
return;
g_bCoreLoaded = true;
// A core reload wiped its flags. A frame later so its natives are bound.
RequestFrame(Frame_ReapplyBans);
}
public void OnLibraryRemoved(const char[] name)
{
if (StrEqual(name, "Couch"))
g_bCoreLoaded = false;
}
void Frame_ReapplyBans(any data)
{
if (!g_bCoreLoaded)
return;
for (int i = 1; i <= MaxClients; i++)
{
if (g_bBanned[i] && IsClientInGame(i))
Couch_SetClientBlocked(i, true);
}
}
public void OnMapStart()
{
for (int i = 1; i <= MAXPLAYERS; i++)
g_bBanned[i] = false;
}
public void OnMapEnd()
{
g_alBannedAccounts.Clear();
}
// The account id stays on the list so a rejoin picks the ban back up.
public void OnClientDisconnect(int client)
{
g_bBanned[client] = false;
}
// Either "in game" or "authorized" can come last, so both are hooked.
public void OnClientPutInServer(int client)
{
TryReapplyBan(client);
}
public void OnClientAuthorized(int client, const char[] auth)
{
TryReapplyBan(client);
}
void TryReapplyBan(int client)
{
if (g_bBanned[client] || !IsClientInGame(client))
return;
int account = GetSteamAccountID(client);
if (account == 0 || g_alBannedAccounts.FindValue(account) == -1)
return;
g_bBanned[client] = true;
if (g_bCoreLoaded)
Couch_SetClientBlocked(client, true);
PrintToChat(client, PREFIX ... "You are couch-banned\x01 until the end of the map.");
}
// ---------------------------------------------------------------------------------------------
// Commands
// ---------------------------------------------------------------------------------------------
public Action Command_CouchBan(int client, int args)
{
if (!RequireCore(client))
return Plugin_Handled;
if (args < 1)
{
ReplyToCommand(client, "[SM] Usage: sm_couchban <#userid|name>");
return Plugin_Handled;
}
char sArg[MAX_TARGET_LENGTH];
GetCmdArg(1, sArg, sizeof(sArg));
int target = FindTarget(client, sArg, true, true);
if (target == -1)
return Plugin_Handled;
if (g_bBanned[target])
{
ReplyToCommand(client, "[SM] %N is already couch-banned.", target);
return Plugin_Handled;
}
DoBan(client, target);
return Plugin_Handled;
}
public Action Command_CouchUnban(int client, int args)
{
if (!RequireCore(client))
return Plugin_Handled;
if (args < 1)
{
ReplyToCommand(client, "[SM] Usage: sm_couchunban <#userid|name>");
return Plugin_Handled;
}
char sArg[MAX_TARGET_LENGTH];
GetCmdArg(1, sArg, sizeof(sArg));
int target = FindTarget(client, sArg, true, true);
if (target == -1)
return Plugin_Handled;
if (!g_bBanned[target])
{
ReplyToCommand(client, "[SM] %N is not couch-banned.", target);
return Plugin_Handled;
}
DoUnban(client, target);
return Plugin_Handled;
}
bool RequireCore(int client)
{
if (g_bCoreLoaded)
return true;
ReplyToCommand(client, "[SM] The Couch core plugin is not loaded.");
return false;
}
// ---------------------------------------------------------------------------------------------
// Actions
// ---------------------------------------------------------------------------------------------
void DoBan(int client, int target)
{
g_bBanned[target] = true;
RememberAccount(target);
Couch_SetClientBlocked(target, true);
// Announced to everyone so players know why the couch stopped reacting to someone.
PrintToChatAll("\x01[SM] \x04%N\x01 couch-banned \x04%N\x01 until the end of the map.", client, target);
LogAction(client, target, "\"%L\" couch-banned \"%L\" until the end of the map.", client, target);
}
void DoUnban(int client, int target)
{
g_bBanned[target] = false;
ForgetAccount(target);
Couch_SetClientBlocked(target, false);
PrintToChatAll("\x01[SM] \x04%N\x01 removed couch-ban from \x04%N\x01.", client, target);
LogAction(client, target, "\"%L\" removed couch-ban from \"%L\".", client, target);
}
// An unauthenticated client has no account id, so their ban ends with the session.
void RememberAccount(int client)
{
int account = GetSteamAccountID(client);
if (account != 0 && g_alBannedAccounts.FindValue(account) == -1)
g_alBannedAccounts.Push(account);
}
void ForgetAccount(int client)
{
int account = GetSteamAccountID(client);
if (account == 0)
return;
int index = g_alBannedAccounts.FindValue(account);
if (index != -1)
g_alBannedAccounts.Erase(index);
}