#pragma semicolon 1
#pragma newdecls required
#include <sourcemod>
#define MAX_PACKETS_PER_SECOND 50 // 每秒最大语音包数量
#define BAN_DURATION 1440 // 封禁时长(分钟),1440=24小时
ConVar g_cvMaxPackets;
bool g_bEnabled = true;
int g_iPacketCount[MAXPLAYERS + 1];
Handle g_hResetTimer;
public Plugin myinfo =
{
name = "Anti voice dos",
author = "Security Expert",
description = "Protects against voice packet flood attacks",
version = "1.3",
url = ""
}
public void OnPluginStart()
{
g_cvMaxPackets = CreateConVar("sm_voice_maxpackets", "50", "Maximum voice packets per second before ban", _, true, 10.0, true, 100.0);
RegAdminCmd("sm_voiceprotect", Command_Toggle, ADMFLAG_ROOT, "Toggle voice flood protection");
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
g_hResetTimer = CreateTimer(60.0, Timer_ResetCounters, _, TIMER_REPEAT);
}
public Action Command_Toggle(int client, int args)
{
g_bEnabled = !g_bEnabled;
ReplyToCommand(client, "Voice flood protection %s", g_bEnabled ? "ENABLED" : "DISABLED");
return Plugin_Handled;
}
public void OnClientPutInServer(int client)
{
g_iPacketCount[client] = 0;
}
public Action OnClientPreAdminCheck(int client)
{
if(!g_bEnabled || IsFakeClient(client))
return Plugin_Continue;
// 检测异常发包模式
if(DetectFloodPattern(client))
{
char reason[256], steamId[32];
GetClientAuthId(client, AuthId_Steam2, steamId, sizeof(steamId));
Format(reason, sizeof(reason), "Voice flood attack detected (SteamID: %s)", steamId);
LogToFile("voice_flood.log", "Banned %L for voice flooding", client);
BanClient(client, BAN_DURATION, BANFLAG_AUTO, reason, reason, "voice_flood_protection");
return Plugin_Stop;
}
return Plugin_Continue;
}
bool DetectFloodPattern(int client)
{
int maxPackets = g_cvMaxPackets.IntValue;
if(g_iPacketCount[client] > maxPackets * 3)
{
return true;
}
return false;
}
public void OnClientSpeakingEnd(int client)
{
if(IsClientInGame(client) && !IsFakeClient(client))
{
g_iPacketCount[client]++;
}
}
public Action Event_PlayerDisconnect(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));
if(client > 0) g_iPacketCount[client] = 0;
return Plugin_Continue;
}
public Action Timer_ResetCounters(Handle timer)
{
for(int i = 1; i <= MaxClients; i++)
{
if(IsClientInGame(i))
{
// 只重置一半的计数,保留历史模式
g_iPacketCount[i] = g_iPacketCount[i] / 2;
}
}
return Plugin_Continue;
}