sm-plugins/NameFilter/scripting/NameFilter.sp
2017-02-12 19:00:58 +01:00

336 lines
6.9 KiB
SourcePawn

#include <sdktools>
#include <regex>
#pragma semicolon 1
#pragma newdecls required
Regex g_FilterExpr;
char g_FilterChar[2] = "";
ArrayList g_BannedExprs;
ArrayList g_ReplacementNames;
int g_iBlockNameChangeEvents[MAXPLAYERS + 1] = {0, ...};
public Plugin myinfo =
{
name = "NameFilter",
author = "BotoX",
description = "Filters player names",
version = "1.0"
}
public void OnPluginStart()
{
HookEvent("player_changename", Event_ChangeName, EventHookMode_Pre);
HookUserMessage(GetUserMessageId("SayText2"), UserMessage_SayText2, true);
LoadConfig();
for(int i = 1; i <= MaxClients; i++)
{
if(IsClientConnected(i))
OnClientConnected(i);
}
}
public void OnClientConnected(int client)
{
g_iBlockNameChangeEvents[client] = 0;
if(IsFakeClient(client))
return;
char sName[MAX_NAME_LENGTH];
GetClientName(client, sName, sizeof(sName));
if(FilterName(client, sName))
SetClientName(client, sName);
}
public void OnClientPutInServer(int client)
{
if(IsFakeClient(client))
return;
char sName[MAX_NAME_LENGTH];
GetClientName(client, sName, sizeof(sName));
if(FilterName(client, sName))
{
g_iBlockNameChangeEvents[client] = 2;
SetClientName(client, sName);
}
}
public Action Event_ChangeName(Event event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(event.GetInt("userid"));
if(g_iBlockNameChangeEvents[client])
{
g_iBlockNameChangeEvents[client]--;
SetEventBroadcast(event, true);
return Plugin_Handled;
}
return Plugin_Continue;
}
public Action UserMessage_SayText2(UserMsg msg_id, BfRead msg, const int[] players, int playersNum, bool reliable, bool init)
{
if(!reliable)
return Plugin_Continue;
int client;
char sMessage[32];
char sOldName[MAX_NAME_LENGTH];
char sNewName[MAX_NAME_LENGTH];
if(GetUserMessageType() == UM_Protobuf)
{
PbReadString(msg, "msg_name", sMessage, sizeof(sMessage));
if(!(sMessage[0] == '#' && StrContains(sMessage, "Name_Change")))
return Plugin_Continue;
client = PbReadInt(msg, "ent_idx");
PbReadString(msg, "params", sOldName, sizeof(sOldName), 1);
PbReadString(msg, "params", sNewName, sizeof(sNewName), 2);
}
else
{
client = BfReadByte(msg);
BfReadByte(msg);
BfReadString(msg, sMessage, sizeof(sMessage));
if(!(sMessage[0] == '#' && StrContains(sMessage, "Name_Change")))
return Plugin_Continue;
BfReadString(msg, sOldName, sizeof(sOldName));
BfReadString(msg, sNewName, sizeof(sNewName));
}
if(g_iBlockNameChangeEvents[client])
{
g_iBlockNameChangeEvents[client]--;
return Plugin_Handled;
}
if(FilterName(client, sNewName))
{
if(StrEqual(sOldName, sNewName))
{
g_iBlockNameChangeEvents[client] = 3;
SetClientName(client, sOldName);
return Plugin_Handled;
}
g_iBlockNameChangeEvents[client] = 3;
SetClientName(client, sOldName);
DataPack pack = new DataPack();
pack.WriteCell(client);
pack.WriteString(sNewName);
CreateTimer(0.1, Timer_ChangeName, pack);
return Plugin_Handled;
}
return Plugin_Continue;
}
public Action Timer_ChangeName(Handle timer, any data)
{
DataPack pack = view_as<DataPack>(data);
pack.Reset();
int client = pack.ReadCell();
char sName[MAX_NAME_LENGTH];
pack.ReadString(sName, sizeof(sName));
delete pack;
SetClientName(client, sName);
return Plugin_Stop;
}
void LoadConfig()
{
if(g_FilterExpr != INVALID_HANDLE)
CloseHandle(g_FilterExpr);
if(g_BannedExprs)
{
for(int i = 0; i < g_BannedExprs.Length; i++)
{
Handle hRegex = g_BannedExprs.Get(i);
CloseHandle(hRegex);
}
}
delete g_BannedExprs;
delete g_ReplacementNames;
static char sConfigFile[PLATFORM_MAX_PATH];
BuildPath(Path_SM, sConfigFile, sizeof(sConfigFile), "configs/NameFilter.cfg");
if(!FileExists(sConfigFile))
SetFailState("Could not find config: \"%s\"", sConfigFile);
KeyValues Config = new KeyValues("NameFilter");
if(!Config.ImportFromFile(sConfigFile))
{
delete Config;
SetFailState("ImportFromFile() failed!");
}
Config.GetString("censor", g_FilterChar, 2, "*");
static char sBuffer[256];
Config.GetString("filter", sBuffer, 256);
char sError[256];
RegexError iError;
g_FilterExpr = CompileRegex(sBuffer, PCRE_UTF8, sError, sizeof(sError), iError);
if(iError != REGEX_ERROR_NONE)
{
delete Config;
SetFailState(sError);
}
g_BannedExprs = new ArrayList();
if(Config.JumpToKey("banned"))
{
if(Config.GotoFirstSubKey(false))
{
do
{
Config.GetString(NULL_STRING, sBuffer, sizeof(sBuffer));
Handle hRegex = CompileRegex(sBuffer, PCRE_UTF8, sError, sizeof(sError), iError);
if(iError != REGEX_ERROR_NONE)
LogError("Error parsing banned filter: %s", sError);
else
g_BannedExprs.Push(hRegex);
} while(Config.GotoNextKey(false));
Config.GoBack();
}
Config.GoBack();
}
g_ReplacementNames = new ArrayList(ByteCountToCells(MAX_NAME_LENGTH));
if(Config.JumpToKey("names"))
{
if(Config.GotoFirstSubKey(false))
{
do
{
Config.GetString(NULL_STRING, sBuffer, sizeof(sBuffer));
g_ReplacementNames.PushString(sBuffer);
} while(Config.GotoNextKey(false));
Config.GoBack();
}
Config.GoBack();
}
if(!g_ReplacementNames.Length)
{
LogError("Warning, you didn't specify any replacement names!");
g_ReplacementNames.PushString("BAD_NAME");
}
delete Config;
}
bool FilterName(int client, char[] sName, int Length = MAX_NAME_LENGTH)
{
bool bChanged = false;
RegexError iError;
// SourceMod Regex bug
int Guard;
for(Guard = 0; Guard < 100; Guard++)
{
int Match = MatchRegex(g_FilterExpr, sName, iError);
if(iError != REGEX_ERROR_NONE)
{
LogError("Regex Error: %d", iError);
break;
}
if(Match <= 0)
break;
for(int i = 0; i < Match; i++)
{
char sMatch[MAX_NAME_LENGTH];
if(GetRegexSubString(g_FilterExpr, i, sMatch, sizeof(sMatch)))
{
if(ReplaceStringEx(sName, Length, sMatch, g_FilterChar) != -1)
bChanged = true;
}
}
}
if(Guard == 100)
LogError("SourceMod Regex failed! \"%s\"", sName);
if(g_BannedExprs)
{
for(int i = 0; i < g_BannedExprs.Length; i++)
{
Handle hRegex = g_BannedExprs.Get(i);
int Match = MatchRegex(hRegex, sName, iError);
if(iError != REGEX_ERROR_NONE)
{
LogError("Regex Error: %d", iError);
continue;
}
if(Match <= 0)
continue;
int RandomName = client % g_ReplacementNames.Length;
g_ReplacementNames.GetString(RandomName, sName, Length);
return true;
}
}
if(!bChanged)
bChanged = TerminateNameUTF8(sName);
if(bChanged)
{
TerminateNameUTF8(sName);
if(strlen(sName) < 2)
{
int RandomName = client % g_ReplacementNames.Length;
g_ReplacementNames.GetString(RandomName, sName, Length);
return true;
}
}
return bChanged;
}
// ensures that utf8 names are properly terminated
stock bool TerminateNameUTF8(char[] name)
{
int len = strlen(name);
for(int i = 0; i < len; i++)
{
int bytes = IsCharMB(name[i]);
if(bytes > 1)
{
if(len - i < bytes)
{
name[i] = '\0';
return true;
}
i += bytes - 1;
}
}
return false;
}