diff --git a/_CallAdmin/scripting/CallAdmin.sp b/_CallAdmin/scripting/CallAdmin.sp
new file mode 100644
index 00000000..cfe000ca
--- /dev/null
+++ b/_CallAdmin/scripting/CallAdmin.sp
@@ -0,0 +1,1448 @@
+/**
+ * -----------------------------------------------------
+ * File calladmin.sp
+ * Authors Impact, dordnung
+ * License GPLv3
+ * Web http://gugyclan.eu, https://dordnung.de
+ * -----------------------------------------------------
+ *
+ * CallAdmin
+ * Copyright (C) 2013-2018 Impact, dordnung
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ */
+
+#include
+#include "include/autoexecconfig"
+#include "include/calladmin"
+#include "include/calladmin_stocks"
+
+#undef REQUIRE_PLUGIN
+#include "include/updater"
+#include
+#pragma semicolon 1
+#pragma newdecls required
+
+
+
+// Banreasons
+ArrayList g_hReasonAdt;
+char g_sReasonConfigFile[PLATFORM_MAX_PATH];
+
+
+// Global Stuff
+ConVar g_hServerName;
+char g_sServerName[64];
+
+ConVar g_hVersion;
+
+ConVar g_hHostPort;
+int g_iHostPort;
+
+ConVar g_hHostIP;
+char g_sHostIP[16];
+
+Handle g_hAdvertTimer;
+ConVar g_hAdvertInterval;
+float g_fAdvertInterval;
+
+ConVar g_hPublicMessage;
+bool g_bPublicMessage;
+
+ConVar g_hOwnReason;
+bool g_bOwnReason;
+
+ConVar g_hConfirmCall;
+bool g_bConfirmCall;
+
+ConVar g_hSpamTime;
+int g_iSpamTime;
+
+ConVar g_hReportTime;
+int g_iReportTime;
+
+ConVar g_hAdminAction;
+int g_iAdminAction;
+
+
+
+// Report id used for handling
+int g_iCurrentReportID;
+
+// List of not handled IDs
+ArrayList g_hActiveReports;
+
+
+
+// Log file
+char g_sLogFile[PLATFORM_MAX_PATH];
+
+
+#define ADMIN_ACTION_PASS 0
+#define ADMIN_ACTION_BLOCK_MESSAGE 1
+
+
+int g_iCurrentTrackers;
+
+
+
+// Current target info
+g_iTarget[MAXPLAYERS + 1];
+char g_sTargetReason[MAXPLAYERS + 1][REASON_MAX_LENGTH];
+
+// Is this player writing his own reason?
+bool g_bAwaitingReason[MAXPLAYERS +1];
+
+// When has this user reported the last time
+g_iLastReport[MAXPLAYERS +1];
+
+// When was this user reported the last time?
+g_iLastReported[MAXPLAYERS +1];
+
+// Whether or not a client saw the antispam message
+bool g_bSawMessage[MAXPLAYERS +1];
+
+
+// Cookies
+Handle g_hLastReportCookie;
+Handle g_hLastReportedCookie;
+
+
+// Api
+Handle g_hOnReportPreForward;
+Handle g_hOnReportPostForward;
+Handle g_hOnDrawMenuForward;
+Handle g_hOnDrawOwnReasonForward;
+Handle g_hOnTrackerCountChangedForward;
+Handle g_hOnDrawTargetForward;
+Handle g_hOnAddToAdminCountForward;
+Handle g_hOnServerDataChangedForward;
+Handle g_hOnLogMessageForward;
+Handle g_hOnReportHandledForward;
+
+
+
+
+// Updater
+#define UPDATER_URL "http://plugins.gugyclan.eu/calladmin/calladmin.txt"
+
+
+public Plugin myinfo =
+{
+ name = "CallAdmin",
+ author = "Impact, dordnung",
+ description = "Call an Admin for help",
+ version = CALLADMIN_VERSION,
+ url = "http://gugyclan.eu"
+}
+
+
+
+public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
+{
+ RegPluginLibrary("calladmin");
+
+
+ // Api
+ CreateNative("CallAdmin_GetTrackersCount", Native_GetCurrentTrackers);
+ CreateNative("CallAdmin_RequestTrackersCountRefresh", Native_RequestTrackersCountRefresh);
+ CreateNative("CallAdmin_GetHostName", Native_GetHostName);
+ CreateNative("CallAdmin_GetHostIP", Native_GetHostIP);
+ CreateNative("CallAdmin_GetHostPort", Native_GetHostPort);
+ CreateNative("CallAdmin_ReportClient", Native_ReportClient);
+ CreateNative("CallAdmin_LogMessage", Native_LogMessage);
+ CreateNative("CallAdmin_GetReportID", Native_GetReportID);
+
+
+ return APLRes_Success;
+}
+
+
+
+
+
+public int Native_GetCurrentTrackers(Handle plugin, int numParams)
+{
+ return g_iCurrentTrackers;
+}
+
+
+
+
+public int Native_RequestTrackersCountRefresh(Handle plugin, int numParams)
+{
+ Timer_UpdateTrackersCount(null);
+}
+
+
+
+
+public int Native_GetHostName(Handle plugin, int numParams)
+{
+ int max_size = GetNativeCell(2);
+ SetNativeString(1, g_sServerName, max_size);
+}
+
+
+
+
+public int Native_GetHostIP(Handle plugin, int numParams)
+{
+ int max_size = GetNativeCell(2);
+ SetNativeString(1, g_sHostIP, max_size);
+}
+
+
+
+
+public int Native_GetHostPort(Handle plugin, int numParams)
+{
+ return g_iHostPort;
+}
+
+
+
+
+public int Native_ReportClient(Handle plugin, int numParams)
+{
+ int client;
+ int target;
+ char sReason[REASON_MAX_LENGTH];
+
+ client = GetNativeCell(1);
+ target = GetNativeCell(2);
+ GetNativeString(3, sReason, sizeof(sReason));
+
+
+ // We check for the REPORTER_CONSOLE define here, if this is set we have no valid client and the report comes from server
+ if (!IsClientValid(client) && client != REPORTER_CONSOLE)
+ {
+ return false;
+ }
+
+ if (!IsClientValid(target))
+ {
+ return false;
+ }
+
+ if (!Forward_OnReportPre(client, target, sReason))
+ {
+ return false;
+ }
+
+ g_iCurrentReportID++;
+ g_hActiveReports.Push(g_iCurrentReportID);
+
+ Forward_OnReportPost(client, target, sReason);
+
+ return true;
+}
+
+
+
+
+public int Native_LogMessage(Handle plugin, int numParams)
+{
+ char sPluginName[64];
+ char sMessage[256];
+ GetPluginInfo(plugin, PlInfo_Name, sPluginName, sizeof(sPluginName));
+
+ FormatNativeString(0, 1, 2, sizeof(sMessage), _, sMessage);
+
+ LogToFileEx(g_sLogFile, "[%s] %s", sPluginName, sMessage);
+
+ Forward_OnLogMessage(plugin, sMessage);
+}
+
+
+
+
+public int Native_GetReportID(Handle plugin, int numParams)
+{
+ return g_iCurrentReportID;
+}
+
+
+
+
+public void OnConfigsExecuted()
+{
+ g_iHostPort = g_hHostPort.IntValue;
+ UpdateHostIp();
+
+ g_hServerName.GetString(g_sServerName, sizeof(g_sServerName));
+ g_bPublicMessage = g_hPublicMessage.BoolValue;
+ g_bOwnReason = g_hOwnReason.BoolValue;
+ g_bConfirmCall = g_hConfirmCall.BoolValue;
+ g_iSpamTime = g_hSpamTime.IntValue;
+ g_iReportTime = g_hReportTime.IntValue;
+ g_iAdminAction = g_hAdminAction.IntValue;
+
+ g_fAdvertInterval = g_hAdvertInterval.FloatValue;
+
+ delete g_hAdvertTimer;
+
+ if (g_fAdvertInterval != 0.0)
+ {
+ g_hAdvertTimer = CreateTimer(g_fAdvertInterval, Timer_Advert, _, TIMER_REPEAT);
+ }
+}
+
+
+
+
+public void OnPluginStart()
+{
+ BuildPath(Path_SM, g_sLogFile, sizeof(g_sLogFile), "logs/calladmin.log");
+
+ g_hHostPort = FindConVar("hostport");
+ g_hHostIP = FindConVar("hostip");
+ g_hServerName = FindConVar("hostname");
+
+
+ if (g_hHostPort == null)
+ {
+ CallAdmin_LogMessage("Couldn't find cvar 'hostport'");
+ SetFailState("Couldn't find cvar 'hostport'");
+ }
+
+ if (g_hHostIP == null)
+ {
+ CallAdmin_LogMessage("Couldn't find cvar 'hostip'");
+ SetFailState("Couldn't find cvar 'hostip'");
+ }
+
+ if (g_hServerName == null)
+ {
+ CallAdmin_LogMessage("Couldn't find cvar 'hostname'");
+ SetFailState("Couldn't find cvar 'hostname'");
+ }
+
+
+ RegConsoleCmd("sm_call", Command_Call);
+ RegConsoleCmd("sm_calladmin", Command_Call);
+
+ RegConsoleCmd("sm_call_handle", Command_HandleCall);
+ RegConsoleCmd("sm_calladmin_handle", Command_HandleCall);
+
+ RegConsoleCmd("sm_calladmin_reload", Command_Reload);
+
+
+ AutoExecConfig_SetFile("plugin.calladmin");
+
+ g_hVersion = AutoExecConfig_CreateConVar("sm_calladmin_version", CALLADMIN_VERSION, "Plugin version", FCVAR_NOTIFY|FCVAR_DONTRECORD);
+ g_hAdvertInterval = AutoExecConfig_CreateConVar("sm_calladmin_advert_interval", "60.0", "Interval to advert the use of calladmin, 0.0 deactivates the feature", FCVAR_NONE, true, 0.0, true, 1800.0);
+ g_hPublicMessage = AutoExecConfig_CreateConVar("sm_calladmin_public_message", "0", "Whether or not a report should be notified to all players or only the reporter.", FCVAR_NONE, true, 0.0, true, 1.0);
+ g_hOwnReason = AutoExecConfig_CreateConVar("sm_calladmin_own_reason", "1", "Whether or not a client can submit their own reason.", FCVAR_NONE, true, 0.0, true, 1.0);
+ g_hConfirmCall = AutoExecConfig_CreateConVar("sm_calladmin_confirm_call", "1", "Whether or not a call must be confirmed by the client", FCVAR_NONE, true, 0.0, true, 1.0);
+ g_hSpamTime = AutoExecConfig_CreateConVar("sm_calladmin_spamtime", "25", "An user must wait this many seconds after a report before he can issue a new one", FCVAR_NONE, true, 0.0);
+ g_hReportTime = AutoExecConfig_CreateConVar("sm_calladmin_reporttime", "300", "An user cannot be reported again for this many seconds", FCVAR_NONE, true, 0.0);
+ g_hAdminAction = AutoExecConfig_CreateConVar("sm_calladmin_admin_action", "1", "What happens when admins are in-game on report: 0 - Do nothing, let the report pass, 1 - Block the report and notify the caller and admins in-game about it", FCVAR_NONE, true, 0.0, true, 1.0);
+
+
+
+ AutoExecConfig(true, "plugin.CallAdmin");
+ AutoExecConfig_CleanFile();
+
+
+ LoadTranslations("calladmin.phrases");
+
+ // This is done so that when the plugin is updated its version stays up to date too
+ g_hVersion.SetString(CALLADMIN_VERSION, false, false);
+ g_hVersion.AddChangeHook(OnCvarChanged);
+
+
+ g_hServerName.AddChangeHook(OnCvarChanged);
+ g_hHostPort.AddChangeHook(OnCvarChanged);
+ g_hHostIP.AddChangeHook(OnCvarChanged);
+ g_hAdvertInterval.AddChangeHook(OnCvarChanged);
+ g_hPublicMessage.AddChangeHook(OnCvarChanged);
+ g_hOwnReason.AddChangeHook(OnCvarChanged);
+ g_hConfirmCall.AddChangeHook(OnCvarChanged);
+ g_hSpamTime.AddChangeHook(OnCvarChanged);
+ g_hReportTime.AddChangeHook(OnCvarChanged);
+ g_hAdminAction.AddChangeHook(OnCvarChanged);
+
+
+ // Modules must create their own updaters
+ CreateTimer(10.0, Timer_UpdateTrackersCount, _, TIMER_REPEAT);
+
+
+ // Used to allow a client to input their own reason
+ AddCommandListener(ChatListener, "say");
+ AddCommandListener(ChatListener, "say2");
+ AddCommandListener(ChatListener, "say_team");
+
+
+ // Api
+ g_hOnReportPreForward = CreateGlobalForward("CallAdmin_OnReportPre", ET_Event, Param_Cell, Param_Cell, Param_String);
+ g_hOnReportPostForward = CreateGlobalForward("CallAdmin_OnReportPost", ET_Ignore, Param_Cell, Param_Cell, Param_String);
+ g_hOnDrawMenuForward = CreateGlobalForward("CallAdmin_OnDrawMenu", ET_Event, Param_Cell);
+ g_hOnDrawOwnReasonForward = CreateGlobalForward("CallAdmin_OnDrawOwnReason", ET_Event, Param_Cell);
+ g_hOnTrackerCountChangedForward = CreateGlobalForward("CallAdmin_OnTrackerCountChanged", ET_Ignore, Param_Cell, Param_Cell);
+ g_hOnDrawTargetForward = CreateGlobalForward("CallAdmin_OnDrawTarget", ET_Event, Param_Cell, Param_Cell);
+ g_hOnAddToAdminCountForward = CreateGlobalForward("CallAdmin_OnAddToAdminCount", ET_Event, Param_Cell);
+ g_hOnServerDataChangedForward = CreateGlobalForward("CallAdmin_OnServerDataChanged", ET_Ignore, Param_Cell, Param_Cell, Param_String, Param_String);
+ g_hOnLogMessageForward = CreateGlobalForward("CallAdmin_OnLogMessage", ET_Ignore, Param_Cell, Param_String);
+ g_hOnReportHandledForward = CreateGlobalForward("CallAdmin_OnReportHandled", ET_Ignore, Param_Cell, Param_Cell);
+
+
+ // Cookies
+ if (LibraryExists("clientprefs"))
+ {
+ g_hLastReportCookie = RegClientCookie("CallAdmin_LastReport", "Contains a timestamp when this user has reported the last time", CookieAccess_Private);
+ g_hLastReportedCookie = RegClientCookie("CallAdmin_LastReported", "Contains a timestamp when this user was reported the last time", CookieAccess_Private);
+
+ FetchClientCookies();
+ }
+
+
+ // Report handling
+ g_hActiveReports = new ArrayList();
+
+ // Reason handling
+ g_hReasonAdt = new ArrayList(ByteCountToCells(REASON_MAX_LENGTH));
+
+ BuildPath(Path_SM, g_sReasonConfigFile, sizeof(g_sReasonConfigFile), "configs/calladmin_reasons.cfg");
+
+ if (!FileExists(g_sReasonConfigFile))
+ {
+ CreateReasonList();
+ }
+
+ ParseReasonList();
+}
+
+
+
+
+void CreateReasonList()
+{
+ File hFile;
+ hFile = OpenFile(g_sReasonConfigFile, "w");
+
+ if (hFile == null)
+ {
+ CallAdmin_LogMessage("Failed to open configfile 'calladmin_reasons.cfg' for writing");
+ SetFailState("Failed to open configfile 'calladmin_reasons.cfg' for writing");
+ }
+
+ hFile.WriteLine("// List of reasons seperated by a new line, max %d in length", REASON_MAX_LENGTH);
+ hFile.WriteLine("Aimbot");
+ hFile.WriteLine("Wallhack");
+ hFile.WriteLine("Speedhack");
+ hFile.WriteLine("Spinhack");
+ hFile.WriteLine("Multihack");
+ hFile.WriteLine("No-Recoil Hack");
+ hFile.WriteLine("Other");
+
+ hFile.Close();
+}
+
+
+
+
+void ParseReasonList()
+{
+ File hFile;
+
+ hFile = OpenFile(g_sReasonConfigFile, "r");
+
+
+ if (hFile == null)
+ {
+ CallAdmin_LogMessage("Failed to open configfile 'calladmin_reasons.cfg' for reading");
+ SetFailState("Failed to open configfile 'calladmin_reasons.cfg' for reading");
+ }
+
+
+ // Buffer must be a little bit bigger to have enough room for possible comments and being able to check for too long reasons
+ char sReadBuffer[PLATFORM_MAX_PATH];
+
+
+ int len;
+ while (!hFile.EndOfFile() && hFile.ReadLine(sReadBuffer, sizeof(sReadBuffer)))
+ {
+ if (sReadBuffer[0] == '/' || IsCharSpace(sReadBuffer[0]))
+ {
+ continue;
+ }
+
+ ReplaceString(sReadBuffer, sizeof(sReadBuffer), "\n", "");
+ ReplaceString(sReadBuffer, sizeof(sReadBuffer), "\r", "");
+ ReplaceString(sReadBuffer, sizeof(sReadBuffer), "\t", "");
+
+ len = strlen(sReadBuffer);
+
+
+ if (len < 3 || len > REASON_MAX_LENGTH)
+ {
+ continue;
+ }
+
+
+ // Add the reason to the list only if it doesn't already exist
+ if (g_hReasonAdt.FindString(sReadBuffer) == -1)
+ {
+ g_hReasonAdt.PushString(sReadBuffer);
+ }
+ }
+
+ hFile.Close();
+}
+
+
+
+
+public void OnClientCookiesCached(int client)
+{
+ char sCookieBuf[24];
+ GetClientCookie(client, g_hLastReportCookie, sCookieBuf, sizeof(sCookieBuf));
+
+ if (strlen(sCookieBuf) > 0)
+ {
+ g_iLastReport[client] = StringToInt(sCookieBuf);
+ }
+
+
+ // Just to be safe
+ sCookieBuf[0] = '\0';
+
+ GetClientCookie(client, g_hLastReportedCookie, sCookieBuf, sizeof(sCookieBuf));
+
+ if (strlen(sCookieBuf) > 0)
+ {
+ g_iLastReported[client] = StringToInt(sCookieBuf);
+ }
+}
+
+
+
+
+void FetchClientCookies()
+{
+ for (int i; i <= MaxClients; i++)
+ {
+ if (IsClientValid(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && !IsClientReplay(i) && AreClientCookiesCached(i))
+ {
+ OnClientCookiesCached(i);
+ }
+ }
+}
+
+
+
+
+bool Forward_OnDrawMenu(int client)
+{
+ Action result;
+
+ Call_StartForward(g_hOnDrawMenuForward);
+ Call_PushCell(client);
+
+ Call_Finish(result);
+
+ return (result == Plugin_Continue);
+}
+
+
+
+
+bool Forward_OnReportPre(int client, int target, const char[] reason)
+{
+ Action result;
+
+ Call_StartForward(g_hOnReportPreForward);
+ Call_PushCell(client);
+ Call_PushCell(target);
+ Call_PushString(reason);
+
+ Call_Finish(result);
+
+ return (result == Plugin_Continue);
+}
+
+
+
+
+void Forward_OnReportPost(int client, int target, const char[] reason)
+{
+ Call_StartForward(g_hOnReportPostForward);
+ Call_PushCell(client);
+ Call_PushCell(target);
+ Call_PushString(reason);
+
+ Call_Finish();
+}
+
+
+
+bool Forward_OnDrawOwnReason(int client)
+{
+ Action result;
+
+ Call_StartForward(g_hOnDrawOwnReasonForward);
+ Call_PushCell(client);
+
+ Call_Finish(result);
+
+ return (result == Plugin_Continue);
+}
+
+
+
+bool Forward_OnAddToAdminCount(int client)
+{
+ Action result;
+
+ Call_StartForward(g_hOnAddToAdminCountForward);
+ Call_PushCell(client);
+
+ Call_Finish(result);
+
+ return (result == Plugin_Continue);
+}
+
+
+
+void Forward_OnTrackerCountChanged(int oldVal, int newVal)
+{
+ Call_StartForward(g_hOnTrackerCountChangedForward);
+ Call_PushCell(oldVal);
+ Call_PushCell(newVal);
+
+ Call_Finish();
+}
+
+
+
+bool Forward_OnDrawTarget(int client, int target)
+{
+ Action result;
+
+ Call_StartForward(g_hOnDrawTargetForward);
+ Call_PushCell(client);
+ Call_PushCell(target);
+
+ Call_Finish(result);
+
+ return (result == Plugin_Continue);
+}
+
+
+
+void Forward_OnServerDataChanged(ConVar convar, ServerData type, const char[] oldVal, const char[] newVal)
+{
+ Call_StartForward(g_hOnServerDataChangedForward);
+ Call_PushCell(convar);
+ Call_PushCell(type);
+ Call_PushString(oldVal);
+ Call_PushString(newVal);
+
+ Call_Finish();
+}
+
+
+
+void Forward_OnLogMessage(Handle plugin, const char[] message)
+{
+ Call_StartForward(g_hOnLogMessageForward);
+ Call_PushCell(plugin);
+ Call_PushString(message);
+
+ Call_Finish();
+}
+
+
+
+void Forward_OnReportHandled(int client, int id)
+{
+ Call_StartForward(g_hOnReportHandledForward);
+ Call_PushCell(client);
+ Call_PushCell(id);
+
+ Call_Finish();
+}
+
+
+
+
+public Action Timer_Advert(Handle timer)
+{
+ if (g_iCurrentTrackers > 0)
+ {
+ // Spelling is different (0 admins, 1 admin, 2 admins, 3 admins...)
+ if (g_iCurrentTrackers == 1)
+ {
+ PrintToChatAll("\x04[CALLADMIN]\x03 %t", "CallAdmin_AdvertMessageSingular", g_iCurrentTrackers);
+ }
+ else
+ {
+ PrintToChatAll("\x04[CALLADMIN]\x03 %t", "CallAdmin_AdvertMessagePlural", g_iCurrentTrackers);
+ }
+ }
+
+ return Plugin_Handled;
+}
+
+
+
+
+public void OnAllPluginsLoaded()
+{
+ if (LibraryExists("updater"))
+ {
+ Updater_AddPlugin(UPDATER_URL);
+ }
+}
+
+
+
+
+public void OnLibraryAdded(const char[] name)
+{
+ if (StrEqual(name, "updater"))
+ {
+ Updater_AddPlugin(UPDATER_URL);
+ }
+}
+
+
+
+
+public void OnCvarChanged(ConVar cvar, const char[] oldValue, const char[] newValue)
+{
+ if (cvar == g_hHostPort)
+ {
+ g_iHostPort = g_hHostPort.IntValue;
+
+ Forward_OnServerDataChanged(cvar, ServerData_HostPort, oldValue, newValue);
+ }
+ else if (cvar == g_hHostIP)
+ {
+ UpdateHostIp();
+
+ Forward_OnServerDataChanged(cvar, ServerData_HostIP, g_sHostIP, g_sHostIP);
+ }
+ else if (cvar == g_hServerName)
+ {
+ g_hServerName.GetString(g_sServerName, sizeof(g_sServerName));
+
+ Forward_OnServerDataChanged(cvar, ServerData_HostName, oldValue, newValue);
+ }
+ else if (cvar == g_hVersion)
+ {
+ g_hVersion.SetString(CALLADMIN_VERSION, false, false);
+ }
+ else if (cvar == g_hAdvertInterval)
+ {
+ delete g_hAdvertTimer;
+
+ g_fAdvertInterval = g_hAdvertInterval.FloatValue;
+
+ if (g_fAdvertInterval != 0.0)
+ {
+ g_hAdvertTimer = CreateTimer(g_fAdvertInterval, Timer_Advert, _, TIMER_REPEAT);
+ }
+ }
+ else if (cvar == g_hPublicMessage)
+ {
+ g_bPublicMessage = g_hPublicMessage.BoolValue;
+ }
+ else if (cvar == g_hOwnReason)
+ {
+ g_bOwnReason = g_hOwnReason.BoolValue;
+ }
+ else if (cvar == g_hConfirmCall)
+ {
+ g_bConfirmCall = g_hConfirmCall.BoolValue;
+ }
+ else if (cvar == g_hSpamTime)
+ {
+ g_iSpamTime = g_hSpamTime.IntValue;
+ }
+ else if (cvar == g_hReportTime)
+ {
+ g_iReportTime = g_hReportTime.IntValue;
+ }
+ else if (cvar == g_hAdminAction)
+ {
+ g_iAdminAction = g_hAdminAction.IntValue;
+ }
+}
+
+
+
+
+public Action Command_Call(int client, int argc)
+{
+ // Console cannot use this
+ if (client == 0)
+ {
+ ReplyToCommand(client, "This command can't be used from console");
+
+ return Plugin_Handled;
+ }
+
+
+ if (!Forward_OnDrawMenu(client))
+ {
+ return Plugin_Handled;
+ }
+
+
+ if (g_iLastReport[client] == 0 || LastReportTimeCheck(client))
+ {
+ g_bSawMessage[client] = false;
+
+ ShowClientSelectMenu(client);
+ }
+ else if (!g_bSawMessage[client])
+ {
+ ReplyToCommand(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_CommandNotAllowed", g_iSpamTime - ( GetTime() - g_iLastReport[client] ));
+ g_bSawMessage[client] = true;
+ }
+
+ return Plugin_Handled;
+}
+
+
+
+public Action Command_HandleCall(int client, int argc)
+{
+ if (client == 0)
+ {
+ ReplyToCommand(client, "This command can't be used from console");
+
+ return Plugin_Handled;
+ }
+
+
+ if (!CheckCommandAccess(client, "sm_calladmin_admin", ADMFLAG_BAN, false))
+ {
+ ReplyToCommand(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_NoAdmin");
+
+ return Plugin_Handled;
+ }
+
+
+ if (argc != 1)
+ {
+ char cmdName[64];
+ GetCmdArg(0, cmdName, sizeof(cmdName));
+ ReplyToCommand(client, "\x04[CALLADMIN]\x03 %t: %s ", "CallAdmin_WrongNumberOfArguments", cmdName);
+
+ return Plugin_Handled;
+ }
+
+
+ char sArgID[10];
+ int reportID;
+
+ GetCmdArg(1, sArgID, sizeof(sArgID));
+ reportID = StringToInt(sArgID);
+
+
+ if (reportID > g_iCurrentReportID)
+ {
+ ReplyToCommand(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_WrongReportID");
+
+ return Plugin_Handled;
+ }
+
+
+ // Report was already handled
+ int reportIndex = g_hActiveReports.FindValue(reportID);
+ if (reportIndex == -1)
+ {
+ ReplyToCommand(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ReportAlreadyHandled");
+
+ return Plugin_Handled;
+ }
+
+
+ g_hActiveReports.Erase(reportIndex);
+ Forward_OnReportHandled(client, reportID);
+
+ return Plugin_Handled;
+}
+
+
+
+public Action Command_Reload(int client, int argc)
+{
+ if (!CheckCommandAccess(client, "sm_calladmin_admin", ADMFLAG_BAN, false))
+ {
+ ReplyToCommand(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_NoAdmin");
+
+ return Plugin_Handled;
+ }
+
+
+ g_hActiveReports.Clear();
+ g_hReasonAdt.Clear();
+ ParseReasonList();
+
+ return Plugin_Handled;
+}
+
+
+
+bool LastReportTimeCheck(int client)
+{
+ if (g_iLastReport[client] <= ( GetTime() - g_iSpamTime ))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+
+bool LastReportedTimeCheck(int client)
+{
+ if (g_iLastReported[client] <= ( GetTime() - g_iReportTime ))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+
+// Updates the timestamps of lastreport and lastreported
+void SetStates(int client, int target)
+{
+ int currentTime = GetTime();
+
+ g_iLastReport[client] = currentTime;
+ g_iLastReported[target] = currentTime;
+
+
+ // Cookies
+ if (LibraryExists("clientprefs"))
+ {
+ SetClientCookieEx(client, g_hLastReportCookie, "%d", currentTime);
+ SetClientCookieEx(target, g_hLastReportedCookie, "%d", currentTime);
+ }
+}
+
+
+
+void ConfirmCall(int client)
+{
+ Menu menu = new Menu(MenuHandler_ConfirmCall);
+ menu.SetTitle("%T", "CallAdmin_ConfirmCall", client);
+
+ char sConfirm[24];
+
+ Format(sConfirm, sizeof(sConfirm), "%T", "CallAdmin_Yes", client);
+ menu.AddItem("Yes", sConfirm);
+
+ Format(sConfirm, sizeof(sConfirm), "%T", "CallAdmin_No", client);
+ menu.AddItem("No", sConfirm);
+
+ menu.Display(client, 30);
+}
+
+
+
+public int MenuHandler_ConfirmCall(Menu menu, MenuAction action, int client, int param2)
+{
+ if (action == MenuAction_Select)
+ {
+ char sInfo[24];
+ menu.GetItem(param2, sInfo, sizeof(sInfo));
+
+ // Client has chosen to confirm the call
+ if (StrEqual("Yes", sInfo))
+ {
+ if (!ReportPlayer(client, g_iTarget[client], g_sTargetReason[client]))
+ {
+ return;
+ }
+ }
+ else
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_CallAborted");
+ }
+ }
+ else if (action == MenuAction_End)
+ {
+ menu.Close();
+ }
+}
+
+
+bool PreReportCheck(int client, int target)
+{
+ // Selected target isn't valid anymore
+ if (!IsClientValid(target))
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_NotInGame");
+
+ return false;
+ }
+
+
+ // Already reported (race condition)
+ if (!LastReportedTimeCheck(target))
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_AlreadyReported");
+
+ return false;
+ }
+
+ return true;
+}
+
+
+
+bool ReportPlayer(int client, int target, char[] sReason)
+{
+ if (!PreReportCheck(client, target))
+ {
+ return false;
+ }
+
+
+ // Admins available and we want to notify them instead of sending the report
+ if (GetAdminCount() > 0 && g_iAdminAction == ADMIN_ACTION_BLOCK_MESSAGE)
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_IngameAdminNotified");
+ PrintNotifyMessageToAdmins(client, g_iTarget[client]);
+
+ SetStates(client, g_iTarget[client]);
+
+ return false;
+ }
+
+
+ if (!Forward_OnReportPre(client, g_iTarget[client], g_sTargetReason[client]))
+ {
+ return false;
+ }
+
+ if (g_bPublicMessage)
+ {
+ PrintToChatAll("\x04[CALLADMIN]\x03 %t", "CallAdmin_HasReported", client, target, sReason);
+ }
+ else
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_YouHaveReported", target, sReason);
+ }
+
+ SetStates(client, target);
+
+
+ g_iCurrentReportID++;
+ g_hActiveReports.Push(g_iCurrentReportID);
+
+ Forward_OnReportPost(client, target, sReason);
+
+ return true;
+}
+
+
+
+
+
+
+
+
+public Action Timer_UpdateTrackersCount(Handle timer)
+{
+ int temp = GetTotalTrackers();
+
+ if (temp != g_iCurrentTrackers)
+ {
+ Forward_OnTrackerCountChanged(g_iCurrentTrackers, temp);
+ }
+
+ g_iCurrentTrackers = temp;
+
+ return Plugin_Continue;
+}
+
+
+
+
+int GetTotalTrackers()
+{
+ Handle hIter;
+ Handle hPlugin;
+ Function func;
+ int count;
+ int tempcount;
+
+ hIter = GetPluginIterator();
+
+ while (MorePlugins(hIter))
+ {
+ hPlugin = ReadPlugin(hIter);
+
+ if (GetPluginStatus(hPlugin) == Plugin_Running)
+ {
+ // We check if the plugin has the public CallAdmin_OnRequestTrackersCountRefresh function
+ if ( (func = GetFunctionByName(hPlugin, "CallAdmin_OnRequestTrackersCountRefresh") ) != INVALID_FUNCTION)
+ {
+ Call_StartFunction(hPlugin, func);
+ Call_PushCellRef(tempcount);
+
+ Call_Finish();
+
+ if (tempcount > 0)
+ {
+ count += tempcount;
+ }
+ }
+ }
+ }
+
+ delete hIter;
+
+ return count;
+}
+
+
+
+
+void ShowClientSelectMenu(int client)
+{
+ char sName[MAX_NAME_LENGTH];
+ char sID[24];
+
+ Menu menu = new Menu(MenuHandler_ClientSelect);
+ menu.SetTitle("%T", "CallAdmin_SelectClient", client);
+
+ for (int i; i <= MaxClients; i++)
+ {
+ if (i != client && LastReportedTimeCheck(i) && IsClientValid(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && !IsClientReplay(i) && Forward_OnDrawTarget(client, i))
+ {
+ GetClientName(i, sName, sizeof(sName));
+ Format(sID, sizeof(sID), "%d", GetClientSerial(i));
+
+ menu.AddItem(sID, sName);
+ }
+ }
+
+ // Menu has no items, no players to report
+ if (menu.ItemCount < 1)
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_NoPlayers");
+ }
+ else
+ {
+ menu.Display(client, 30);
+ }
+}
+
+
+
+
+public int MenuHandler_ClientSelect(Menu menu, MenuAction action, int client, int param2)
+{
+ if (action == MenuAction_Select)
+ {
+ char sInfo[24];
+ int iSerial;
+ int iID;
+
+ menu.GetItem(param2, sInfo, sizeof(sInfo));
+
+ iSerial = StringToInt(sInfo);
+ iID = GetClientFromSerial(iSerial);
+
+
+ if (!PreReportCheck(client, iID))
+ {
+ return;
+ }
+
+ g_iTarget[client] = iID;
+
+ ShowBanReasonMenu(client);
+ }
+ else if (action == MenuAction_End)
+ {
+ menu.Close();
+ }
+}
+
+
+
+
+public void OnClientDisconnect_Post(int client)
+{
+ g_iTarget[client] = 0;
+ g_sTargetReason[client][0] = '\0';
+ g_iLastReport[client] = 0;
+ g_iLastReported[client] = 0;
+ g_bSawMessage[client] = false;
+ g_bAwaitingReason[client] = false;
+
+ RemoveAsTarget(client);
+}
+
+
+
+
+void RemoveAsTarget(int client)
+{
+ for (int i; i <= MaxClients; i++)
+ {
+ if (g_iTarget[i] == client)
+ {
+ g_iTarget[i] = 0;
+ }
+ }
+}
+
+
+
+
+void ShowBanReasonMenu(int client)
+{
+ int count;
+ char sReasonBuffer[REASON_MAX_LENGTH];
+ count = g_hReasonAdt.Length;
+
+
+ Menu menu = new Menu(MenuHandler_BanReason);
+ menu.SetTitle("%T", "CallAdmin_SelectReason", client, g_iTarget[client]);
+
+ for (int i; i < count; i++)
+ {
+ g_hReasonAdt.GetString(i, sReasonBuffer, sizeof(sReasonBuffer));
+
+ if (strlen(sReasonBuffer) < 3)
+ {
+ continue;
+ }
+
+
+ menu.AddItem(sReasonBuffer, sReasonBuffer);
+ }
+
+ // Own reason, call the forward
+ if (g_bOwnReason && Forward_OnDrawOwnReason(client))
+ {
+ char sOwnReason[REASON_MAX_LENGTH];
+
+ Format(sOwnReason, sizeof(sOwnReason), "%T", "CallAdmin_OwnReason", client);
+ menu.AddItem("Own reason", sOwnReason);
+ }
+
+ menu.Display(client, 30);
+}
+
+
+
+
+public int MenuHandler_BanReason(Menu menu, MenuAction action, int client, int param2)
+{
+ if (action == MenuAction_Select)
+ {
+ char sInfo[REASON_MAX_LENGTH];
+ menu.GetItem(param2, sInfo, sizeof(sInfo));
+
+ // User has chosen to use his own reason
+ if (StrEqual("Own reason", sInfo))
+ {
+ g_bAwaitingReason[client] = true;
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_TypeOwnReason");
+ return;
+ }
+
+ Format(g_sTargetReason[client], sizeof(g_sTargetReason[]), sInfo);
+
+ if (!PreReportCheck(client, g_iTarget[client]))
+ {
+ return;
+ }
+
+
+ if (g_bConfirmCall)
+ {
+ ConfirmCall(client);
+ }
+ else
+ {
+ if (!ReportPlayer(client, g_iTarget[client], g_sTargetReason[client]))
+ {
+ return;
+ }
+ }
+ }
+ else if (action == MenuAction_End)
+ {
+ menu.Close();
+ }
+}
+
+
+
+
+public Action ChatListener(int client, const char[] command, int argc)
+{
+ // There were a few cases were the client index was invalid which caused an index out-of-bounds error
+ // Invalid clients shouldn't be able to trigger this callback so the reason why this happens has yet to be found out
+ // Until then we have this check here to prevent it
+ if (!IsClientValid(client))
+ {
+ return Plugin_Continue;
+ }
+
+
+ if (g_bAwaitingReason[client] && !IsChatTrigger())
+ {
+ // 2 more for quotes
+ char sReason[REASON_MAX_LENGTH + 2];
+
+ GetCmdArgString(sReason, sizeof(sReason));
+ StripQuotes(sReason);
+ strcopy(g_sTargetReason[client], sizeof(g_sTargetReason[]), sReason);
+
+ g_bAwaitingReason[client] = false;
+
+
+ // Has aborted
+ if (StrEqual(sReason, "!noreason") || StrEqual(sReason, "!abort"))
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_CallAborted");
+
+ return Plugin_Handled;
+ }
+
+
+ // Reason was too short
+ if (strlen(sReason) < 3)
+ {
+ g_bAwaitingReason[client] = true;
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_OwnReasonTooShort");
+
+ return Plugin_Handled;
+ }
+
+
+ if (!PreReportCheck(client, g_iTarget[client]))
+ {
+ return Plugin_Handled;
+ }
+
+
+ if (g_bConfirmCall)
+ {
+ ConfirmCall(client);
+ }
+ else
+ {
+ if (!ReportPlayer(client, g_iTarget[client], g_sTargetReason[client]))
+ {
+ return Plugin_Handled;
+ }
+ }
+
+
+ // Block the chatmessage
+ return Plugin_Handled;
+ }
+
+ return Plugin_Continue;
+}
+
+
+
+stock int GetRealClientCount()
+{
+ int count;
+
+ for (int i; i <= MaxClients; i++)
+ {
+ if (IsClientValid(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && !IsClientReplay(i))
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+
+stock int GetAdminCount()
+{
+ int count;
+
+ for (int i; i <= MaxClients; i++)
+ {
+ if (IsClientValid(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && !IsClientReplay(i) && CheckCommandAccess(i, "sm_calladmin_admin", ADMFLAG_BAN, false) && Forward_OnAddToAdminCount(i))
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+stock void PrintNotifyMessageToAdmins(int client, int target)
+{
+ for (int i; i <= MaxClients; i++)
+ {
+ if (IsClientValid(i) && !IsFakeClient(i) && !IsClientSourceTV(i) && !IsClientReplay(i) && CheckCommandAccess(i, "sm_calladmin_admin", ADMFLAG_BAN, false) && Forward_OnAddToAdminCount(i))
+ {
+ PrintToChat(i, "\x04[CALLADMIN]\x03 %t", "CallAdmin_AdminNotification", client, target, g_sTargetReason[client]);
+ }
+ }
+}
+
+
+
+stock void LongToIp(int long, char[] str, int maxlen)
+{
+ int pieces[4];
+
+ pieces[0] = ((long >>> 24) & 255);
+ pieces[1] = ((long >>> 16) & 255);
+ pieces[2] = ((long >>> 8) & 255);
+ pieces[3] = (long & 255);
+
+ Format(str, maxlen, "%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]);
+}
+
+
+
+// Updates the global g_sHostIP variable to the current ip of the server
+// Using the int value directly provides incorrect results, when given the time it should be examined why
+void UpdateHostIp()
+{
+ char tmpString[sizeof(g_sHostIP)];
+ g_hHostIP.GetString(tmpString, sizeof(tmpString));
+
+ int tmpInt = StringToInt(tmpString);
+ LongToIp(tmpInt, g_sHostIP, sizeof(g_sHostIP));
+}
+
+
+
+stock void SetClientCookieEx(int client, Handle cookie, const char[] format, any:...)
+{
+ char sFormatBuf[1024];
+ VFormat(sFormatBuf, sizeof(sFormatBuf), format, 4);
+
+ SetClientCookie(client, cookie, sFormatBuf);
+}
\ No newline at end of file
diff --git a/calladmin_restrictions/scripting/calladmin_restrictions.sp b/_CallAdmin/scripting/CallAdminRestrictions.sp
similarity index 100%
rename from calladmin_restrictions/scripting/calladmin_restrictions.sp
rename to _CallAdmin/scripting/CallAdminRestrictions.sp
diff --git a/_CallAdmin/scripting/CallAdminUsermanager.sp b/_CallAdmin/scripting/CallAdminUsermanager.sp
new file mode 100644
index 00000000..5c97d58d
--- /dev/null
+++ b/_CallAdmin/scripting/CallAdminUsermanager.sp
@@ -0,0 +1,402 @@
+/**
+ * -----------------------------------------------------
+ * File calladmin_usermanager.sp
+ * Authors dordnung, Impact
+ * License GPLv3
+ * Web https://dordnung.de, http://gugyclan.eu
+ * -----------------------------------------------------
+ *
+ * CallAdmin
+ * Copyright (C) 2013-2018 dordnung, Impact
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ */
+
+
+#include
+#include "include/autoexecconfig"
+#include "include/calladmin"
+#include "include/calladmin_stocks"
+
+#undef REQUIRE_PLUGIN
+#include "include/updater"
+#include
+
+#pragma semicolon 1
+#pragma newdecls required
+
+
+// Version cvar
+ConVar g_hVersion;
+
+// Cvar to blacklist muted players
+ConVar g_hBlacklistMuted;
+bool g_bBlacklistMuted;
+
+// Cvar to blacklist gagged players
+ConVar g_hBlacklistGagged;
+bool g_bBlacklistGagged;
+
+// Cvar to show information
+ConVar g_hShowInformation;
+bool g_bShowInformation;
+
+
+
+// Is immune or on blacklist?
+bool g_bClientOnBlacklist[MAXPLAYERS + 1];
+bool g_bClientImmune[MAXPLAYERS + 1];
+
+
+
+// Updater
+#define UPDATER_URL "http://plugins.gugyclan.eu/calladmin/calladmin_usermanager.txt"
+
+
+
+public Plugin myinfo =
+{
+ name = "CallAdmin UserManager",
+ author = "dordnung, Impact",
+ description = "The usermanagermodule for CallAdmin",
+ version = CALLADMIN_VERSION,
+ url = "https://dordnung.de"
+}
+
+
+
+
+
+
+/*
+
+Sourcemod
+
+*/
+
+
+// Register the library
+public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
+{
+ RegPluginLibrary("calladmin_usermanager");
+
+
+ // Api
+ CreateNative("CallAdmin_SetClientOnBlacklist", Native_SetClientOnBlacklist);
+ CreateNative("CallAdmin_SetClientImmune", Native_SetClientImmune);
+ CreateNative("CallAdmin_IsClientOnBlacklist", Native_IsClientOnBlacklist);
+ CreateNative("CallAdmin_IsClientImmune", Native_IsClientImmune);
+
+
+ return APLRes_Success;
+}
+
+
+
+public void OnConfigsExecuted()
+{
+ g_bBlacklistMuted = g_hBlacklistMuted.BoolValue;
+ g_bBlacklistGagged = g_hBlacklistGagged.BoolValue;
+ g_bShowInformation = g_hShowInformation.BoolValue;
+}
+
+
+
+
+// Plugin Started
+public void OnPluginStart()
+{
+ // Create config and load it
+ AutoExecConfig_SetFile("plugin.calladmin_usermanager");
+
+
+ g_hVersion = AutoExecConfig_CreateConVar("sm_calladmin_usermanager_version", CALLADMIN_VERSION, "Plugin version", FCVAR_NOTIFY|FCVAR_DONTRECORD);
+ g_hBlacklistMuted = AutoExecConfig_CreateConVar("sm_calladmin_blacklist_muted", "0", "Disallow muted players to report a player", FCVAR_NONE);
+ g_hBlacklistGagged = AutoExecConfig_CreateConVar("sm_calladmin_blacklist_gagged", "1", "Disallow gagged players to report a player", FCVAR_NONE);
+ g_hShowInformation = AutoExecConfig_CreateConVar("sm_calladmin_show_information", "0", "Show status to player on mute/gag", FCVAR_NONE);
+
+
+ AutoExecConfig(true, "plugin.CallAdminUsermanager");
+ AutoExecConfig_CleanFile();
+
+
+ // Load translation
+ LoadTranslations("calladmin_usermanager.phrases");
+
+
+ // Set Version
+ g_hVersion.SetString(CALLADMIN_VERSION);
+
+ // Hook changes
+ g_hVersion.AddChangeHook(OnCvarChanged);
+ g_hBlacklistMuted.AddChangeHook(OnCvarChanged);
+ g_hBlacklistGagged.AddChangeHook(OnCvarChanged);
+ g_hShowInformation.AddChangeHook(OnCvarChanged);
+}
+
+
+// Convar Changed
+public void OnCvarChanged(Handle cvar, const char[] oldValue, const char[] newValue)
+{
+ if (cvar == g_hBlacklistMuted)
+ {
+ g_bBlacklistMuted = g_hBlacklistMuted.BoolValue;
+
+ // Check basecomm
+ if (!LibraryExists("basecomm") && g_bBlacklistMuted)
+ {
+ CallAdmin_LogMessage("Couldn't find Plugin basecomm.smx. But you've activated mute blacklisting!");
+ }
+ }
+
+ else if (cvar == g_hBlacklistGagged)
+ {
+ g_bBlacklistGagged = g_hBlacklistGagged.BoolValue;
+
+ // Check basecomm
+ if (!LibraryExists("basecomm") && g_hBlacklistGagged)
+ {
+ CallAdmin_LogMessage("Couldn't find Plugin basecomm.smx. But you've activated gag blacklisting!");
+ }
+ }
+
+ else if (cvar == g_hShowInformation)
+ {
+ g_bShowInformation = g_hShowInformation.BoolValue;
+ }
+
+ else if (cvar == g_hVersion)
+ {
+ g_hVersion.SetString(CALLADMIN_VERSION);
+ }
+}
+
+
+// Updater
+public void OnAllPluginsLoaded()
+{
+ if (LibraryExists("updater"))
+ {
+ Updater_AddPlugin(UPDATER_URL);
+ }
+
+ if (!LibraryExists("basecomm") && (g_bBlacklistMuted || g_bBlacklistGagged))
+ {
+ CallAdmin_LogMessage("Couldn't find Plugin basecomm.smx. But you've activated mute or gag blacklisting!");
+ }
+}
+
+
+// Updater
+public void OnLibraryAdded(const char[] name)
+{
+ if (StrEqual(name, "updater"))
+ {
+ Updater_AddPlugin(UPDATER_URL);
+ }
+}
+
+
+
+
+
+
+/*
+
+NATIVES
+
+*/
+
+
+// Set client on blacklist
+public int Native_SetClientOnBlacklist(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+
+ if (IsClientValid(client))
+ {
+ g_bClientOnBlacklist[client] = GetNativeCell(2);
+ }
+}
+
+
+// Set Client immune
+public int Native_SetClientImmune(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+
+ if (IsClientValid(client))
+ {
+ g_bClientImmune[client] = GetNativeCell(2);
+ }
+}
+
+
+// Checks if the client is on the blacklist
+public int Native_IsClientOnBlacklist(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+
+ if (IsClientValid(client))
+ {
+ return g_bClientOnBlacklist[client];
+ }
+
+ return false;
+}
+
+
+// Checks if the client is immune
+public int Native_IsClientImmune(Handle plugin, int numParams)
+{
+ int client = GetNativeCell(1);
+
+ if (IsClientValid(client))
+ {
+ return g_bClientImmune[client];
+ }
+
+ return false;
+}
+
+
+
+
+
+/*
+
+CallAdmin
+
+*/
+
+// Client open the menu
+public Action CallAdmin_OnDrawMenu(int client)
+{
+ // Client is on blacklist, so don't open menu
+ if (g_bClientOnBlacklist[client])
+ {
+ // Info text
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ClientOnBlacklist");
+
+ return Plugin_Handled;
+ }
+
+ return Plugin_Continue;
+}
+
+
+// Client will drawn to menu
+public Action CallAdmin_OnDrawTarget(int client, int target)
+{
+ // Target is immune, so don't draw it
+ if (g_bClientImmune[target])
+ {
+ return Plugin_Handled;
+ }
+
+ return Plugin_Continue;
+}
+
+
+// Client will report
+public Action CallAdmin_OnReportPre(int client, int target, const char[] reason)
+{
+ // Target is immune, so don't report
+ if (g_bClientImmune[target])
+ {
+ // Info text
+ if (client != REPORTER_CONSOLE)
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_TargetImmune", target);
+ }
+
+ return Plugin_Handled;
+ }
+
+ // Client is on blacklist so don't allow report
+ if (client != REPORTER_CONSOLE && g_bClientOnBlacklist[client])
+ {
+ // Info text
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ClientOnBlacklist");
+
+ return Plugin_Handled;
+ }
+
+ return Plugin_Continue;
+}
+
+
+
+
+
+/*
+
+Basecomm
+
+*/
+
+
+// Client get muted
+public void BaseComm_OnClientMute(int client, bool muteState)
+{
+ if (g_bBlacklistMuted && IsClientValid(client))
+ {
+ // Show information
+ if (g_bShowInformation && muteState != g_bClientOnBlacklist[client])
+ {
+ if (muteState)
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ClientBlacklistMute");
+ }
+ else
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ClientBlacklistRemove");
+ }
+ }
+
+ // Set client on blacklist
+ g_bClientOnBlacklist[client] = muteState;
+ }
+}
+
+
+// Client get gagged
+public void BaseComm_OnClientGag(int client, bool gagState)
+{
+ if (g_bBlacklistGagged && IsClientValid(client))
+ {
+ // Show information
+ if (g_bShowInformation && g_bClientOnBlacklist[client] != gagState)
+ {
+ if (gagState)
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ClientBlacklistGag");
+ }
+ else
+ {
+ PrintToChat(client, "\x04[CALLADMIN]\x03 %t", "CallAdmin_ClientBlacklistRemove");
+ }
+ }
+
+ // Set client on blacklist
+ g_bClientOnBlacklist[client] = gagState;
+ }
+}
+
+
+public void OnClientDisconnect_Post(int client)
+{
+ g_bClientOnBlacklist[client] = false;
+ g_bClientImmune[client] = false;
+}
\ No newline at end of file
diff --git a/_CallAdmin/scripting/include/autoexecconfig.inc b/_CallAdmin/scripting/include/autoexecconfig.inc
new file mode 100644
index 00000000..73e07d9d
--- /dev/null
+++ b/_CallAdmin/scripting/include/autoexecconfig.inc
@@ -0,0 +1,715 @@
+#if defined _autoexecconfig_included
+ #endinput
+#endif
+#define _autoexecconfig_included
+
+
+#include
+
+
+// Append
+#define AUTOEXEC_APPEND_BAD_FILENAME 0
+#define AUTOEXEC_APPEND_FILE_NOT_FOUND 1
+#define AUTOEXEC_APPEND_BAD_HANDLE 2
+#define AUTOEXEC_APPEND_SUCCESS 3
+
+
+
+// Find
+#define AUTOEXEC_FIND_BAD_FILENAME 10
+#define AUTOEXEC_FIND_FILE_NOT_FOUND 11
+#define AUTOEXEC_FIND_BAD_HANDLE 12
+#define AUTOEXEC_FIND_NOT_FOUND 13
+#define AUTOEXEC_FIND_SUCCESS 14
+
+
+
+// Clean
+#define AUTOEXEC_CLEAN_FILE_NOT_FOUND 20
+#define AUTOEXEC_CLEAN_BAD_HANDLE 21
+#define AUTOEXEC_CLEAN_SUCCESS 22
+
+
+
+// General
+#define AUTOEXEC_NO_CONFIG 30
+
+
+
+// Formatter
+#define AUTOEXEC_FORMAT_BAD_FILENAME 40
+#define AUTOEXEC_FORMAT_SUCCESS 41
+
+
+
+// Global variables
+static char g_sConfigFile[PLATFORM_MAX_PATH];
+static char g_sRawFileName[PLATFORM_MAX_PATH];
+static char g_sFolderPath[PLATFORM_MAX_PATH];
+
+static bool g_bCreateFile = false;
+static Handle g_hPluginHandle = null;
+
+
+
+// Workaround for now
+static int g_iLastFindResult;
+static int g_iLastAppendResult;
+
+
+
+
+/**
+ * Returns the last result from the parser.
+ *
+ * @return Returns one of the AUTOEXEC_FIND values or -1 if not set.
+*/
+stock int AutoExecConfig_GetFindResult()
+{
+ return g_iLastFindResult;
+}
+
+
+
+
+
+/**
+ * Returns the last result from the appender.
+ *
+ * @return Returns one of the AUTOEXEC_APPEND values or -1 if not set.
+*/
+stock int AutoExecConfig_GetAppendResult()
+{
+ return g_iLastAppendResult;
+}
+
+
+/**
+ * Set if the config file should be created if it doesn't exist yet.
+ *
+ * @param create True if config file should be created, false otherwise.
+ * @noreturn
+ */
+stock void AutoExecConfig_SetCreateFile(bool create)
+{
+ g_bCreateFile = create;
+}
+
+
+/**
+ * Returns if the config file should be created if it doesn't exist.
+ *
+ * @return Returns true, if the config file should be created or false if it should not.
+ */
+stock bool AutoExecConfig_GetCreateFile()
+{
+ return g_bCreateFile;
+}
+
+
+/**
+ * Set the plugin for which the config file should be created.
+ * Set to null to use the calling plugin.
+ * Used to print the correct filename in the top comment when creating the file.
+ *
+ * @param plugin The plugin to create convars for or null to use the calling plugin.
+ * @noreturn
+ */
+stock void AutoExecConfig_SetPlugin(Handle plugin)
+{
+ g_hPluginHandle = plugin;
+}
+
+
+/**
+ * Returns the plugin for which the config file is created.
+ *
+ * @return The plugin handle
+ */
+stock Handle AutoExecConfig_GetPlugin()
+{
+ return g_hPluginHandle;
+}
+
+
+/**
+ * Set the global autoconfigfile used by functions of this file.
+ *
+ * @param file Name of the config file, path and .cfg extension is being added if not given.
+ * @param folder Folder under cfg/ to use. By default this is "sourcemod."
+ * @return True if formatter returned success, false otherwise.
+*/
+stock bool AutoExecConfig_SetFile(char[] file, char[] folder="sourcemod")
+{
+ Format(g_sConfigFile, sizeof(g_sConfigFile), "%s", file);
+
+ // Global buffers for cfg execution
+ strcopy(g_sRawFileName, sizeof(g_sRawFileName), file);
+ strcopy(g_sFolderPath, sizeof(g_sFolderPath), folder);
+
+
+ // Format the filename
+ return AutoExecConfig_FormatFileName(g_sConfigFile, sizeof(g_sConfigFile), folder) == AUTOEXEC_FORMAT_SUCCESS;
+}
+
+
+
+
+
+
+/**
+ * Get the formatted autoconfigfile used by functions of this file.
+ *
+ * @param buffer String to format.
+ * @param size Maximum size of buffer
+ * @return True if filename was set, false otherwise.
+*/
+stock bool AutoExecConfig_GetFile(char[] buffer,int size)
+{
+ if(strlen(g_sConfigFile) > 0)
+ {
+ strcopy(buffer, size, g_sConfigFile);
+
+ return true;
+ }
+
+ // Security for decl users
+ buffer[0] = '\0';
+
+ return false;
+}
+
+
+
+
+
+
+/**
+ * Creates a convar and appends it to the autoconfigfile if not found.
+ * FCVAR_DONTRECORD will be skipped.
+ *
+ * @param name Name of new convar.
+ * @param defaultValue String containing the default value of new convar.
+ * @param description Optional description of the convar.
+ * @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details.
+ * @param hasMin Optional boolean that determines if the convar has a minimum value.
+ * @param min Minimum floating point value that the convar can have if hasMin is true.
+ * @param hasMax Optional boolean that determines if the convar has a maximum value.
+ * @param max Maximum floating point value that the convar can have if hasMax is true.
+ * @return A handle to the newly created convar. If the convar already exists, a handle to it will still be returned.
+ * @error Convar name is blank or is the same as an existing console command.
+*/
+stock ConVar AutoExecConfig_CreateConVar(const char[] name, const char[] defaultValue, const char[] description="", int flags=0, bool hasMin=false, float min=0.0, bool hasMax=false, float max=0.0)
+{
+ // If configfile was set and convar has no dontrecord flag
+ if(!(flags & FCVAR_DONTRECORD) && strlen(g_sConfigFile) > 0)
+ {
+ // Reset the results
+ g_iLastFindResult = -1;
+ g_iLastAppendResult = -1;
+
+
+ // Add it if not found
+ char buffer[64];
+
+ g_iLastFindResult = AutoExecConfig_FindValue(name, buffer, sizeof(buffer), true);
+
+ // We only add this convar if it doesn't exist, or the file doesn't exist and it should be auto-generated
+ if(g_iLastFindResult == AUTOEXEC_FIND_NOT_FOUND || (g_iLastFindResult == AUTOEXEC_FIND_FILE_NOT_FOUND && g_bCreateFile))
+ {
+ g_iLastAppendResult = AutoExecConfig_AppendValue(name, defaultValue, description, flags, hasMin, min, hasMax, max);
+ }
+ }
+
+
+ // Create the convar
+ return CreateConVar(name, defaultValue, description, flags, hasMin, min, hasMax, max);
+}
+
+
+
+
+/**
+ * Executes the autoconfigfile, and adds it to the OnConfigsExecuted forward.
+ * If we didn't created it already we let SourceMod create it.
+ *
+ * @noreturn
+*/
+stock void AutoExecConfig_ExecuteFile()
+{
+ // Only let sourcemod create the file, if we didn't do that already.
+ AutoExecConfig(!g_bCreateFile, g_sRawFileName, g_sFolderPath);
+}
+
+
+
+
+
+/**
+ * Formats a autoconfigfile, prefixes path and adds .cfg extension if missed.
+ *
+ * @param buffer String to format.
+ * @param size Maximum size of buffer.
+ * @return Returns one of the AUTOEXEC_FORMAT values..
+*/
+stock static int AutoExecConfig_FormatFileName(char[] buffer, int size, char[] folder="sourcemod")
+{
+ // No config set
+ if(strlen(g_sConfigFile) < 1)
+ {
+ return AUTOEXEC_NO_CONFIG;
+ }
+
+
+ // Can't be an cfgfile
+ if(StrContains(g_sConfigFile, ".cfg") == -1 && strlen(g_sConfigFile) < 4)
+ {
+ return AUTOEXEC_FORMAT_BAD_FILENAME;
+ }
+
+
+ // Pathprefix
+ char pathprefixbuffer[PLATFORM_MAX_PATH];
+ if(strlen(folder) > 0)
+ {
+ Format(pathprefixbuffer, sizeof(pathprefixbuffer), "cfg/%s/", folder);
+ }
+ else
+ {
+ Format(pathprefixbuffer, sizeof(pathprefixbuffer), "cfg/");
+ }
+
+
+ char filebuffer[PLATFORM_MAX_PATH];
+ filebuffer[0] = '\0';
+
+ // Add path if file doesn't begin with it
+ if(StrContains(buffer, pathprefixbuffer) != 0)
+ {
+ StrCat(filebuffer, sizeof(filebuffer), pathprefixbuffer);
+ }
+
+ StrCat(filebuffer, sizeof(filebuffer), g_sConfigFile);
+
+
+ // Add .cfg extension if file doesn't end with it
+ if(StrContains(filebuffer[strlen(filebuffer) - 4], ".cfg") != 0)
+ {
+ StrCat(filebuffer, sizeof(filebuffer), ".cfg");
+ }
+
+ strcopy(buffer, size, filebuffer);
+
+ return AUTOEXEC_FORMAT_SUCCESS;
+}
+
+
+
+
+
+
+/**
+ * Appends a convar to the global autoconfigfile
+ *
+ * @param name Name of new convar.
+ * @param defaultValue String containing the default value of new convar.
+ * @param description Optional description of the convar.
+ * @param flags Optional bitstring of flags determining how the convar should be handled. See FCVAR_* constants for more details.
+ * @param hasMin Optional boolean that determines if the convar has a minimum value.
+ * @param min Minimum floating point value that the convar can have if hasMin is true.
+ * @param hasMax Optional boolean that determines if the convar has a maximum value.
+ * @param max Maximum floating point value that the convar can have if hasMax is true.
+ * @return Returns one of the AUTOEXEC_APPEND values
+*/
+stock int AutoExecConfig_AppendValue(const char[] name, const char[] defaultValue, const char[] description, int flags, bool hasMin, float min, bool hasMax, float max)
+{
+ // No config set
+ if(strlen(g_sConfigFile) < 1)
+ {
+ return AUTOEXEC_NO_CONFIG;
+ }
+
+
+ char filebuffer[PLATFORM_MAX_PATH];
+ strcopy(filebuffer, sizeof(filebuffer), g_sConfigFile);
+
+
+ //PrintToServer("pathbuffer: %s", filebuffer);
+
+ bool bFileExists = FileExists(filebuffer);
+
+ if(g_bCreateFile || bFileExists)
+ {
+ // If the file already exists we open it in append mode, otherwise we use a write mode which creates the file
+ File fFile = OpenFile(filebuffer, (bFileExists ? "a" : "w"));
+ char writebuffer[2048];
+
+
+ if(fFile == null)
+ {
+ return AUTOEXEC_APPEND_BAD_HANDLE;
+ }
+
+ // We just created the file, so add some header about version and stuff
+ if(g_bCreateFile && !bFileExists)
+ {
+ fFile.WriteLine( "// This file was auto-generated by AutoExecConfig read and append beta");
+
+ GetPluginFilename(g_hPluginHandle, writebuffer, sizeof(writebuffer));
+ Format(writebuffer, sizeof(writebuffer), "// ConVars for plugin \"%s\"", writebuffer);
+ fFile.WriteLine(writebuffer);
+ }
+
+ // Spacer
+ fFile.WriteLine("\n");
+
+
+ // This is used for multiline comments
+ int newlines = GetCharCountInStr('\n', description);
+ if(newlines == 0)
+ {
+ // We have no newlines, we can write the description to the file as is
+ Format(writebuffer, sizeof(writebuffer), "// %s", description);
+ fFile.WriteLine(writebuffer);
+ }
+ else
+ {
+ char[][] newlineBuf = new char[newlines +1][2048];
+ ExplodeString(description, "\n", newlineBuf, newlines +1, 2048, false);
+
+ // Each newline gets a commented newline
+ for(int i; i <= newlines; i++)
+ {
+ if(strlen(newlineBuf[i]) > 0)
+ {
+ fFile.WriteLine("// %s", newlineBuf[i]);
+ }
+ }
+ }
+
+
+ // Descspacer
+ fFile.WriteLine("// -");
+
+
+ // Default
+ Format(writebuffer, sizeof(writebuffer), "// Default: \"%s\"", defaultValue);
+ fFile.WriteLine(writebuffer);
+
+
+ // Minimum
+ if(hasMin)
+ {
+ Format(writebuffer, sizeof(writebuffer), "// Minimum: \"%f\"", min);
+ fFile.WriteLine(writebuffer);
+ }
+
+
+ // Maximum
+ if(hasMax)
+ {
+ Format(writebuffer, sizeof(writebuffer), "// Maximum: \"%f\"", max);
+ fFile.WriteLine(writebuffer);
+ }
+
+
+ // Write end and defaultvalue
+ Format(writebuffer, sizeof(writebuffer), "%s \"%s\"", name, defaultValue);
+ fFile.WriteLine(writebuffer);
+
+
+ fFile.Close();
+
+
+ // Clean up the file
+ //AutoExecConfig_CleanFile(filebuffer, false);
+
+
+ return AUTOEXEC_APPEND_SUCCESS;
+ }
+
+ return AUTOEXEC_APPEND_FILE_NOT_FOUND;
+}
+
+
+
+
+
+
+/**
+ * Returns a convars value from the global autoconfigfile
+ *
+ * @param cvar Cvar to search for.
+ * @param value Buffer to store result into.
+ * @param size Maximum size of buffer.
+ * @param caseSensitive Whether or not the search should be case sensitive.
+ * @return Returns one of the AUTOEXEC_FIND values
+*/
+stock int AutoExecConfig_FindValue(const char[] cvar, char[] value, int size, bool caseSensitive=false)
+{
+ // Security for decl users
+ value[0] = '\0';
+
+
+ // No config set
+ if(strlen(g_sConfigFile) < 1)
+ {
+ return AUTOEXEC_NO_CONFIG;
+ }
+
+
+ char filebuffer[PLATFORM_MAX_PATH];
+ strcopy(filebuffer, sizeof(filebuffer), g_sConfigFile);
+
+
+
+ //PrintToServer("pathbuffer: %s", filebuffer);
+
+ bool bFileExists = FileExists(filebuffer);
+
+ // We want to create the config file and it doesn't exist yet.
+ if(g_bCreateFile && !bFileExists)
+ {
+ return AUTOEXEC_FIND_FILE_NOT_FOUND;
+ }
+
+
+ if(bFileExists)
+ {
+ File fFile = OpenFile(filebuffer, "r");
+ int valuestart;
+ int valueend;
+ int cvarend;
+
+ // Just an reminder to self, leave the values that high
+ char sConvar[64];
+ char sValue[64];
+ char readbuffer[2048];
+ char copybuffer[2048];
+
+ if(fFile == null)
+ {
+ return AUTOEXEC_FIND_BAD_HANDLE;
+ }
+
+
+ while(!fFile.EndOfFile() && fFile.ReadLine(readbuffer, sizeof(readbuffer)))
+ {
+ // Is a comment or not valid
+ if(IsCharSpace(readbuffer[0]) || readbuffer[0] == '/' || !IsCharAlpha(readbuffer[0]))
+ {
+ continue;
+ }
+
+
+ // Has not enough spaces, must have at least 1
+ if(GetCharCountInStr(' ', readbuffer) < 1)
+ {
+ continue;
+ }
+
+
+ // Ignore cvars which aren't quoted
+ if(GetCharCountInStr('"', readbuffer) != 2)
+ {
+ continue;
+ }
+
+
+
+ // Get the start of the value
+ if( (valuestart = StrContains(readbuffer, "\"")) == -1 )
+ {
+ continue;
+ }
+
+
+ // Get the end of the value
+ if( (valueend = StrContains(readbuffer[valuestart+1], "\"")) == -1 )
+ {
+ continue;
+ }
+
+
+ // Get the start of the cvar,
+ if( (cvarend = StrContains(readbuffer, " ")) == -1 || cvarend >= valuestart)
+ {
+ continue;
+ }
+
+
+ // Skip if cvarendindex is before valuestartindex
+ if(cvarend >= valuestart)
+ {
+ continue;
+ }
+
+
+ // Convar
+ // Tempcopy for security
+ strcopy(copybuffer, sizeof(copybuffer), readbuffer);
+ copybuffer[cvarend] = '\0';
+
+ strcopy(sConvar, sizeof(sConvar), copybuffer);
+
+
+ // Value
+ // Tempcopy for security
+ strcopy(copybuffer, sizeof(copybuffer), readbuffer[valuestart+1]);
+ copybuffer[valueend] = '\0';
+
+ strcopy(sValue, sizeof(sValue), copybuffer);
+
+
+ //PrintToServer("Cvar %s has a value of %s", sConvar, sValue);
+
+ if(StrEqual(sConvar, cvar, caseSensitive))
+ {
+ Format(value, size, "%s", sConvar);
+
+ fFile.Close();
+ return AUTOEXEC_FIND_SUCCESS;
+ }
+ }
+
+ fFile.Close();
+ return AUTOEXEC_FIND_NOT_FOUND;
+ }
+
+
+ return AUTOEXEC_FIND_FILE_NOT_FOUND;
+}
+
+
+
+
+
+
+/**
+ * Cleans the global autoconfigfile from too much spaces
+ *
+ * @return One of the AUTOEXEC_CLEAN values.
+*/
+stock int AutoExecConfig_CleanFile()
+{
+ // No config set
+ if(strlen(g_sConfigFile) < 1)
+ {
+ return AUTOEXEC_NO_CONFIG;
+ }
+
+
+ char sfile[PLATFORM_MAX_PATH];
+ strcopy(sfile, sizeof(sfile), g_sConfigFile);
+
+
+ // Security
+ if(!FileExists(sfile))
+ {
+ return AUTOEXEC_CLEAN_FILE_NOT_FOUND;
+ }
+
+
+
+ char sfile2[PLATFORM_MAX_PATH];
+ Format(sfile2, sizeof(sfile2), "%s_tempcopy", sfile);
+
+
+ char readbuffer[2048];
+ int count;
+ bool firstreached;
+
+
+ // Open files
+ File fFile1 = OpenFile(sfile, "r");
+ File fFile2 = OpenFile(sfile2, "w");
+
+
+
+ // Check filehandles
+ if(fFile1 == null || fFile2 == null)
+ {
+ if(fFile1 != null)
+ {
+ //PrintToServer("Handle1 invalid");
+ fFile1.Close();
+ }
+
+ if(fFile2 != null)
+ {
+ //PrintToServer("Handle2 invalid");
+ fFile2.Close();
+ }
+
+ return AUTOEXEC_CLEAN_BAD_HANDLE;
+ }
+
+
+
+ while(!fFile1.EndOfFile() && fFile1.ReadLine(readbuffer, sizeof(readbuffer)))
+ {
+ // Is space
+ if(IsCharSpace(readbuffer[0]))
+ {
+ count++;
+ }
+ // No space, count from start
+ else
+ {
+ count = 0;
+ }
+
+
+ // Don't write more than 1 space if seperation after informations have been reached
+ if(count < 2 || !firstreached)
+ {
+ ReplaceString(readbuffer, sizeof(readbuffer), "\n", "");
+ fFile2.WriteLine(readbuffer);
+ }
+
+
+ // First bigger seperation after informations has been reached
+ if(count == 2)
+ {
+ firstreached = true;
+ }
+ }
+
+
+ fFile1.Close();
+ fFile2.Close();
+
+
+ // This might be a risk, for now it works
+ DeleteFile(sfile);
+ RenameFile(sfile, sfile2);
+
+ return AUTOEXEC_CLEAN_SUCCESS;
+}
+
+
+
+
+
+
+/**
+ * Returns how many times the given char occures in the given string.
+ *
+ * @param str String to search for in.
+ * @return Occurences of the given char found in string.
+*/
+stock static int GetCharCountInStr(int character, const char[] str)
+{
+ int len = strlen(str);
+ int count;
+
+ for(int i; i < len; i++)
+ {
+ if(str[i] == character)
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
diff --git a/calladmin_restrictions/scripting/include/calladmin.inc b/_CallAdmin/scripting/include/calladmin.inc
similarity index 100%
rename from calladmin_restrictions/scripting/include/calladmin.inc
rename to _CallAdmin/scripting/include/calladmin.inc
diff --git a/_CallAdmin/scripting/include/calladmin_stocks.inc b/_CallAdmin/scripting/include/calladmin_stocks.inc
new file mode 100644
index 00000000..34ca323d
--- /dev/null
+++ b/_CallAdmin/scripting/include/calladmin_stocks.inc
@@ -0,0 +1,18 @@
+/** Include guard */
+#if defined _calladmin_stocks_included
+ #endinput
+#endif
+#define _calladmin_stocks_included
+
+
+
+
+stock bool IsClientValid(int id)
+{
+ if (id > 0 && id <= MaxClients && IsClientInGame(id))
+ {
+ return true;
+ }
+
+ return false;
+}
\ No newline at end of file
diff --git a/_CallAdmin/scripting/include/calladmin_usermanager.inc b/_CallAdmin/scripting/include/calladmin_usermanager.inc
new file mode 100644
index 00000000..d2161f3f
--- /dev/null
+++ b/_CallAdmin/scripting/include/calladmin_usermanager.inc
@@ -0,0 +1,83 @@
+/** Include guard */
+#if defined _calladmin_usermanager_included
+ #endinput
+#endif
+#define _calladmin_usermanager_included
+
+
+
+
+/**
+ * Returns whether a client is immune or not.
+ * If a client is immune, he can't be reported.
+ *
+ * @param client The Client.
+ * @return True if client is immune, false otherwise.
+ */
+native bool CallAdmin_IsClientImmune(int client);
+
+
+
+
+/**
+ * Returns whether a client is on the blacklist or not.
+ * If a client is on the blacklist, he can't report players.
+ *
+ * @param client The Client.
+ * @return True if client is on blacklist, false otherwise.
+ */
+native bool CallAdmin_IsClientOnBlacklist(int client);
+
+
+
+
+/**
+ * Sets whether a client is immune or not.
+ * If a client is immune, he can't be reported.
+ *
+ * @param client The Client.
+ * @param immune True to immune client, false otherwise.
+ * @noreturn
+ */
+native void CallAdmin_SetClientImmune(int client, bool immune);
+
+
+
+
+/**
+ * Sets whether a client is in the backlist or not.
+ * If a client is on the blacklist, he can't report players.
+ *
+ * @param client The Client.
+ * @param add True to add client to blacklist, false to remove.
+ * @noreturn
+ */
+native void CallAdmin_SetClientOnBlacklist(int client, bool add);
+
+
+
+
+/* Do not edit below this line */
+public SharedPlugin __pl_calladmin_usermanager =
+{
+ name = "calladmin_usermanager",
+ file = "calladmin_usermanager.smx",
+#if defined REQUIRE_PLUGIN
+ required = 1,
+#else
+ required = 0,
+#endif
+};
+
+
+
+
+#if !defined REQUIRE_PLUGIN
+public __pl_calladmin_SetNTVOptional()
+{
+ MarkNativeAsOptional("CallAdmin_SetClientOnBlacklist");
+ MarkNativeAsOptional("CallAdmin_SetClientImmune");
+ MarkNativeAsOptional("CallAdmin_IsClientOnBlacklist");
+ MarkNativeAsOptional("CallAdmin_IsClientImmune");
+}
+#endif
diff --git a/_CallAdmin/scripting/translations/calladmin.phrases.txt b/_CallAdmin/scripting/translations/calladmin.phrases.txt
new file mode 100644
index 00000000..06236b79
--- /dev/null
+++ b/_CallAdmin/scripting/translations/calladmin.phrases.txt
@@ -0,0 +1,168 @@
+"Phrases"
+{
+ "CallAdmin_CommandNotAllowed"
+ {
+ "#format" "{1:d}"
+ "en" "You are not allowed to use this command for the next {1} seconds"
+ "de" "Du darfst diesen Befehl nicht innerhalb der nächsten {1} Sekunden benutzen"
+ "fr" "Vous ne pouvez pas utiliser cette commande avant {1} secondes"
+ "ru" "Вы не можете использовать эту команду в течение следующих {1} секунд"
+ }
+ "CallAdmin_HasReported"
+ {
+ "#format" "{1:N},{2:N},{3:s}"
+ "en" "{1} reported {2} for {3}"
+ "de" "{1} hat {2} wegen {3} gemeldet"
+ "fr" "{1} a signalé {2} pour {3}"
+ "ru" "{1} пожаловался на {2} по причине {3}"
+ }
+ "CallAdmin_YouHaveReported"
+ {
+ "#format" "{1:N},{2:s}"
+ "en" "You have reported {1} for {2}"
+ "de" "Du hast {1} wegen {2} gemeldet"
+ "fr" "Vous avez signalé {1} pour {2}"
+ "ru" "Вы успешно пожаловались на {1} по причине {2}"
+ }
+ "CallAdmin_NoPlayers"
+ {
+ "en" "There are no players you can report at this time"
+ "de" "Es sind derzeit keine Spieler vorhanden die du melden kannst"
+ "fr" "Aucun joueur ne peut être signalé pour le moment"
+ "ru" "В настоящее время на сервере нет игроков, на которых вы можете пожаловаться"
+ }
+ "CallAdmin_NotInGame"
+ {
+ "en" "Player is not on the server anymore"
+ "de" "Spieler ist nicht mehr auf dem Server"
+ "fr" "Le joueur n'est plus sur le serveur"
+ "ru" "Игрок уже покинул данный сервер"
+ }
+ "CallAdmin_SelectClient"
+ {
+ "en" "Select a client to report"
+ "de" "Wähle einen Spieler zum Melden"
+ "fr" "Sélectionnez un joueur à signaler"
+ "ru" "Выберите игрока, на которого вы хотите пожаловаться"
+ }
+ "CallAdmin_SelectReason"
+ {
+ "#format" "{1:N}"
+ "en" "Select a reason to report {1}"
+ "de" "Wähle einen Grund um {1} zu melden"
+ "fr" "Sélectionnez une raison pour signaler {1}"
+ "ru" "Выберите причину, по которой вы хотите пожаловаться на {1}"
+ }
+ // Used when exactly 1 admin is available
+ "CallAdmin_AdvertMessageSingular"
+ {
+ "#format" "{1:d}"
+ "en" "There is {1} admin available, type !call or /call in chat to report a player"
+ "de" "Es ist {1} Admin verfügbar, tippe !call oder /call in den Chat um einen Spieler zu melden"
+ "fr" "Il y a {1} admin disponible , tapez !call ou /call dans le chat pour signaler un joueur"
+ "ru" "Сейчас на сервере находится {1} администратор. Напишите в чате !call или /call, чтобы пожаловаться ему на игрока"
+ }
+ "CallAdmin_AdvertMessagePlural"
+ {
+ "#format" "{1:d}"
+ "en" "There are {1} admins available, type !call or /call in chat to report a player"
+ "de" "Es sind {1} Admins verfügbar, tippe !call oder /call in den Chat um einen Spieler zu melden"
+ "fr" "Il y a {1} admins disponibles , tapez !call ou /call dans le chat pour signaler un joueur"
+ "ru" "Администраторов на сервере в настоящее время: {1}. Напишите в чате !call или /call, чтобы пожаловаться на игрока"
+ }
+ "CallAdmin_OwnReason"
+ {
+ "en" "Own reason"
+ "de" "Eigener Grund"
+ "fr" "Raison personnelle"
+ "ru" "Другая причина"
+ }
+ "CallAdmin_TypeOwnReason"
+ {
+ "en" "You now can type your own banreason in the chat, please keep it short\nType '!noreason' or '!abort' to abort"
+ "de" "Du kannst nun deinen eigenen Bangrund in den Chat schreiben, fasse dich bitte kurz\nSchreibe '!noreason' oder '!abort' um abzubrechen"
+ "fr" "Vous pouvez maintenant écrire une raison personnelle dans le chat , écrivez une raison abrégée s'il vous plait\nEcrivez '!noreason' ou '!abort' pour annuler"
+ "ru" "Сейчас вы можете указать свою причину в чате. Будьте лаконичнее\nВведите '!noreason' или '!abort' для отмены"
+ }
+ "CallAdmin_CallAborted"
+ {
+ "en" "Your call has been aborted"
+ "de" "Der Meldevorgang wurde abgebrochen"
+ "fr" "Votre appel à été annulé"
+ "ru" "Ваша жалоба была отменена"
+ }
+ "CallAdmin_OwnReasonTooShort"
+ {
+ "en" "Your banreason is too short, please try it again"
+ "de" "Dein Bangrund ist zu kurz, bitte versuche es erneut"
+ "fr" "La raison du ban est trop courte , veuillez réessayer"
+ "ru" "Вы указали слишком короткую причину жалобы. Попробуйте ещё раз"
+ }
+ "CallAdmin_ConfirmCall"
+ {
+ "en" "Are you sure you want to call an admin?, abuse will be punished"
+ "de" "Bist du sicher dass du einen Admin rufen willst?, Missbrauch wird bestraft"
+ "fr" "Voulez-vous vraiment appeler un admin ?, les abus seront sanctionnés"
+ "ru" "Вы уверены, что хотите отправить жалобу администратору? Помните, что существует наказание за ложные вызовы"
+ }
+ "CallAdmin_Yes"
+ {
+ "en" "Yes"
+ "de" "Ja"
+ "fr" "Oui"
+ "ru" "Да"
+ }
+ "CallAdmin_No"
+ {
+ "en" "No"
+ "de" "Nein"
+ "fr" "Non"
+ "ru" "Нет"
+ }
+ "CallAdmin_AlreadyReported"
+ {
+ "en" "Player was already reported"
+ "de" "Spieler wurde bereits gemeldet"
+ "fr" "Joueur déjà signalé"
+ "ru" "На этого игрока уже пожаловались"
+ }
+ "CallAdmin_IngameAdminNotified"
+ {
+ "en" "An admin in-game was notified about your report"
+ "de" "Ein Admin in-game wurde über deine Meldung benachrichtigt"
+ "fr" "Un admin en jeu a été notifié de votre signalement"
+ "ru" "Администраторы, находящиеся на сервере, были проинформированы о вашей жалобе"
+ }
+ "CallAdmin_AdminNotification"
+ {
+ "#format" "{1:N},{2:N},{3:s}"
+ "en" "Player {1} reported {2} for {3}"
+ "de" "Spieler {1} hat {2} wegen {3} gemeldet"
+ "fr" "Le joueur {1} à signalé {2} pour {3}"
+ "ru" "Игрок {1} пожаловался на {2} по причине {3}"
+ }
+ "CallAdmin_ReportAlreadyHandled"
+ {
+ "en" "This report was already handled"
+ "de" "Diese Meldung wurde bereits erledigt"
+ "ru" "Эта жалоба уже была рассмотрена"
+ }
+ "CallAdmin_NoAdmin"
+ {
+ "en" "Only admins have access to this command"
+ "de" "Nur Admins können diesen Befehl ausführen"
+ "ru" "Только администраторы имеют доступ к этой команде"
+ }
+ "CallAdmin_WrongNumberOfArguments"
+ {
+ "en" "You've used a wrong number of arguments"
+ "de" "Du hast eine falsche Anzahl an Argumenten benutzt"
+ "ru" "Указано неправильное количество аргументов"
+ }
+ "CallAdmin_WrongReportID"
+ {
+ "en" "You've used a wrong report id"
+ "de" "Du hast eine falsche Melde-id benutzt"
+ "ru" "Указан неправильный ID жалобы"
+ }
+}
diff --git a/_CallAdmin/scripting/translations/calladmin_usermanager.phrases.txt b/_CallAdmin/scripting/translations/calladmin_usermanager.phrases.txt
new file mode 100644
index 00000000..51e6b12e
--- /dev/null
+++ b/_CallAdmin/scripting/translations/calladmin_usermanager.phrases.txt
@@ -0,0 +1,34 @@
+"Phrases"
+{
+ "CallAdmin_ClientBlacklistMute"
+ {
+ "en" "You are now, because of muting, on the blacklist. You can't report players anymore!"
+ "de" "Aufgrund eines Mutes bist du nun auf der Blacklist. Du kannst jetzt keine Spieler mehr reporten!"
+ "ru" "Из-за того, что вам был отключён микрофон, вы попали в чёрный список и больше не можете жаловаться на других игроков!"
+ }
+ "CallAdmin_ClientBlacklistGag"
+ {
+ "en" "You are now, because of gagging, on the blacklist. You can't report players anymore!"
+ "de" "Aufgrund eines Maulkorbes bist du nun auf der Blacklist. Du kannst jetzt keine Spieler mehr reporten!"
+ "ru" "Из-за того, что вам был отключён чат, вы попали в чёрный список и больше не можете жаловаться на других игроков!"
+ }
+ "CallAdmin_ClientBlacklistRemove"
+ {
+ "en" "You got removed from blacklist. You can report players again!"
+ "de" "Du wurdest von der Blacklist entfernt. Du kannst jetzt wieder Spieler reporten!"
+ "ru" "Вы были удалены из чёрного списка и теперь снова можете жаловаться на других игроков!"
+ }
+ "CallAdmin_ClientOnBlacklist"
+ {
+ "en" "You are currently on the blacklist. You can't report players!"
+ "de" "Du bist zurzeit auf der Blacklist. Du kannst keine Spieler reporten!"
+ "ru" "Вы находитесь в чёрном списке и не можете жаловаться на других игроков!"
+ }
+ "CallAdmin_TargetImmune"
+ {
+ "#format" "{1:N}"
+ "en" "Player {1} is immune. You can't report him."
+ "de" "Spieler {1} ist Immun. Du kannst ihn nicht reporten!"
+ "ru" "У игрока {1} есть иммунитет. Вы не можете жаловаться на него."
+ }
+}
\ No newline at end of file