AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   SourceBans / SourceBans++ (https://forums.alliedmods.net/forumdisplay.php?f=152)
-   -   sbpp_main compile problem (https://forums.alliedmods.net/showthread.php?t=340111)

Elit59 10-25-2022 08:06

sbpp_main compile problem
 
hi.

Code:

SourcePawn Compiler 1.11.0.6915
Copyright (c) 1997-2006 ITB CompuPhase 
Copyright (c) 2004-2021 AlliedModders LLC

d:\sm1.11-3\scripting\sbpp_main.sp(785) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(785) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(785) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(831) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(879) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(2316) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(2316) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(2730) : warning 246: function CreateFlagLetters returns an array but return type is not marked as an array
d:\sm1.11-3\scripting\sbpp_main.sp(2737) : warning 234: symbol "GetMaxClients" is marked as deprecated: Use MaxClients variable instead.
Code size:        69816 bytes
Data size:        19276 bytes
Stack/heap size:      19628 bytes
Total requirements:  108720 bytes

9 Warnings.

Code:

// *************************************************************************
//  This file is part of SourceBans++.
//
//  Copyright (C) 2014-2016 SourceBans++ Dev Team <https://github.com/sbpp>
//
//  SourceBans++ 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, per version 3 of the License.
//
//  SourceBans++ 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 SourceBans++. If not, see <http://www.gnu.org/licenses/>.
//
//  This file is based off work(s) covered by the following copyright(s):
//
//  SourceBans 1.4.11
//  Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect
//  Licensed under GNU GPL version 3, or later.
//  Page: <http://www.sourcebans.net/> - <https://github.com/GameConnect/sourcebansv1>
//
// *************************************************************************

#pragma semicolon 1
#include <sourcemod>
#include <sourcebanspp>

#undef REQUIRE_PLUGIN
#include <adminmenu>
#tryinclude <updater>

#define SB_VERSION "1.6.4"
#define SBR_VERSION "1.6.4"

#if defined _updater_included
#define UPDATE_URL "https://sbpp.github.io/updater/updatefile.txt"
#endif

//GLOBAL DEFINES
#define YELLOW                                0x01
#define NAMECOLOR                        0x02
#define TEAMCOLOR                        0x03
#define GREEN                                0x04

#define DISABLE_ADDBAN                1
#define DISABLE_UNBAN                2

#define FLAG_LETTERS_SIZE 26

//#define DEBUG

enum State/* ConfigState */
{
        ConfigStateNone = 0,
        ConfigStateConfig,
        ConfigStateReasons,
        ConfigStateHacking,
        ConfigStateTime
}

State ConfigState;

#define Prefix "[SourceBans++] "

/* Admin Stuff*/
AdminCachePart loadPart;

AdminFlag g_FlagLetters[FLAG_LETTERS_SIZE];

/* Cvar handle*/
ConVar
        CvarHostIp
        , CvarPort;

/* Database handle */
Database DB;
Database SQLiteDB;

char
        ServerIp[24]
        , ServerPort[7]
        , DatabasePrefix[10] = "sb"
        , WebsiteAddress[128]
        , groupsLoc[128] /* Admin KeyValues */
        , adminsLoc[128]
        , overridesLoc[128]
        , logFile[256]; /* Log Stuff */

float RetryTime = 15.0;

bool
        loadAdmins /* Admin Stuff*/
        , loadGroups
        , loadOverrides
        , LateLoaded
        , AutoAdd
        , g_bConnecting = false
        , requireSiteLogin = false /* Require a lastvisited from SB site */
        , backupConfig = true
        , enableAdmins = true
        , PlayerStatus[MAXPLAYERS + 1]; /* Player ban check status */

int
        g_BanTarget[MAXPLAYERS + 1] =  { -1, ... }
        , g_BanTime[MAXPLAYERS + 1] =  { -1, ... }
        , curLoading
        , serverID = -1
        , ProcessQueueTime = 5
        , g_ownReasons[MAXPLAYERS + 1] =  { false, ... } /* Own Chat Reason */
        , CommandDisable; /* Disable of addban and unban */

Handle
        ConfigParser
        , hTopMenu = INVALID_HANDLE
        , TimeMenuHandle /* Menu file globals */
        , ReasonMenuHandle
        , HackingMenuHandle
        , g_hFwd_OnBanAdded
        , g_hFwd_OnReportAdded
        , PlayerRecheck[MAXPLAYERS + 1] =  { INVALID_HANDLE, ... } /* Datapack and Timer handles */
        , PlayerDataPack[MAXPLAYERS + 1] =  { INVALID_HANDLE, ... };

public Plugin myinfo =
{
        name = "SourceBans++: Main Plugin",
        author = "SourceBans Development Team, SourceBans++ Dev Team",
        description = "Advanced ban management for the Source engine",
        version = SBR_VERSION,
        url = "https://sbpp.github.io"
};

#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
#else
public bool AskPluginLoad(Handle myself, bool late, char[] error, int err_max)
#endif
{
        RegPluginLibrary("sourcebans++");

        CreateNative("SBBanPlayer", Native_SBBanPlayer);
        CreateNative("SBPP_BanPlayer", Native_SBBanPlayer);
        CreateNative("SBPP_ReportPlayer", Native_SBReportPlayer);

        g_hFwd_OnBanAdded = CreateGlobalForward("SBPP_OnBanPlayer", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_String);
        g_hFwd_OnReportAdded = CreateGlobalForward("SBPP_OnReportPlayer", ET_Ignore, Param_Cell, Param_Cell, Param_String);

        LateLoaded = late;

        #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
        return APLRes_Success;
        #else
        return true;
        #endif
}

public OnPluginStart()
{
        LoadTranslations("common.phrases");
        LoadTranslations("plugin.basecommands");
        LoadTranslations("sourcebans.phrases");
        LoadTranslations("basebans.phrases");
        loadAdmins = loadGroups = loadOverrides = false;

        CvarHostIp = FindConVar("hostip");
        CvarPort = FindConVar("hostport");
        CreateConVar("sb_version", SB_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY);
        CreateConVar("sbr_version", SBR_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY);
        RegServerCmd("sm_rehash", sm_rehash, "Reload SQL admins");
        RegAdminCmd("sm_ban", CommandBan, ADMFLAG_BAN, "sm_ban <#userid|name> <minutes|0> [reason]", "sourcebans");
        RegAdminCmd("sm_banip", CommandBanIp, ADMFLAG_BAN, "sm_banip <ip|#userid|name> <time> [reason]", "sourcebans");
        RegAdminCmd("sm_addban", CommandAddBan, ADMFLAG_RCON, "sm_addban <time> <steamid> [reason]", "sourcebans");
        RegAdminCmd("sm_unban", CommandUnban, ADMFLAG_UNBAN, "sm_unban <steamid|ip> [reason]", "sourcebans");
        RegAdminCmd("sb_reload",
                _CmdReload,
                ADMFLAG_RCON,
                "Reload sourcebans config and ban reason menu options",
                "sourcebans");

        RegConsoleCmd("say", ChatHook);
        RegConsoleCmd("say_team", ChatHook);

        if ((TimeMenuHandle = CreateMenu(MenuHandler_BanTimeList, MenuAction_Select|MenuAction_Cancel|MenuAction_DrawItem)) != INVALID_HANDLE)
        {
                SetMenuPagination(TimeMenuHandle, 8);
                SetMenuExitBackButton(TimeMenuHandle, true);
        }

        if ((ReasonMenuHandle = CreateMenu(ReasonSelected)) != INVALID_HANDLE)
        {
                SetMenuPagination(ReasonMenuHandle, 8);
                SetMenuExitBackButton(ReasonMenuHandle, true);
        }

        if ((HackingMenuHandle = CreateMenu(HackingSelected)) != INVALID_HANDLE)
        {
                SetMenuPagination(HackingMenuHandle, 8);
                SetMenuExitBackButton(HackingMenuHandle, true);
        }

        g_FlagLetters = CreateFlagLetters();

        BuildPath(Path_SM, logFile, sizeof(logFile), "logs/sourcebans.log");
        g_bConnecting = true;

        // Catch config error and show link to FAQ
        if (!SQL_CheckConfig("sourcebans"))
        {
                if (ReasonMenuHandle != INVALID_HANDLE)
                        CloseHandle(ReasonMenuHandle);
                if (HackingMenuHandle != INVALID_HANDLE)
                        CloseHandle(HackingMenuHandle);
                LogToFile(logFile, "Database failure: Could not find Database conf \"sourcebans\". See Docs: https://sbpp.github.io/docs/");
                SetFailState("Database failure: Could not find Database conf \"sourcebans\"");
                return;
        }

        Database.Connect(GotDatabase, "sourcebans");

        BuildPath(Path_SM, groupsLoc, sizeof(groupsLoc), "configs/sourcebans/sb_admin_groups.cfg");

        BuildPath(Path_SM, adminsLoc, sizeof(adminsLoc), "configs/sourcebans/sb_admins.cfg");

        BuildPath(Path_SM, overridesLoc, sizeof(overridesLoc), "configs/sourcebans/overrides_backup.cfg");

        InitializeBackupDB();

        // This timer is what processes the SQLite queue when the database is unavailable
        CreateTimer(float(ProcessQueueTime * 60), ProcessQueue);

        if (LateLoaded)
        {
                AccountForLateLoading();
        }

        #if defined _updater_included
        if (LibraryExists("updater"))
        {
                Updater_AddPlugin(UPDATE_URL);
        }
        #endif
}

#if defined _updater_included
public void OnLibraryAdded(const char[] name)
{
        if (StrEqual(name, "updater"))
        {
                Updater_AddPlugin(UPDATE_URL);
        }
}
#endif

public void OnAllPluginsLoaded()
{
        Handle topmenu;
        #if defined DEBUG
        LogToFile(logFile, "OnAllPluginsLoaded()");
        #endif

        if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
        {
                OnAdminMenuReady(topmenu);
        }
}

public void OnConfigsExecuted()
{
        char filename[200];
        BuildPath(Path_SM, filename, sizeof(filename), "plugins/basebans.smx");
        if (FileExists(filename))
        {
                char newfilename[200];
                BuildPath(Path_SM, newfilename, sizeof(newfilename), "plugins/disabled/basebans.smx");
                ServerCommand("sm plugins unload basebans");
                if (FileExists(newfilename))
                        DeleteFile(newfilename);
                RenameFile(newfilename, filename);
                LogToFile(logFile, "plugins/basebans.smx was unloaded and moved to plugins/disabled/basebans.smx");
        }
}

public void OnMapStart()
{
        ResetSettings();
}

public OnMapEnd()
{
        for (int i = 0; i <= MaxClients; i++)
        {
                if (PlayerDataPack[i] != INVALID_HANDLE)
                {
                        /* Need to close reason pack */
                        CloseHandle(PlayerDataPack[i]);
                        PlayerDataPack[i] = INVALID_HANDLE;
                }
        }
}

// CLIENT CONNECTION FUNCTIONS //

public Action OnClientPreAdminCheck(int client)
{
        if (!DB || GetUserAdmin(client) != INVALID_ADMIN_ID)
                return Plugin_Continue;

        return curLoading > 0 ? Plugin_Handled : Plugin_Continue;
}

public OnClientDisconnect(int client)
{
        if (PlayerRecheck[client] != INVALID_HANDLE)
        {
                KillTimer(PlayerRecheck[client]);
                PlayerRecheck[client] = INVALID_HANDLE;
        }
        g_ownReasons[client] = false;
}

public bool OnClientConnect(int client, char[] rejectmsg, int maxlen)
{
        PlayerStatus[client] = false;
        return true;
}

public void OnClientAuthorized(int client, const char[] auth)
{
        /* Do not check bots nor check player with lan steamid. */
        if (auth[0] == 'B' || auth[9] == 'L' || DB == INVALID_HANDLE)
        {
                PlayerStatus[client] = true;
                return;
        }

        char Query[256], ip[30];

        GetClientIP(client, ip, sizeof(ip));

        FormatEx(Query, sizeof(Query), "SELECT bid, ip FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", DatabasePrefix, auth[8], ip);

        #if defined DEBUG
        LogToFile(logFile, "Checking ban for: %s", auth);
        #endif

        DB.Query(VerifyBan, Query, GetClientUserId(client), DBPrio_High);
}

public void OnRebuildAdminCache(AdminCachePart part)
{
        loadPart = part;
        switch (loadPart)
        {
                case AdminCache_Overrides:
                loadOverrides = true;
                case AdminCache_Groups:
                loadGroups = true;
                case AdminCache_Admins:
                loadAdmins = true;
        }
        if (DB == INVALID_HANDLE) {
                if (!g_bConnecting) {
                        g_bConnecting = true;
                        Database.Connect(GotDatabase, "sourcebans");
                }
        }
        else {
                GotDatabase(DB, "", 0);
        }
}

// COMMAND CODE //

public Action ChatHook(int client, int args)
{
        // is this player preparing to ban someone
        if (g_ownReasons[client])
        {
                // get the reason
                char reason[512];
                GetCmdArgString(reason, sizeof(reason));
                StripQuotes(reason);

                g_ownReasons[client] = false;

                if (StrEqual(reason[0], "!noreason"))
                {
                        PrintToChat(client, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Chat Reason Aborted");
                        return Plugin_Handled;
                }

                // ban him!
                PrepareBan(client, g_BanTarget[client], g_BanTime[client], reason, sizeof(reason));

                // block the reason to be sent in chat
                return Plugin_Handled;
        }
        return Plugin_Continue;
}

public Action _CmdReload(int client, int args)
{
        ResetSettings();
        return Plugin_Handled;
}

public Action CommandBan(int client, int args)
{
        if (args < 2)
        {
                ReplyToCommand(client, "%sUsage: sm_ban <#userid|name> <time|0> [reason]", Prefix);
                return Plugin_Handled;
        }

        // This is mainly for me sanity since client used to be called admin and target used to be called client
        int admin = client;

        // Get the target, find target returns a message on failure so we do not
        char buffer[100];

        GetCmdArg(1, buffer, sizeof(buffer));

        int  target = FindTarget(client, buffer, true);

        if (target == -1)
        {
                return Plugin_Handled;
        }

        // Get the ban time
        GetCmdArg(2, buffer, sizeof(buffer));

        int time = StringToInt(buffer);

        if (!time && client && !(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)))
        {
                ReplyToCommand(client, "You do not have Perm Ban Permission");
                return Plugin_Handled;
        }

        // Get the reason
        char reason[128];

        if (args >= 3)
        {
                GetCmdArg(3, reason, sizeof(reason));
                for (new i = 4; i <= args; i++)
                {
                        GetCmdArg(i, buffer, sizeof(buffer));
                        Format(reason, sizeof(reason), "%s %s", reason, buffer);
                }
        }
        else
        {
                reason[0] = '\0';
        }

        g_BanTarget[client] = target;
        g_BanTime[client] = time;

        if (!PlayerStatus[target])
        {
                // The target has not been banned verify. It must be completed before you can ban anyone.
                ReplyToCommand(admin, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Ban Not Verified");
                return Plugin_Handled;
        }


        CreateBan(client, target, time, reason);
        return Plugin_Handled;
}

public Action CommandBanIp(int client, int args)
{
        if (args < 2)
        {
                ReplyToCommand(client, "%sUsage: sm_banip <ip|#userid|name> <time> [reason]", Prefix);
                return Plugin_Handled;
        }

        int len, next_len;
        char Arguments[256], arg[50], time[20];

        GetCmdArgString(Arguments, sizeof(Arguments));
        len = BreakString(Arguments, arg, sizeof(arg));

        if ((next_len = BreakString(Arguments[len], time, sizeof(time))) != -1)
        {
                len += next_len;
        }
        else
        {
                len = 0;
                Arguments[0] = '\0';
        }

        char target_name[MAX_TARGET_LENGTH];
        int target_list[1];
        bool tn_is_ml;

        int target = -1;

        if (ProcessTargetString(
                        arg,
                        client,
                        target_list,
                        1,
                        COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_MULTI,
                        target_name,
                        sizeof(target_name),
                        tn_is_ml) > 0)
        {
                target = target_list[0];

                if (!IsFakeClient(target) && CanUserTarget(client, target))
                        GetClientIP(target, arg, sizeof(arg));
        }

        char adminIp[24], adminAuth[64];

        int minutes = StringToInt(time);

        if (!minutes && client && !(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)))
        {
                ReplyToCommand(client, "You do not have Perm Ban Permission");
                return Plugin_Handled;
        }
        if (!client)
        {
                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
                strcopy(adminIp, sizeof(adminIp), ServerIp);
        } else {
                GetClientIP(client, adminIp, sizeof(adminIp));
                GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        dataPack.WriteCell(client);
        dataPack.WriteCell(minutes);
        dataPack.WriteString(Arguments[len]);
        dataPack.WriteString(arg);
        dataPack.WriteString(adminAuth);
        dataPack.WriteString(adminIp);

        char sQuery[256];

        FormatEx(sQuery, sizeof(sQuery), "SELECT bid FROM %s_bans WHERE type = 1 AND ip    = '%s' AND (length = 0 OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL",
                DatabasePrefix, arg);

        SQL_TQuery(DB, SelectBanIpCallback, sQuery, dataPack, DBPrio_High);

        return Plugin_Handled;
}

public Action CommandUnban(int client, int args)
{
        if (args < 1)
        {
                ReplyToCommand(client, "%sUsage: sm_unban <steamid|ip> [reason]", Prefix);
                return Plugin_Handled;
        }

        if (CommandDisable & DISABLE_UNBAN)
        {
                // They must go to the website to unban people
                ReplyToCommand(client, "%s%t", Prefix, "Can Not Unban", WebsiteAddress);
                return Plugin_Handled;
        }

        int len;
        char Arguments[256], arg[50], adminAuth[64];

        GetCmdArgString(Arguments, sizeof(Arguments));

        if ((len = BreakString(Arguments, arg, sizeof(arg))) == -1)
        {
                len = 0;
                Arguments[0] = '\0';
        }
        if (!client)
        {
                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
        } else {
                GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        dataPack.WriteCell(client);
        dataPack.WriteString(Arguments[len]);
        dataPack.WriteString(arg);
        dataPack.WriteString(adminAuth);

        char query[200];

        if (strncmp(arg, "STEAM_", 6) == 0)
        {
                Format(query, sizeof(query), "SELECT bid FROM %s_bans WHERE (type = 0 AND authid = '%s') AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", DatabasePrefix, arg);
        } else {
                Format(query, sizeof(query), "SELECT bid FROM %s_bans WHERE (type = 1 AND ip    = '%s') AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", DatabasePrefix, arg);
        }

        SQL_TQuery(DB, SelectUnbanCallback, query, dataPack);

        return Plugin_Handled;
}

public Action CommandAddBan(int client, int args)
{
        if (args < 2)
        {
                ReplyToCommand(client, "%sUsage: sm_addban <time> <steamid> [reason]", Prefix);
                return Plugin_Handled;
        }

        if (CommandDisable & DISABLE_ADDBAN)
        {
                // They must go to the website to add bans
                ReplyToCommand(client, "%s%t", Prefix, "Can Not Add Ban", WebsiteAddress);
                return Plugin_Handled;
        }

        char arg_string[256], time[50], authid[50];

        GetCmdArgString(arg_string, sizeof(arg_string));

        int len, total_len;

        /* Get time */
        if ((len = BreakString(arg_string, time, sizeof(time))) == -1)
        {
                ReplyToCommand(client, "%sUsage: sm_addban <time> <steamid> [reason]", Prefix);
                return Plugin_Handled;
        }
        total_len += len;

        /* Get steamid */
        if ((len = BreakString(arg_string[total_len], authid, sizeof(authid))) != -1)
        {
                total_len += len;
        }
        else
        {
                total_len = 0;
                arg_string[0] = '\0';
        }

        char adminIp[24], adminAuth[64];

        int  minutes = StringToInt(time);

        if (!minutes && client && !(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)))
        {
                ReplyToCommand(client, "You do not have Perm Ban Permission");
                return Plugin_Handled;
        }
        if (!client)
        {
                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
                strcopy(adminIp, sizeof(adminIp), ServerIp);
        } else {
                GetClientIP(client, adminIp, sizeof(adminIp));
                GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        dataPack.WriteCell(client);
        dataPack.WriteCell(minutes);
        dataPack.WriteString(arg_string[total_len]);
        dataPack.WriteString(authid);
        dataPack.WriteString(adminAuth);
        dataPack.WriteString(adminIp);

        char sQuery[256];

        FormatEx(sQuery, sizeof sQuery, "SELECT bid FROM %s_bans WHERE type = 0 AND authid = '%s' AND (length = 0 OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL",
                DatabasePrefix, authid);

        SQL_TQuery(DB, SelectAddbanCallback, sQuery, dataPack, DBPrio_High);

        return Plugin_Handled;
}

public Action sm_rehash(int args)
{
        if (enableAdmins)
                DumpAdminCache(AdminCache_Groups, true);
        DumpAdminCache(AdminCache_Overrides, true);
        return Plugin_Handled;
}



// MENU CODE //

public void OnAdminMenuReady(Handle topmenu)
{
        #if defined DEBUG
        LogToFile(logFile, "OnAdminMenuReady()");
        #endif

        /* Block us from being called twice */
        if (topmenu == hTopMenu)
        {
                return;
        }

        /* Save the Handle */
        hTopMenu = topmenu;

        /* Find the "Player Commands" category */
        TopMenuObject player_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_PLAYERCOMMANDS);

        if (player_commands != INVALID_TOPMENUOBJECT)
        {
                // just to avoid "unused variable 'res'" warning
                #if defined DEBUG
                TopMenuObject res = AddToTopMenu(hTopMenu,
                        "sm_ban",  // Name
                        TopMenuObject_Item,  // We are a submenu
                        AdminMenu_Ban,  // Handler function
                        player_commands,  // We are a submenu of Player Commands
                        "sm_ban",  // The command to be finally called (Override checks)
                        ADMFLAG_BAN); // What flag do we need to see the menu option
                char temp[125];
                Format(temp, 125, "Result of AddToTopMenu: %d", res);
                LogToFile(logFile, temp);
                LogToFile(logFile, "Added Ban option to admin menu");
                #else
                AddToTopMenu(hTopMenu,
                        "sm_ban",  // Name
                        TopMenuObject_Item,  // We are a submenu
                        AdminMenu_Ban,  // Handler function
                        player_commands,  // We are a submenu of Player Commands
                        "sm_ban",  // The command to be finally called (Override checks)
                        ADMFLAG_BAN); // What flag do we need to see the menu option
                #endif
        }
}

public void AdminMenu_Ban(Handle topmenu,
        TopMenuAction action,  // Action being performed
        TopMenuObject object_id,  // The object ID (if used)
        int param,  // client idx of admin who chose the option (if used)
        char[] buffer,  // Output buffer (if used)
        int maxlength) // Output buffer (if used)
{
        /* Clear the Ownreason bool, so he is able to chat again;) */
        g_ownReasons[param] = false;

        #if defined DEBUG
        LogToFile(logFile, "AdminMenu_Ban()");
        #endif

        switch (action)
        {
                // We are only being displayed, We only need to show the option name
                case TopMenuAction_DisplayOption:
                {
                        FormatEx(buffer, maxlength, "%T", "Ban player", param);

                        #if defined DEBUG
                        LogToFile(logFile, "AdminMenu_Ban() -> Formatted the Ban option text");
                        #endif
                }

                case TopMenuAction_SelectOption:
                {
                        DisplayBanTargetMenu(param); // Someone chose to ban someone, show the list of users menu

                        #if defined DEBUG
                        LogToFile(logFile, "AdminMenu_Ban() -> DisplayBanTargetMenu()");
                        #endif
                }
        }
}

public int ReasonSelected(Handle menu, MenuAction action, int param1, int param2)
{
        switch (action)
        {
                case MenuAction_Select:
                {
                        char info[128], key[128];

                        GetMenuItem(menu, param2, key, sizeof(key), _, info, sizeof(info));

                        if (StrEqual("Hacking", key))
                        {
                                DisplayMenu(HackingMenuHandle, param1, MENU_TIME_FOREVER);
                                return;
                        }

                        else if (StrEqual("Own Reason", key)) // admin wants to use his own reason
                        {
                                g_ownReasons[param1] = true;
                                PrintToChat(param1, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Chat Reason");
                                return;
                        }

                        else if (g_BanTarget[param1] != -1 && g_BanTime[param1] != -1)
                                PrepareBan(param1, g_BanTarget[param1], g_BanTime[param1], info, sizeof(info));
                }

                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_Disconnected)
                        {
                                if (PlayerDataPack[param1] != INVALID_HANDLE)
                                {
                                        CloseHandle(PlayerDataPack[param1]);
                                        PlayerDataPack[param1] = INVALID_HANDLE;
                                }
                        }

                        else
                        {
                                DisplayBanTimeMenu(param1);
                        }
                }
        }
}

public int HackingSelected(Handle menu, MenuAction action, int param1, int param2)
{
        switch (action)
        {
                case MenuAction_Select:
                {
                        char info[128], key[128];

                        GetMenuItem(menu, param2, key, sizeof(key), _, info, sizeof(info));

                        if (g_BanTarget[param1] != -1 && g_BanTime[param1] != -1)
                                PrepareBan(param1, g_BanTarget[param1], g_BanTime[param1], info, sizeof(info));
                }

                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_Disconnected)
                        {
                                DataPack Pack = view_as<DataPack>(PlayerDataPack[param1]);

                                if (Pack != INVALID_HANDLE)
                                {
                                        Pack.ReadCell(); // admin index
                                        Pack.ReadCell(); // target index
                                        Pack.ReadCell(); // admin userid
                                        Pack.ReadCell(); // target userid
                                        Pack.ReadCell(); // time

                                        DataPack ReasonPack = Pack.ReadCell();

                                        if (ReasonPack != INVALID_HANDLE)
                                        {
                                                CloseHandle(ReasonPack);
                                        }

                                        CloseHandle(Pack);
                                        PlayerDataPack[param1] = INVALID_HANDLE;
                                }
                        }

                        else
                        {
                                DisplayMenu(ReasonMenuHandle, param1, MENU_TIME_FOREVER);
                        }
                }
        }
}

public int MenuHandler_BanPlayerList(Handle menu, MenuAction action, int param1, int param2)
{
        #if defined DEBUG
        LogToFile(logFile, "MenuHandler_BanPlayerList()");
        #endif

        switch (action)
        {
                case MenuAction_End:
                {
                        CloseHandle(menu);
                }

                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
                        {
                                DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
                        }
                }

                case MenuAction_Select:
                {
                        char info[32], name[32];
                        int userid, target;

                        GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
                        userid = StringToInt(info);

                        if ((target = GetClientOfUserId(userid)) == 0)
                        {
                                PrintToChat(param1, "%s%t", Prefix, "Player no longer available");
                        }
                        else if (!CanUserTarget(param1, target))
                        {
                                PrintToChat(param1, "%s%t", Prefix, "Unable to target");
                        }
                        else
                        {
                                g_BanTarget[param1] = target;
                                DisplayBanTimeMenu(param1);
                        }
                }
        }
}

public int MenuHandler_BanTimeList(Handle menu, MenuAction action, int param1, int param2)
{
        #if defined DEBUG
        LogToFile(logFile, "MenuHandler_BanTimeList()");
        #endif

        switch (action)
        {
                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
                        {
                                DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
                        }
                }

                case MenuAction_Select:
                {
                        char info[32];

                        GetMenuItem(menu, param2, info, sizeof(info));
                        g_BanTime[param1] = StringToInt(info);

                        //DisplayBanReasonMenu(param1);
                        DisplayMenu(ReasonMenuHandle, param1, MENU_TIME_FOREVER);
                }

                case MenuAction_DrawItem:
                {
                        char time[16];

                        GetMenuItem(menu, param2, time, sizeof(time));

                        return (StringToInt(time) > 0 || CheckCommandAccess(param1, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
                }
        }

        return 0;
}

stock void DisplayBanTargetMenu(int client)
{
        #if defined DEBUG
        LogToFile(logFile, "DisplayBanTargetMenu()");
        #endif

        Handle menu = CreateMenu(MenuHandler_BanPlayerList); // Create a new menu, pass it the handler.

        char title[100];

        FormatEx(title, sizeof(title), "%T:", "Ban player", client);

        SetMenuTitle(menu, title); // Set the title
        SetMenuExitBackButton(menu, true); // Yes we want back/exit

        AddTargetsToMenu(menu,  // Add clients to our menu
                client,  // The client that called the display
                false,  // We want to see people connecting
                false); // And dead people

        DisplayMenu(menu, client, MENU_TIME_FOREVER); // Show the menu to the client FOREVER!
}

stock void DisplayBanTimeMenu(int client)
{
        #if defined DEBUG
        LogToFile(logFile, "DisplayBanTimeMenu()");
        #endif

        char title[100];
        FormatEx(title, sizeof(title), "%T:", "Ban player", client);
        SetMenuTitle(TimeMenuHandle, title);

        DisplayMenu(TimeMenuHandle, client, MENU_TIME_FOREVER);
}

stock void ResetMenu()
{
        if (TimeMenuHandle != INVALID_HANDLE)
        {
                RemoveAllMenuItems(TimeMenuHandle);
        }

        if (ReasonMenuHandle != INVALID_HANDLE)
        {
                RemoveAllMenuItems(ReasonMenuHandle);
        }

        if (HackingMenuHandle != INVALID_HANDLE)
        {
                RemoveAllMenuItems(HackingMenuHandle);
        }
}

// QUERY CALL BACKS //

public void GotDatabase(Database db, const char[] error, any data)
{
        if (db == INVALID_HANDLE)
        {
                LogToFile(logFile, "Database failure: %s. See Docs: https://sbpp.github.io/docs/", error);
                g_bConnecting = false;

                // Parse the overrides backup!
                ParseBackupConfig_Overrides();
                return;
        }

        DB = db;

        char query[1024];

        SQL_SetCharset(DB, "utf8");

        InsertServerInfo();

        //CreateTimer(900.0, PruneBans);

        if (loadOverrides)
        {
                Format(query, 1024, "SELECT type, name, flags FROM %s_overrides", DatabasePrefix);
                SQL_TQuery(DB, OverridesDone, query);
                loadOverrides = false;
        }

        if (loadGroups && enableAdmins)
        {
                FormatEx(query, 1024, "SELECT name, flags, immunity, groups_immune  \
                                        FROM %s_srvgroups ORDER BY id", DatabasePrefix);
                curLoading++;
                SQL_TQuery(DB, GroupsDone, query);

                #if defined DEBUG
                LogToFile(logFile, "Fetching Group List");
                #endif
                loadGroups = false;
        }

        if (loadAdmins && enableAdmins)
        {
                char queryLastLogin[50] = "";

                if (requireSiteLogin)
                        queryLastLogin = "lastvisit IS NOT NULL AND lastvisit != '' AND";

                if (serverID == -1)
                {
                        FormatEx(query, 1024, "SELECT authid, srv_password, (SELECT name FROM %s_srvgroups WHERE name = srv_group AND flags != '') AS srv_group, srv_flags, user, immunity  \
                                                FROM %s_admins_servers_groups AS asg \
                                                LEFT JOIN %s_admins AS a ON a.aid = asg.admin_id \
                                                WHERE %s (server_id = (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1)  \
                                                OR srv_group_id = ANY (SELECT group_id FROM %s_servers_groups WHERE server_id = (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1))) \
                                                GROUP BY aid, authid, srv_password, srv_group, srv_flags, user",
                                DatabasePrefix, DatabasePrefix, DatabasePrefix, queryLastLogin, DatabasePrefix, ServerIp, ServerPort, DatabasePrefix, DatabasePrefix, ServerIp, ServerPort);
                } else {
                        FormatEx(query, 1024, "SELECT authid, srv_password, (SELECT name FROM %s_srvgroups WHERE name = srv_group AND flags != '') AS srv_group, srv_flags, user, immunity  \
                                                FROM %s_admins_servers_groups AS asg \
                                                LEFT JOIN %s_admins AS a ON a.aid = asg.admin_id \
                                                WHERE %s server_id = %d  \
                                                OR srv_group_id = ANY (SELECT group_id FROM %s_servers_groups WHERE server_id = %d) \
                                                GROUP BY aid, authid, srv_password, srv_group, srv_flags, user",
                                DatabasePrefix, DatabasePrefix, DatabasePrefix, queryLastLogin, serverID, DatabasePrefix, serverID);
                }
                curLoading++;
                SQL_TQuery(DB, AdminsDone, query);

                #if defined DEBUG
                LogToFile(logFile, "Fetching Admin List");
                LogToFile(logFile, query);
                #endif
                loadAdmins = false;
        }
        g_bConnecting = false;
}

public VerifyInsert(Handle:owner, Handle:hndl, const String:error[], DataPack dataPack)
{
        if (dataPack == INVALID_HANDLE)
        {
                LogToFile(logFile, "Ban Failed: %s", error);
                return;
        }

        if (hndl == INVALID_HANDLE || error[0])
        {
                LogToFile(logFile, "Verify Insert Query Failed: %s", error);

                int admin = dataPack.ReadCell();
                dataPack.ReadCell(); // target
                dataPack.ReadCell(); // admin userid
                dataPack.ReadCell(); // target userid
                int time = dataPack.ReadCell();

                DataPack reasonPack = dataPack.ReadCell();

                char reason[128], name[50], auth[30], ip[20], adminAuth[30], adminIp[20];

                reasonPack.ReadString(reason, sizeof reason);

                dataPack.ReadString(name, sizeof name);
                dataPack.ReadString(auth, sizeof auth);
                dataPack.ReadString(ip, sizeof ip);
                dataPack.ReadString(adminAuth, sizeof adminAuth);
                dataPack.ReadString(adminIp, sizeof adminIp);

                dataPack.Reset();
                reasonPack.Reset();

                PlayerDataPack[admin] = INVALID_HANDLE;
                UTIL_InsertTempBan(time, name, auth, ip, reason, adminAuth, adminIp, dataPack);
                return;
        }

        int admin = dataPack.ReadCell();
        int client = dataPack.ReadCell();

        if (!IsClientConnected(client) || IsFakeClient(client))
                return;

        dataPack.ReadCell(); // admin userid

        int UserId = dataPack.ReadCell();
        int time = dataPack.ReadCell();

        DataPack ReasonPack = dataPack.ReadCell();

        char Name[64], Reason[128];

        dataPack.ReadString(Name, sizeof(Name));
        ReasonPack.ReadString(Reason, sizeof(Reason));

        if (!time)
        {
                if (Reason[0] == '\0')
                {
                        ShowActivityEx(admin, Prefix, "%t", "Permabanned player", Name);
                } else {
                        ShowActivityEx(admin, Prefix, "%t", "Permabanned player reason", Name, Reason);
                }
        } else {
                if (Reason[0] == '\0')
                {
                        ShowActivityEx(admin, Prefix, "%t", "Banned player", Name, time);
                } else {
                        ShowActivityEx(admin, Prefix, "%t", "Banned player reason", Name, time, Reason);
                }
        }

        LogAction(admin, client, "\"%L\" banned \"%L\" (minutes \"%d\") (reason \"%s\")", admin, client, time, Reason);

        if (PlayerDataPack[admin] != INVALID_HANDLE)
        {
                CloseHandle(PlayerDataPack[admin]);
                CloseHandle(ReasonPack);
                PlayerDataPack[admin] = INVALID_HANDLE;
        }

        // Kick player
        if (GetClientUserId(client) == UserId)
                KickClient(client, "%t", "Banned Check Site", WebsiteAddress);
}

public SelectBanIpCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, minutes, String:adminAuth[30], String:adminIp[30], String:banReason[256], String:ip[16], String:Query[512];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        minutes = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, ip, sizeof(ip));
        ReadPackString(data, adminAuth, sizeof(adminAuth));
        ReadPackString(data, adminIp, sizeof(adminIp));
        SQL_EscapeString(DB, reason, banReason, sizeof(banReason));

        if (error[0])
        {
                LogToFile(logFile, "Ban IP Select Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%sFailed to ban %s.", Prefix, ip);
                else
                        PrintToServer("%sFailed to ban %s.", Prefix, ip);
                return;
        }
        if (SQL_GetRowCount(hndl))
        {
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%s%s is already banned.", Prefix, ip);
                else
                        PrintToServer("%s%s is already banned.", Prefix, ip);
                return;
        }
        if (serverID == -1)
        {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (type, ip, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                (1, '%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), ' ')",
                        DatabasePrefix, ip, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, DatabasePrefix, ServerIp, ServerPort);
        } else {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (type, ip, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                (1, '%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                %d, ' ')",
                        DatabasePrefix, ip, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, serverID);
        }

        SQL_TQuery(DB, InsertBanIpCallback, Query, data, DBPrio_High);
}

public InsertBanIpCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        // if the pack is good unpack it and close the handle
        new admin, minutes;
        new String:reason[128];
        decl String:arg[30];
        if (data != INVALID_HANDLE)
        {
                ResetPack(data);
                admin = ReadPackCell(data);
                minutes = ReadPackCell(data);
                ReadPackString(data, reason, sizeof(reason));
                ReadPackString(data, arg, sizeof(arg));
                CloseHandle(data);
        } else {
                // Technically this should not be possible
                ThrowError("Invalid Handle in InsertBanIpCallback");
        }

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Ban IP Insert Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%ssm_banip failed", Prefix);
                return;
        }

        LogAction(admin,
                -1,
                "\"%L\" added ban (minutes \"%d\") (ip \"%s\") (reason \"%s\")",
                admin,
                minutes,
                arg,
                reason);
        if (admin && IsClientInGame(admin))
                PrintToChat(admin, "%s%s successfully banned", Prefix, arg);
        else
                PrintToServer("%s%s successfully banned", Prefix, arg);
}

public SelectUnbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, String:arg[30], String:adminAuth[30], String:unbanReason[256];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, arg, sizeof(arg));
        ReadPackString(data, adminAuth, sizeof(adminAuth));
        SQL_EscapeString(DB, reason, unbanReason, sizeof(unbanReason));

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Unban Select Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%ssm_unban failed", Prefix);
                }
                return;
        }

        // If there was no results then a ban does not exist for that id
        if (hndl == INVALID_HANDLE || !SQL_GetRowCount(hndl))
        {
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%sNo active bans found for that filter", Prefix);
                } else {
                        PrintToServer("%sNo active bans found for that filter", Prefix);
                }
                return;
        }

        // There is ban
        if (hndl != INVALID_HANDLE && SQL_FetchRow(hndl))
        {
                // Get the values from the existing ban record
                new bid = SQL_FetchInt(hndl, 0);

                decl String:query[1000];
                Format(query, sizeof(query), "UPDATE %s_bans SET RemovedBy = (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), RemoveType = 'U', RemovedOn = UNIX_TIMESTAMP(), ureason = '%s' WHERE bid = %d",
                        DatabasePrefix, DatabasePrefix, adminAuth, adminAuth[8], unbanReason, bid);

                SQL_TQuery(DB, InsertUnbanCallback, query, data);
        }
        return;
}

public InsertUnbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        // if the pack is good unpack it and close the handle
        decl admin, String:arg[30];
        new String:reason[128];
        if (data != INVALID_HANDLE)
        {
                ResetPack(data);
                admin = ReadPackCell(data);
                ReadPackString(data, reason, sizeof(reason));
                ReadPackString(data, arg, sizeof(arg));
                CloseHandle(data);
        } else {
                // Technically this should not be possible
                ThrowError("Invalid Handle in InsertUnbanCallback");
        }

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Unban Insert Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%ssm_unban failed", Prefix);
                }
                return;
        }

        LogAction(admin, -1, "\"%L\" removed ban (filter \"%s\") (reason \"%s\")", admin, arg, reason);
        if (admin && IsClientInGame(admin))
        {
                PrintToChat(admin, "%s%s successfully unbanned", Prefix, arg);
        } else {
                PrintToServer("%s%s successfully unbanned", Prefix, arg);
        }
}

public SelectAddbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, minutes, String:adminAuth[30], String:adminIp[30], String:authid[20], String:banReason[256], String:Query[512];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        minutes = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, authid, sizeof(authid));
        ReadPackString(data, adminAuth, sizeof(adminAuth));
        ReadPackString(data, adminIp, sizeof(adminIp));
        SQL_EscapeString(DB, reason, banReason, sizeof(banReason));

        if (error[0])
        {
                LogToFile(logFile, "Add Ban Select Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%sFailed to ban %s.", Prefix, authid);
                else
                        PrintToServer("%sFailed to ban %s.", Prefix, authid);
                return;
        }
        if (SQL_GetRowCount(hndl))
        {
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%s%s is already banned.", Prefix, authid);
                else
                        PrintToServer("%s%s is already banned.", Prefix, authid);
                return;
        }
        if (serverID == -1)
        {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), ' ')",
                        DatabasePrefix, authid, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, DatabasePrefix, ServerIp, ServerPort);
        } else {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                %d, ' ')",
                        DatabasePrefix, authid, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, serverID);
        }

        SQL_TQuery(DB, InsertAddbanCallback, Query, data, DBPrio_High);
}

public InsertAddbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, minutes, String:authid[20];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        minutes = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, authid, sizeof(authid));

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Add Ban Insert Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%ssm_addban failed", Prefix);
                }
                return;
        }

        LogAction(admin,
                -1,
                "\"%L\" added ban (minutes \"%i\") (id \"%s\") (reason \"%s\")",
                admin,
                minutes,
                authid,
                reason);
        if (admin && IsClientInGame(admin))
        {
                PrintToChat(admin, "%s%s successfully banned", Prefix, authid);
        } else {
                PrintToServer("%s%s successfully banned", Prefix, authid);
        }
}

// ProcessQueueCallback is called as the result of selecting all the rows from the queue table
public ProcessQueueCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE || strlen(error) > 0)
        {
                LogToFile(logFile, "Failed to retrieve queued bans from sqlite database, %s", error);
                return;
        }

        decl String:auth[30];
        decl time;
        decl startTime;
        new String:reason[128];
        decl String:name[64];
        decl String:ip[20];
        decl String:adminAuth[30];
        decl String:adminIp[20];
        decl String:query[1024];
        decl String:banName[128];
        decl String:banReason[256];
        while (SQL_MoreRows(hndl))
        {
                // Oh noes! What happened?!
                if (!SQL_FetchRow(hndl))
                        continue;

                // if we get to here then there are rows in the queue pending processing
                SQL_FetchString(hndl, 0, auth, sizeof(auth));
                time = SQL_FetchInt(hndl, 1);
                startTime = SQL_FetchInt(hndl, 2);
                SQL_FetchString(hndl, 3, reason, sizeof(reason));
                SQL_FetchString(hndl, 4, name, sizeof(name));
                SQL_FetchString(hndl, 5, ip, sizeof(ip));
                SQL_FetchString(hndl, 6, adminAuth, sizeof(adminAuth));
                SQL_FetchString(hndl, 7, adminIp, sizeof(adminIp));
                SQL_EscapeString(SQLiteDB, name, banName, sizeof(banName));
                SQL_EscapeString(SQLiteDB, reason, banReason, sizeof(banReason));
                if (startTime + time * 60 > GetTime() || time == 0)
                {
                        // This ban is still valid and should be entered into the db
                        if (serverID == -1)
                        {
                                FormatEx(query, sizeof(query),
                                        "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid) VALUES  \
                                                ('%s', '%s', '%s', %d, %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1))",
                                        DatabasePrefix, ip, auth, banName, startTime, startTime + time * 60, time * 60, banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, DatabasePrefix, ServerIp, ServerPort);
                        }
                        else
                        {
                                FormatEx(query, sizeof(query),
                                        "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid) VALUES  \
                                                ('%s', '%s', '%s', %d, %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                %d)",
                                        DatabasePrefix, ip, auth, banName, startTime, startTime + time * 60, time * 60, banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, serverID);
                        }
                        new Handle:authPack = CreateDataPack();
                        WritePackString(authPack, auth);
                        ResetPack(authPack);
                        SQL_TQuery(DB, AddedFromSQLiteCallback, query, authPack);
                } else {
                        // The ban is no longer valid and should be deleted from the queue
                        FormatEx(query, sizeof(query), "DELETE FROM queue WHERE steam_id = '%s'", auth);
                        SQL_TQuery(SQLiteDB, ErrorCheckCallback, query);
                }
        }
        // We have finished processing the queue but should process again in ProcessQueueTime minutes
        CreateTimer(float(ProcessQueueTime * 60), ProcessQueue);
}

public AddedFromSQLiteCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl String:buffer[512];
        decl String:auth[40];
        ReadPackString(data, auth, sizeof(auth));
        if (error[0] == '\0')
        {
                // The insert was successful so delete the record from the queue
                FormatEx(buffer, sizeof(buffer), "DELETE FROM queue WHERE steam_id = '%s'", auth);
                SQL_TQuery(SQLiteDB, ErrorCheckCallback, buffer);

                // They are added to main banlist, so remove the temp ban
                RemoveBan(auth, BANFLAG_AUTHID);

        } else {
                // the insert failed so we leave the record in the queue and increase our temporary ban
                FormatEx(buffer, sizeof(buffer), "banid %d %s", ProcessQueueTime, auth);
                ServerCommand(buffer);
        }
        CloseHandle(data);
}

public ServerInfoCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (error[0])
        {
                LogToFile(logFile, "Server Select Query Failed: %s", error);
                return;
        }

        if (hndl == INVALID_HANDLE || SQL_GetRowCount(hndl) == 0)
        {
                // get the game folder name used to determine the mod
                decl String:desc[64], String:query[200];
                GetGameFolderName(desc, sizeof(desc));
                FormatEx(query, sizeof(query), "INSERT INTO %s_servers (ip, port, rcon, modid) VALUES ('%s', '%s', '', (SELECT mid FROM %s_mods WHERE modfolder = '%s'))", DatabasePrefix, ServerIp, ServerPort, DatabasePrefix, desc);
                SQL_TQuery(DB, ErrorCheckCallback, query);
        }
}

public ErrorCheckCallback(Handle:owner, Handle:hndle, const String:error[], any:data)
{
        if (error[0])
        {
                LogToFile(logFile, "Query Failed: %s", error);
        }
}

public void VerifyBan(Database db, DBResultSet results, const char[] error, int userid)
{
        char clientName[64], clientAuth[64], clientIp[64];

        int client = GetClientOfUserId(userid);

        if (!client)
                return;

        /* Failure happen. Do retry with delay */
        if (results == null)
        {
                LogToFile(logFile, "Verify Ban Query Failed: %s", error);
                PlayerRecheck[client] = CreateTimer(RetryTime, ClientRecheck, client);
                return;
        }

        GetClientIP(client, clientIp, sizeof(clientIp));
        GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth));
        GetClientName(client, clientName, sizeof(clientName));

        if (results.RowCount > 0)
        {
                char buffer[40], Name[128], Query[512];

                // Amending to ban record's IP field
                if (results.FetchRow())
                {
                        char sIP[32];

                        int iBid = results.FetchInt(0);
                        results.FetchString(1, sIP, sizeof sIP);

                        if (StrEqual(sIP, ""))
                        {
                                char sQuery[256];

                                FormatEx(sQuery, sizeof sQuery, "UPDATE %s_bans SET `ip` = '%s' WHERE `bid` = '%d'", DatabasePrefix, clientIp, iBid);

                                DB.Query(SQL_OnIPMend, sQuery, client);
                        }
                }

                DB.Escape(clientName, Name, sizeof Name);

                if (serverID == -1)
                {
                        FormatEx(Query, sizeof(Query), "INSERT INTO %s_banlog (sid ,time ,name ,bid) VALUES  \
                                ((SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), UNIX_TIMESTAMP(), '%s', \
                                (SELECT bid FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND RemoveType IS NULL LIMIT 0,1))",
                                DatabasePrefix, DatabasePrefix, ServerIp, ServerPort, Name, DatabasePrefix, clientAuth[8], clientIp);
                }
                else
                {
                        FormatEx(Query, sizeof(Query), "INSERT INTO %s_banlog (sid ,time ,name ,bid) VALUES  \
                                (%d, UNIX_TIMESTAMP(), '%s', \
                                (SELECT bid FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND RemoveType IS NULL LIMIT 0,1))",
                                DatabasePrefix, serverID, Name, DatabasePrefix, clientAuth[8], clientIp);
                }

                SQL_TQuery(DB, ErrorCheckCallback, Query, client, DBPrio_High);

                FormatEx(buffer, sizeof(buffer), "banid 5 %s", clientAuth);
                ServerCommand(buffer);
                KickClient(client, "%t", "Banned Check Site", WebsiteAddress);

                return;
        }

        #if defined DEBUG
        LogToFile(logFile, "%s is NOT banned.", clientAuth);
        #endif

        PlayerStatus[client] = true;
}

public void SQL_OnIPMend(Database db, DBResultSet results, const char[] error, int iClient)
{
        if (results == null)
        {
                char sIP[32], sSteamID[32];

                GetClientAuthId(iClient, AuthId_Steam3, sSteamID, sizeof sSteamID);
                GetClientIP(iClient, sIP, sizeof sIP);

                LogToFile(logFile, "Failed to mend IP address for %s (%s): %s", sSteamID, sIP, error);
        }
}

public AdminsDone(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        //SELECT authid, srv_password , srv_group, srv_flags, user
        if (hndl == INVALID_HANDLE || strlen(error) > 0)
        {
                --curLoading;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve admins from the database, %s", error);
                return;
        }
        decl String:authType[] = "steam";
        decl String:identity[66];
        decl String:password[66];
        decl String:groups[256];
        decl String:flags[32];
        decl String:name[66];
        new admCount = 0;
        new Immunity = 0;
        new AdminId:curAdm = INVALID_ADMIN_ID;
        new Handle:adminsKV = CreateKeyValues("Admins");

        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups

                SQL_FetchString(hndl, 0, identity, 66);
                SQL_FetchString(hndl, 1, password, 66);
                SQL_FetchString(hndl, 2, groups, 256);
                SQL_FetchString(hndl, 3, flags, 32);
                SQL_FetchString(hndl, 4, name, 66);

                Immunity = SQL_FetchInt(hndl, 5);

                TrimString(name);
                TrimString(identity);
                TrimString(groups);
                TrimString(flags);

                // Disable writing to file if they chose to
                if (backupConfig)
                {
                        KvJumpToKey(adminsKV, name, true);

                        KvSetString(adminsKV, "auth", authType);
                        KvSetString(adminsKV, "identity", identity);

                        if (strlen(flags) > 0)
                                KvSetString(adminsKV, "flags", flags);

                        if (strlen(groups) > 0)
                                KvSetString(adminsKV, "group", groups);

                        if (strlen(password) > 0)
                                KvSetString(adminsKV, "password", password);

                        if (Immunity > 0)
                                KvSetNum(adminsKV, "immunity", Immunity);

                        KvRewind(adminsKV);
                }

                // find or create the admin using that identity
                if ((curAdm = FindAdminByIdentity(authType, identity)) == INVALID_ADMIN_ID)
                {
                        curAdm = CreateAdmin(name);
                        // That should never happen!
                        if (!BindAdminIdentity(curAdm, authType, identity))
                        {
                                LogToFile(logFile, "Unable to bind admin %s to identity %s", name, identity);
                                RemoveAdmin(curAdm);
                                continue;
                        }
                }

                #if defined DEBUG
                LogToFile(logFile, "Given %s (%s) admin", name, identity);
                #endif

                new curPos = 0;
                new GroupId:curGrp = INVALID_GROUP_ID;
                new numGroups;
                decl String:iterGroupName[64];

                // Who thought this comma seperated group parsing would be a good idea?!
                /*
                decl String:grp[64];
                new nextPos = 0;
                while ((nextPos = SplitString(groups[curPos],",",grp,64)) != -1)
                {
                        curPos += nextPos;
                        curGrp = FindAdmGroup(grp);
                        if (curGrp == INVALID_GROUP_ID)
                        {
                                LogToFile(logFile, "Unknown group \"%s\"",grp);
                        }
                        else
                        {
                                // Check, if he's not in the group already.
                                numGroups = GetAdminGroupCount(curAdm);
                                for(new i=0;i<numGroups;i++)
                                {
                                        GetAdminGroup(curAdm, i, iterGroupName, sizeof(iterGroupName));
                                        // Admin is already part of the group, so don't try to inherit its permissions.
                                        if(StrEqual(iterGroupName, grp))
                                        {
                                                numGroups = -2;
                                                break;
                                        }
                                }
                                // Only try to inherit the group, if it's a new one.
                                if (numGroups != -2 && !AdminInheritGroup(curAdm,curGrp))
                                {
                                        LogToFile(logFile, "Unable to inherit group \"%s\"",grp);
                                }
                        }
                }*/

                if (strcmp(groups[curPos], "") != 0)
                {
                        curGrp = FindAdmGroup(groups[curPos]);
                        if (curGrp == INVALID_GROUP_ID)
                        {
                                LogToFile(logFile, "Unknown group \"%s\"", groups[curPos]);
                        }
                        else
                        {
                                // Check, if he's not in the group already.
                                numGroups = GetAdminGroupCount(curAdm);
                                for (new i = 0; i < numGroups; i++)
                                {
                                        GetAdminGroup(curAdm, i, iterGroupName, sizeof(iterGroupName));
                                        // Admin is already part of the group, so don't try to inherit its permissions.
                                        if (StrEqual(iterGroupName, groups[curPos]))
                                        {
                                                numGroups = -2;
                                                break;
                                        }
                                }

                                // Only try to inherit the group, if it's a new one.
                                if (numGroups != -2 && !AdminInheritGroup(curAdm, curGrp))
                                {
                                        LogToFile(logFile, "Unable to inherit group \"%s\"", groups[curPos]);
                                }

                                if (GetAdminImmunityLevel(curAdm) < Immunity)
                                {
                                        SetAdminImmunityLevel(curAdm, Immunity);
                                }
                                #if defined DEBUG
                                LogToFile(logFile, "Admin %s (%s) has %d immunity", name, identity, Immunity);
                                #endif
                        }
                }

                if (strlen(password) > 0)
                        SetAdminPassword(curAdm, password);

                for (new i = 0; i < strlen(flags); ++i)
                {
                        if (flags[i] < 'a' || flags[i] > 'z')
                                continue;

                        if (g_FlagLetters[flags[i]-'a'] < Admin_Reservation)
                                continue;

                        SetAdminFlag(curAdm, g_FlagLetters[flags[i]-'a'], true);
                }
                ++admCount;
        }

        if (backupConfig)
                KeyValuesToFile(adminsKV, adminsLoc);
        CloseHandle(adminsKV);

        #if defined DEBUG
        LogToFile(logFile, "Finished loading %i admins.", admCount);
        #endif

        --curLoading;
        CheckLoadAdmins();
}

public GroupsDone(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                curLoading--;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve groups from the database, %s", error);
                return;
        }
        decl String:grpName[128], String:immuneGrpName[128];
        decl String:grpFlags[32];
        new Immunity;
        new grpCount = 0;
        new Handle:groupsKV = CreateKeyValues("Groups");

        new GroupId:curGrp = INVALID_GROUP_ID;
        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups
                SQL_FetchString(hndl, 0, grpName, 128);
                SQL_FetchString(hndl, 1, grpFlags, 32);
                Immunity = SQL_FetchInt(hndl, 2);
                SQL_FetchString(hndl, 3, immuneGrpName, 128);

                TrimString(grpName);
                TrimString(grpFlags);
                TrimString(immuneGrpName);

                // Ignore empty rows..
                if (!strlen(grpName))
                        continue;

                curGrp = CreateAdmGroup(grpName);

                if (backupConfig)
                {
                        KvJumpToKey(groupsKV, grpName, true);
                        if (strlen(grpFlags) > 0)
                                KvSetString(groupsKV, "flags", grpFlags);
                        if (Immunity > 0)
                                KvSetNum(groupsKV, "immunity", Immunity);

                        KvRewind(groupsKV);
                }

                if (curGrp == INVALID_GROUP_ID)
                {  //This occurs when the group already exists
                        curGrp = FindAdmGroup(grpName);
                }

                for (new i = 0; i < strlen(grpFlags); ++i)
                {
                        if (grpFlags[i] < 'a' || grpFlags[i] > 'z')
                                continue;

                        if (g_FlagLetters[grpFlags[i]-'a'] < Admin_Reservation)
                                continue;

                        SetAdmGroupAddFlag(curGrp, g_FlagLetters[grpFlags[i]-'a'], true);
                }

                // Set the group immunity.
                if (Immunity > 0)
                {
                        SetAdmGroupImmunityLevel(curGrp, Immunity);
                        #if defined DEBUG
                        LogToFile(logFile, "Group %s has %d immunity", grpName, Immunity);
                        #endif
                }

                grpCount++;
        }

        if (backupConfig)
                KeyValuesToFile(groupsKV, groupsLoc);
        CloseHandle(groupsKV);

        #if defined DEBUG
        LogToFile(logFile, "Finished loading %i groups.", grpCount);
        #endif

        // Load the group overrides
        decl String:query[512];
        FormatEx(query, 512, "SELECT sg.name, so.type, so.name, so.access FROM %s_srvgroups_overrides so LEFT JOIN %s_srvgroups sg ON sg.id = so.group_id ORDER BY sg.id", DatabasePrefix, DatabasePrefix);
        SQL_TQuery(DB, LoadGroupsOverrides, query);

        /*if (reparse)
        {
                decl String:query[512];
                FormatEx(query,512,"SELECT name, immunity, groups_immune FROM %s_srvgroups ORDER BY id",DatabasePrefix);
                SQL_TQuery(DB,GroupsSecondPass,query);
        }
        else
        {
                curLoading--;
                CheckLoadAdmins();
        }*/
}

// Reparse to apply inherited immunity
public GroupsSecondPass(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                curLoading--;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve groups from the database, %s", error);
                return;
        }
        decl String:grpName[128], String:immunityGrpName[128];

        new GroupId:curGrp = INVALID_GROUP_ID;
        new GroupId:immuneGrp = INVALID_GROUP_ID;
        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups

                SQL_FetchString(hndl, 0, grpName, 128);
                TrimString(grpName);
                if (strlen(grpName) == 0)
                        continue;

                SQL_FetchString(hndl, 2, immunityGrpName, sizeof(immunityGrpName));
                TrimString(immunityGrpName);

                curGrp = FindAdmGroup(grpName);
                if (curGrp == INVALID_GROUP_ID)
                        continue;

                immuneGrp = FindAdmGroup(immunityGrpName);
                if (immuneGrp == INVALID_GROUP_ID)
                        continue;

                SetAdmGroupImmuneFrom(curGrp, immuneGrp);

                #if defined DEBUG
                LogToFile(logFile, "Group %s inhertied immunity from group %s", grpName, immunityGrpName);
                #endif
        }
        --curLoading;
        CheckLoadAdmins();
}

public LoadGroupsOverrides(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                curLoading--;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve group overrides from the database, %s", error);
                return;
        }
        decl String:sGroupName[128], String:sType[16], String:sCommand[64], String:sAllowed[16];
        decl OverrideRule:iRule, OverrideType:iType;

        new Handle:groupsKV = CreateKeyValues("Groups");
        FileToKeyValues(groupsKV, groupsLoc);

        new GroupId:curGrp = INVALID_GROUP_ID;
        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups

                SQL_FetchString(hndl, 0, sGroupName, sizeof(sGroupName));
                TrimString(sGroupName);
                if (strlen(sGroupName) == 0)
                        continue;

                SQL_FetchString(hndl, 1, sType, sizeof(sType));
                SQL_FetchString(hndl, 2, sCommand, sizeof(sCommand));
                SQL_FetchString(hndl, 3, sAllowed, sizeof(sAllowed));

                curGrp = FindAdmGroup(sGroupName);
                if (curGrp == INVALID_GROUP_ID)
                        continue;

                iRule = StrEqual(sAllowed, "allow") ? Command_Allow : Command_Deny;
                iType = StrEqual(sType, "group") ? Override_CommandGroup : Override_Command;

                #if defined DEBUG
                PrintToServer("AddAdmGroupCmdOverride(%i, %s, %i, %i)", curGrp, sCommand, iType, iRule);
                #endif

                // Save overrides into admin_groups.cfg backup
                if (KvJumpToKey(groupsKV, sGroupName))
                {
                        KvJumpToKey(groupsKV, "Overrides", true);
                        if (iType == Override_Command)
                                KvSetString(groupsKV, sCommand, sAllowed);
                        else
                        {
                                Format(sCommand, sizeof(sCommand), "@%s", sCommand);
                                KvSetString(groupsKV, sCommand, sAllowed);
                        }
                        KvRewind(groupsKV);
                }

                AddAdmGroupCmdOverride(curGrp, sCommand, iType, iRule);
        }
        curLoading--;
        CheckLoadAdmins();

        if (backupConfig)
                KeyValuesToFile(groupsKV, groupsLoc);
        CloseHandle(groupsKV);
}

public OverridesDone(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                LogToFile(logFile, "Failed to retrieve overrides from the database, %s", error);
                ParseBackupConfig_Overrides();
                return;
        }

        new Handle:hKV = CreateKeyValues("SB_Overrides");

        decl String:sFlags[32], String:sName[64], String:sType[64];
        while (SQL_FetchRow(hndl))
        {
                SQL_FetchString(hndl, 0, sType, sizeof(sType));
                SQL_FetchString(hndl, 1, sName, sizeof(sName));
                SQL_FetchString(hndl, 2, sFlags, sizeof(sFlags));

                // KeyValuesToFile won't add that key, if the value is ""..
                if (sFlags[0] == '\0')
                {
                        sFlags[0] = ' ';
                        sFlags[1] = '\0';
                }

                #if defined DEBUG
                LogToFile(logFile, "Adding override (%s, %s, %s)", sType, sName, sFlags);
                #endif

                if (StrEqual(sType, "command"))
                {
                        AddCommandOverride(sName, Override_Command, ReadFlagString(sFlags));
                        KvJumpToKey(hKV, "override_commands", true);
                        KvSetString(hKV, sName, sFlags);
                        KvGoBack(hKV);
                }
                else if (StrEqual(sType, "group"))
                {
                        AddCommandOverride(sName, Override_CommandGroup, ReadFlagString(sFlags));
                        KvJumpToKey(hKV, "override_groups", true);
                        KvSetString(hKV, sName, sFlags);
                        KvGoBack(hKV);
                }
        }

        KvRewind(hKV);

        if (backupConfig)
                KeyValuesToFile(hKV, overridesLoc);
        CloseHandle(hKV);
}

// TIMER CALL BACKS //

public Action:ClientRecheck(Handle:timer, any:client)
{
        decl String:Authid[64];
        if (!PlayerStatus[client] && IsClientConnected(client) && GetClientAuthId(client, AuthId_Steam2, Authid, sizeof(Authid)))
        {
                OnClientAuthorized(client, Authid);
        }

        PlayerRecheck[client] = INVALID_HANDLE;
        return Plugin_Stop;
}

/*
public Action:PruneBans(Handle:timer)
{
        decl String:Query[512];
        FormatEx(Query, sizeof(Query),
                        "UPDATE %s_bans SET RemovedBy = 0, RemoveType = 'E', RemovedOn = UNIX_TIMESTAMP() WHERE length != '0' AND ends < UNIX_TIMESTAMP()",
                        DatabasePrefix);

        SQL_TQuery(DB, ErrorCheckCallback, Query);
        return Plugin_Continue;
}
*/

public Action:ProcessQueue(Handle:timer, any:data)
{
        decl String:buffer[512];
        Format(buffer, sizeof(buffer), "SELECT steam_id, time, start_time, reason, name, ip, admin_id, admin_ip FROM queue");
        SQL_TQuery(SQLiteDB, ProcessQueueCallback, buffer);
}

// PARSER //

static InitializeConfigParser()
{
        if (ConfigParser == INVALID_HANDLE)
        {
                ConfigParser = SMC_CreateParser();
                SMC_SetReaders(ConfigParser, ReadConfig_NewSection, ReadConfig_KeyValue, ReadConfig_EndSection);
        }
}

static InternalReadConfig(const String:path[])
{
        ConfigState = ConfigStateNone;

        new SMCError:err = SMC_ParseFile(ConfigParser, path);

        if (err != SMCError_Okay)
        {
                decl String:buffer[64];
                PrintToServer("%s", SMC_GetErrorString(err, buffer, sizeof(buffer)) ? buffer : "Fatal parse error");
        }
}

public SMCResult:ReadConfig_NewSection(Handle:smc, const String:name[], bool:opt_quotes)
{
        if (name[0])
        {
                if (strcmp("Config", name, false) == 0) {
                        ConfigState = ConfigStateConfig;
                } else if (strcmp("BanReasons", name, false) == 0) {
                        ConfigState = ConfigStateReasons;
                } else if (strcmp("HackingReasons", name, false) == 0) {
                        ConfigState = ConfigStateHacking;
                } else if (strcmp("BanTime", name, false) == 0) {
                        ConfigState = ConfigStateTime;
                }
        }
        return SMCParse_Continue;
}

public SMCResult:ReadConfig_KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes)
{
        if (!key[0])
                return SMCParse_Continue;

        switch (ConfigState)
        {
                case ConfigStateConfig:
                {
                        if (strcmp("website", key, false) == 0)
                        {
                                strcopy(WebsiteAddress, sizeof(WebsiteAddress), value);
                        }
                        else if (strcmp("Addban", key, false) == 0)
                        {
                                if (StringToInt(value) == 0)
                                {
                                        CommandDisable |= DISABLE_ADDBAN;
                                }
                        }
                        else if (strcmp("AutoAddServer", key, false) == 0)
                        {
                                AutoAdd = StringToInt(value) == 1;
                        }
                        else if (strcmp("Unban", key, false) == 0)
                        {
                                if (StringToInt(value) == 0)
                                {
                                        CommandDisable |= DISABLE_UNBAN;
                                }
                        }
                        else if (strcmp("DatabasePrefix", key, false) == 0)
                        {
                                strcopy(DatabasePrefix, sizeof(DatabasePrefix), value);

                                if (DatabasePrefix[0] == '\0')
                                {
                                        DatabasePrefix = "sb";
                                }
                        }
                        else if (strcmp("RetryTime", key, false) == 0)
                        {
                                RetryTime = StringToFloat(value);
                                if (RetryTime < 15.0)
                                {
                                        RetryTime = 15.0;
                                } else if (RetryTime > 60.0) {
                                        RetryTime = 60.0;
                                }
                        }
                        else if (strcmp("ProcessQueueTime", key, false) == 0)
                        {
                                ProcessQueueTime = StringToInt(value);
                        }
                        else if (strcmp("BackupConfigs", key, false) == 0)
                        {
                                backupConfig = StringToInt(value) == 1;
                        }
                        else if (strcmp("EnableAdmins", key, false) == 0)
                        {
                                enableAdmins = StringToInt(value) == 1;
                        }
                        else if (strcmp("RequireSiteLogin", key, false) == 0)
                        {
                                requireSiteLogin = StringToInt(value) == 1;
                        }
                        else if (strcmp("ServerID", key, false) == 0)
                        {
                                serverID = StringToInt(value);
                        }
                }

                case ConfigStateReasons:
                {
                        if (ReasonMenuHandle != INVALID_HANDLE)
                        {
                                AddMenuItem(ReasonMenuHandle, key, value);
                        }
                }
                case ConfigStateHacking:
                {
                        if (HackingMenuHandle != INVALID_HANDLE)
                        {
                                AddMenuItem(HackingMenuHandle, key, value);
                        }
                }
                case ConfigStateTime:
                {
                        if (StringToInt(key) > -1 && TimeMenuHandle != INVALID_HANDLE)
                        {
                                AddMenuItem(TimeMenuHandle, key, value);
                        }
                }
        }
        return SMCParse_Continue;
}

public SMCResult:ReadConfig_EndSection(Handle:smc)
{
        return SMCParse_Continue;
}


/*********************************************************
 * Ban Player from server
 *
 * @param client        The client index of the player to ban
 * @param time                The time to ban the player for (in minutes, 0 = permanent)
 * @param reason        The reason to ban the player from the server
 * @noreturn
 *********************************************************/
public Native_SBBanPlayer(Handle plugin, int numParams)
{
        int client = GetNativeCell(1);
        int target = GetNativeCell(2);
        int time = GetNativeCell(3);
        char reason[128];
        GetNativeString(4, reason, 128);

        if (reason[0] == '\0')
                strcopy(reason, sizeof(reason), "Banned by SourceBans");

        if (client && IsClientInGame(client))
        {
                AdminId aid = GetUserAdmin(client);
                if (aid == INVALID_ADMIN_ID)
                {
                        ThrowNativeError(SP_ERROR_NATIVE, "Ban Error: Player is not an admin.");
                        return 0;
                }

                if (!GetAdminFlag(aid, Admin_Ban))
                {
                        ThrowNativeError(SP_ERROR_NATIVE, "Ban Error: Player does not have BAN flag.");
                        return 0;
                }
        }

        PrepareBan(client, target, time, reason, sizeof(reason));
        return true;
}

public int Native_SBReportPlayer(Handle plugin, int numParams)
{
        if (numParams < 3)
        {
                ThrowNativeError(SP_ERROR_NATIVE, "Invalid amount of arguments. Received %d arguments", numParams);
                return;
        }

        int iReporter = GetNativeCell(1)
          , iTarget = GetNativeCell(2)
          , iReasonLen;

        int iTime = GetTime();

        GetNativeStringLength(3, iReasonLen);

        iReasonLen++;

        char[] sReason = new char[iReasonLen];

        GetNativeString(3, sReason, iReasonLen);

        char sRAuth[32], sTAuth[32], sRName[MAX_NAME_LENGTH + 1], sTName[MAX_NAME_LENGTH + 1],
        sRIP[16], sTIP[16], sREscapedName[MAX_NAME_LENGTH * 2 + 1], sTEscapedName[MAX_NAME_LENGTH * 2 + 1];

        char[] sEscapedReason = new char[iReasonLen * 2 + 1];

        GetClientAuthId(iReporter, AuthId_Steam2, sRAuth, sizeof sRAuth);
        GetClientAuthId(iTarget, AuthId_Steam2, sTAuth, sizeof sTAuth);

        GetClientName(iReporter, sRName, sizeof sRName);
        GetClientName(iTarget, sTName, sizeof sTName);

        GetClientIP(iReporter, sRIP, sizeof sRIP);
        GetClientIP(iTarget, sTIP, sizeof sTIP);

        DB.Escape(sRName, sREscapedName, sizeof sREscapedName);
        DB.Escape(sTName, sTEscapedName, sizeof sTEscapedName);
        DB.Escape(sReason, sEscapedReason, iReasonLen * 2 + 1);

        char[] sQuery = new char[512 + (iReasonLen * 2 + 1)];

        Format(sQuery, 512 + (iReasonLen * 2 + 1), "INSERT INTO %s_submissions (`submitted`, `modid`, `SteamId`, `name`, `email`, `reason`, `ip`, `subname`, `sip`, `archiv`, `server`)"
        ... "VALUES ('%d', 0, '%s', '%s', '%s', '%s', '%s', '%s', '%s', 0, 0)", DatabasePrefix, iTime, sTAuth, sTEscapedName, sRAuth, sEscapedReason, sRIP, sREscapedName, sTIP);

        DataPack ForwardPack = new DataPack();

        ForwardPack.WriteCell(iReporter);
        ForwardPack.WriteCell(iTarget);
        ForwardPack.WriteCell(iReasonLen);
        ForwardPack.WriteString(sReason);

        DB.Query(SQL_OnReportPlayer, sQuery, ForwardPack);
}

public void SQL_OnReportPlayer(Database db, DBResultSet results, const char[] error, DataPack ForwardPack)
{
        if (results == null)
                LogToFile(logFile, "Failed to submit report: %s", error);
        else
        {
                ForwardPack.Reset();

                int iReporter = ForwardPack.ReadCell();
                int iTarget = ForwardPack.ReadCell();
                int iReasonLen = ForwardPack.ReadCell();

                char[] sReason = new char[iReasonLen];

                ForwardPack.ReadString(sReason, iReasonLen);

                Call_StartForward(g_hFwd_OnReportAdded);
                Call_PushCell(iReporter);
                Call_PushCell(iTarget);
                Call_PushString(sReason);
                Call_Finish();
        }
}

// STOCK FUNCTIONS //

public InitializeBackupDB()
{
        char error[255];

        SQLiteDB = SQLite_UseDatabase("sourcebans-queue", error, sizeof(error));
        if (SQLiteDB == INVALID_HANDLE)
                SetFailState(error);

        SQL_LockDatabase(SQLiteDB);
        SQL_FastQuery(SQLiteDB, "CREATE TABLE IF NOT EXISTS queue (steam_id TEXT PRIMARY KEY ON CONFLICT REPLACE, time INTEGER, start_time INTEGER, reason TEXT, name TEXT, ip TEXT, admin_id TEXT, admin_ip TEXT);");
        SQL_UnlockDatabase(SQLiteDB);
}

public bool CreateBan(int client, int target, int time, const char[] reason)
{
        char adminIp[24], adminAuth[64];
        int admin = client;

        // The server is the one calling the ban
        if (!admin)
        {
                if (reason[0] == '\0')
                {
                        // We cannot pop the reason menu if the command was issued from the server
                        PrintToServer("%s%T", Prefix, "Include Reason", LANG_SERVER);
                        return false;
                }

                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
                strcopy(adminIp, sizeof(adminIp), ServerIp);
        } else {
                GetClientIP(admin, adminIp, sizeof(adminIp));
                GetClientAuthId(admin, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // target information
        char ip[24], auth[64], name[64];

        GetClientName(target, name, sizeof(name));
        GetClientIP(target, ip, sizeof(ip));
        if (!GetClientAuthId(target, AuthId_Steam2, auth, sizeof(auth)))
                return false;

        int userid = admin ? GetClientUserId(admin) : 0;

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        DataPack reasonPack = new DataPack();

        WritePackString(reasonPack, reason);

        dataPack.WriteCell(admin);
        dataPack.WriteCell(target);
        dataPack.WriteCell(userid);
        dataPack.WriteCell(GetClientUserId(target));
        dataPack.WriteCell(time);
        dataPack.WriteCell( _:reasonPack);
        dataPack.WriteString(name);
        dataPack.WriteString(auth);
        dataPack.WriteString(ip);
        dataPack.WriteString(adminAuth);
        dataPack.WriteString(adminIp);

        dataPack.Reset();
        reasonPack.Reset();

        if (reason[0] != '\0')
        {
                // if we have a valid reason pass move forward with the ban
                if (DB != INVALID_HANDLE)
                {
                        UTIL_InsertBan(time, name, auth, ip, reason, adminAuth, adminIp, dataPack);
                } else {
                        UTIL_InsertTempBan(time, name, auth, ip, reason, adminAuth, adminIp, dataPack);
                }
        } else {
                // We need a reason so offer the administrator a menu of reasons
                PlayerDataPack[admin] = dataPack;
                DisplayMenu(ReasonMenuHandle, admin, MENU_TIME_FOREVER);
                ReplyToCommand(admin, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Check Menu");
        }

        Call_StartForward(g_hFwd_OnBanAdded);
        Call_PushCell(client);
        Call_PushCell(target);
        Call_PushCell(time);
        Call_PushString(reason);
        Call_Finish();

        return true;
}

stock UTIL_InsertBan(time, const String:Name[], const String:Authid[], const String:Ip[], const String:Reason[], const String:AdminAuthid[], const String:AdminIp[], Handle:Pack)
{
        //new Handle:dummy;
        //PruneBans(dummy);
        decl String:banName[128];
        decl String:banReason[256];
        decl String:Query[1024];
        SQL_EscapeString(DB, Name, banName, sizeof(banName));
        SQL_EscapeString(DB, Reason, banReason, sizeof(banReason));
        if (serverID == -1)
        {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'),'0'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), ' ')",
                        DatabasePrefix, Ip, Authid, banName, (time * 60), (time * 60), banReason, DatabasePrefix, AdminAuthid, AdminAuthid[8], AdminIp, DatabasePrefix, ServerIp, ServerPort);
        } else {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'),'0'), '%s', \
                                                %d, ' ')",
                        DatabasePrefix, Ip, Authid, banName, (time * 60), (time * 60), banReason, DatabasePrefix, AdminAuthid, AdminAuthid[8], AdminIp, serverID);
        }

        SQL_TQuery(DB, VerifyInsert, Query, Pack, DBPrio_High);
}

stock UTIL_InsertTempBan(int time, const char[] name, const char[] auth, const char[] ip, const char[] reason, const char[] adminAuth, const char[] adminIp, DataPack dataPack)
{
        dataPack.ReadCell(); // admin index

        int client = ReadPackCell(dataPack);

        dataPack.ReadCell(); // admin userid
        dataPack.ReadCell(); // target userid
        dataPack.ReadCell(); // time

        DataPack reasonPack = view_as<DataPack>(dataPack.ReadCell());

        if (reasonPack != INVALID_HANDLE)
                CloseHandle(reasonPack);
        CloseHandle(dataPack);

        // we add a temporary ban and then add the record into the queue to be processed when the database is available
        char buffer[50];

        Format(buffer, sizeof(buffer), "banid %d %s", ProcessQueueTime, auth);

        ServerCommand(buffer);

        if (IsClientInGame(client))
                KickClient(client, "%t", "Banned Check Site", WebsiteAddress);

        char banName[128], banReason[256], query[512];

        SQL_EscapeString(SQLiteDB, name, banName, sizeof(banName));
        SQL_EscapeString(SQLiteDB, reason, banReason, sizeof(banReason));

        FormatEx(query, sizeof(query), "INSERT INTO queue VALUES ('%s', %i, %i, '%s', '%s', '%s', '%s', '%s')",
                auth, time, GetTime(), banReason, banName, ip, adminAuth, adminIp);

        SQL_TQuery(SQLiteDB, ErrorCheckCallback, query);
}

stock CheckLoadAdmins()
{
        for (new i = 1; i <= MaxClients; i++)
        {
                if (IsClientInGame(i) && IsClientAuthorized(i))
                {
                        RunAdminCacheChecks(i);
                        NotifyPostAdminCheck(i);
                }
        }
}

stock InsertServerInfo()
{
    if (DB == INVALID_HANDLE) {
        return;
    }
   
    char query[100];
    int pieces[4];
    int longip = GetConVarInt(CvarHostIp);

    pieces[0] = (longip >> 24) & 0x000000FF;
    pieces[1] = (longip >> 16) & 0x000000FF;
    pieces[2] = (longip >> 8) & 0x000000FF;
    pieces[3] = longip & 0x000000FF;

    FormatEx(ServerIp, sizeof(ServerIp), "%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]);
    GetConVarString(CvarPort, ServerPort, sizeof(ServerPort));

    if (AutoAdd != false) {
        FormatEx(query, sizeof(query), "SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s'", DatabasePrefix, ServerIp, ServerPort);
        SQL_TQuery(DB, ServerInfoCallback, query);
    }
}

stock void PrepareBan(int client, int target, int time, char[] reason, int size)
{
        #if defined DEBUG
        LogToFile(logFile, "PrepareBan()");
        #endif
        if (!target || !IsClientInGame(target))
                return;
        char authid[64], name[32], bannedSite[512];
        if (!GetClientAuthId(target, AuthId_Steam2, authid, sizeof(authid)))
                return;
        GetClientName(target, name, sizeof(name));


        if (CreateBan(client, target, time, reason))
        {
                if (!time)
                {
                        if (reason[0] == '\0')
                        {
                                ShowActivity(client, "%t", "Permabanned player", name);
                        } else {
                                ShowActivity(client, "%t", "Permabanned player reason", name, reason);
                        }
                } else {
                        if (reason[0] == '\0')
                        {
                                ShowActivity(client, "%t", "Banned player", name, time);
                        } else {
                                ShowActivity(client, "%t", "Banned player reason", name, time, reason);
                        }
                }
                LogAction(client, target, "\"%L\" banned \"%L\" (minutes \"%d\") (reason \"%s\")", client, target, time, reason);

                if (time > 5 || time == 0)
                        time = 5;
                Format(bannedSite, sizeof(bannedSite), "%T", "Banned Check Site", target, WebsiteAddress);
                BanClient(target, time, BANFLAG_AUTO, bannedSite, bannedSite, "sm_ban", client);
        }

        g_BanTarget[client] = -1;
        g_BanTime[client] = -1;
}

stock void ReadConfig()
{
        InitializeConfigParser();

        if (ConfigParser == INVALID_HANDLE)
        {
                return;
        }

        char ConfigFile[PLATFORM_MAX_PATH];
        BuildPath(Path_SM, ConfigFile, sizeof(ConfigFile), "configs/sourcebans/sourcebans.cfg");

        if (FileExists(ConfigFile))
        {
                InternalReadConfig(ConfigFile);
                PrintToServer("%sLoading configs/sourcebans.cfg config file", Prefix);
        } else {
                char Error[PLATFORM_MAX_PATH + 64];
                FormatEx(Error, sizeof(Error), "%sFATAL *** ERROR *** can not find %s", Prefix, ConfigFile);
                LogToFile(logFile, "FATAL *** ERROR *** can not find %s", ConfigFile);
                SetFailState(Error);
        }
}

stock void ResetSettings()
{
        CommandDisable = 0;

        ResetMenu();
        ReadConfig();
}

stock void ParseBackupConfig_Overrides()
{
        Handle hKV = CreateKeyValues("SB_Overrides");

        if (!FileToKeyValues(hKV, overridesLoc))
                return;

        if (!KvGotoFirstSubKey(hKV))
                return;

        char sSection[16], sFlags[32], sName[64];
        OverrideType type;

        do
        {
                KvGetSectionName(hKV, sSection, sizeof(sSection));
                if (StrEqual(sSection, "override_commands"))
                        type = Override_Command;
                else if (StrEqual(sSection, "override_groups"))
                        type = Override_CommandGroup;
                else
                        continue;

                if (KvGotoFirstSubKey(hKV, false))
                {
                        do
                        {
                                KvGetSectionName(hKV, sName, sizeof(sName));
                                KvGetString(hKV, NULL_STRING, sFlags, sizeof(sFlags));
                                AddCommandOverride(sName, type, ReadFlagString(sFlags));
                                #if defined _DEBUG
                                PrintToServer("Adding override (%s, %s, %s)", sSection, sName, sFlags);
                                #endif
                        } while (KvGotoNextKey(hKV, false));
                        KvGoBack(hKV);
                }
        }
        while (KvGotoNextKey(hKV));
        CloseHandle(hKV);
}

stock AdminFlag CreateFlagLetters()
{
        AdminFlag FlagLetters[FLAG_LETTERS_SIZE];

        FlagLetters['a'-'a'] = Admin_Reservation;
        FlagLetters['b'-'a'] = Admin_Generic;
        FlagLetters['c'-'a'] = Admin_Kick;
        FlagLetters['d'-'a'] = Admin_Ban;
        FlagLetters['e'-'a'] = Admin_Unban;
        FlagLetters['f'-'a'] = Admin_Slay;
        FlagLetters['g'-'a'] = Admin_Changemap;
        FlagLetters['h'-'a'] = Admin_Convars;
        FlagLetters['i'-'a'] = Admin_Config;
        FlagLetters['j'-'a'] = Admin_Chat;
        FlagLetters['k'-'a'] = Admin_Vote;
        FlagLetters['l'-'a'] = Admin_Password;
        FlagLetters['m'-'a'] = Admin_RCON;
        FlagLetters['n'-'a'] = Admin_Cheats;
        FlagLetters['o'-'a'] = Admin_Custom1;
        FlagLetters['p'-'a'] = Admin_Custom2;
        FlagLetters['q'-'a'] = Admin_Custom3;
        FlagLetters['r'-'a'] = Admin_Custom4;
        FlagLetters['s'-'a'] = Admin_Custom5;
        FlagLetters['t'-'a'] = Admin_Custom6;
        FlagLetters['z'-'a'] = Admin_Root;

        return FlagLetters;
}

stock AccountForLateLoading()
{
        char auth[30];

        for (new i = 1; i <= GetMaxClients(); i++)
        {
                if (IsClientConnected(i) && !IsFakeClient(i))
                {
                        PlayerStatus[i] = false;
                }
                if (IsClientInGame(i) && !IsFakeClient(i) && IsClientAuthorized(i) && GetClientAuthId(i, AuthId_Steam2, auth, sizeof(auth)))
                {
                        OnClientAuthorized(i, auth);
                }
        }
}
//Yarr!


SyntX 10-29-2022 09:22

Re: sbpp_main compile problem
 
2 Attachment(s)
Quote:

Originally Posted by Elit59 (Post 2791580)
hi.

Code:

SourcePawn Compiler 1.11.0.6915
Copyright (c) 1997-2006 ITB CompuPhase 
Copyright (c) 2004-2021 AlliedModders LLC

d:\sm1.11-3\scripting\sbpp_main.sp(785) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(785) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(785) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(831) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(879) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(2316) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(2316) : warning 209: function has explicit 'int' tag but does not return a value
d:\sm1.11-3\scripting\sbpp_main.sp(2730) : warning 246: function CreateFlagLetters returns an array but return type is not marked as an array
d:\sm1.11-3\scripting\sbpp_main.sp(2737) : warning 234: symbol "GetMaxClients" is marked as deprecated: Use MaxClients variable instead.
Code size:        69816 bytes
Data size:        19276 bytes
Stack/heap size:      19628 bytes
Total requirements:  108720 bytes

9 Warnings.

Code:

// *************************************************************************
//  This file is part of SourceBans++.
//
//  Copyright (C) 2014-2016 SourceBans++ Dev Team <https://github.com/sbpp>
//
//  SourceBans++ 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, per version 3 of the License.
//
//  SourceBans++ 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 SourceBans++. If not, see <http://www.gnu.org/licenses/>.
//
//  This file is based off work(s) covered by the following copyright(s):
//
//  SourceBans 1.4.11
//  Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect
//  Licensed under GNU GPL version 3, or later.
//  Page: <http://www.sourcebans.net/> - <https://github.com/GameConnect/sourcebansv1>
//
// *************************************************************************

#pragma semicolon 1
#include <sourcemod>
#include <sourcebanspp>

#undef REQUIRE_PLUGIN
#include <adminmenu>
#tryinclude <updater>

#define SB_VERSION "1.6.4"
#define SBR_VERSION "1.6.4"

#if defined _updater_included
#define UPDATE_URL "https://sbpp.github.io/updater/updatefile.txt"
#endif

//GLOBAL DEFINES
#define YELLOW                                0x01
#define NAMECOLOR                        0x02
#define TEAMCOLOR                        0x03
#define GREEN                                0x04

#define DISABLE_ADDBAN                1
#define DISABLE_UNBAN                2

#define FLAG_LETTERS_SIZE 26

//#define DEBUG

enum State/* ConfigState */
{
        ConfigStateNone = 0,
        ConfigStateConfig,
        ConfigStateReasons,
        ConfigStateHacking,
        ConfigStateTime
}

State ConfigState;

#define Prefix "[SourceBans++] "

/* Admin Stuff*/
AdminCachePart loadPart;

AdminFlag g_FlagLetters[FLAG_LETTERS_SIZE];

/* Cvar handle*/
ConVar
        CvarHostIp
        , CvarPort;

/* Database handle */
Database DB;
Database SQLiteDB;

char
        ServerIp[24]
        , ServerPort[7]
        , DatabasePrefix[10] = "sb"
        , WebsiteAddress[128]
        , groupsLoc[128] /* Admin KeyValues */
        , adminsLoc[128]
        , overridesLoc[128]
        , logFile[256]; /* Log Stuff */

float RetryTime = 15.0;

bool
        loadAdmins /* Admin Stuff*/
        , loadGroups
        , loadOverrides
        , LateLoaded
        , AutoAdd
        , g_bConnecting = false
        , requireSiteLogin = false /* Require a lastvisited from SB site */
        , backupConfig = true
        , enableAdmins = true
        , PlayerStatus[MAXPLAYERS + 1]; /* Player ban check status */

int
        g_BanTarget[MAXPLAYERS + 1] =  { -1, ... }
        , g_BanTime[MAXPLAYERS + 1] =  { -1, ... }
        , curLoading
        , serverID = -1
        , ProcessQueueTime = 5
        , g_ownReasons[MAXPLAYERS + 1] =  { false, ... } /* Own Chat Reason */
        , CommandDisable; /* Disable of addban and unban */

Handle
        ConfigParser
        , hTopMenu = INVALID_HANDLE
        , TimeMenuHandle /* Menu file globals */
        , ReasonMenuHandle
        , HackingMenuHandle
        , g_hFwd_OnBanAdded
        , g_hFwd_OnReportAdded
        , PlayerRecheck[MAXPLAYERS + 1] =  { INVALID_HANDLE, ... } /* Datapack and Timer handles */
        , PlayerDataPack[MAXPLAYERS + 1] =  { INVALID_HANDLE, ... };

public Plugin myinfo =
{
        name = "SourceBans++: Main Plugin",
        author = "SourceBans Development Team, SourceBans++ Dev Team",
        description = "Advanced ban management for the Source engine",
        version = SBR_VERSION,
        url = "https://sbpp.github.io"
};

#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
#else
public bool AskPluginLoad(Handle myself, bool late, char[] error, int err_max)
#endif
{
        RegPluginLibrary("sourcebans++");

        CreateNative("SBBanPlayer", Native_SBBanPlayer);
        CreateNative("SBPP_BanPlayer", Native_SBBanPlayer);
        CreateNative("SBPP_ReportPlayer", Native_SBReportPlayer);

        g_hFwd_OnBanAdded = CreateGlobalForward("SBPP_OnBanPlayer", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_String);
        g_hFwd_OnReportAdded = CreateGlobalForward("SBPP_OnReportPlayer", ET_Ignore, Param_Cell, Param_Cell, Param_String);

        LateLoaded = late;

        #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3
        return APLRes_Success;
        #else
        return true;
        #endif
}

public OnPluginStart()
{
        LoadTranslations("common.phrases");
        LoadTranslations("plugin.basecommands");
        LoadTranslations("sourcebans.phrases");
        LoadTranslations("basebans.phrases");
        loadAdmins = loadGroups = loadOverrides = false;

        CvarHostIp = FindConVar("hostip");
        CvarPort = FindConVar("hostport");
        CreateConVar("sb_version", SB_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY);
        CreateConVar("sbr_version", SBR_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY);
        RegServerCmd("sm_rehash", sm_rehash, "Reload SQL admins");
        RegAdminCmd("sm_ban", CommandBan, ADMFLAG_BAN, "sm_ban <#userid|name> <minutes|0> [reason]", "sourcebans");
        RegAdminCmd("sm_banip", CommandBanIp, ADMFLAG_BAN, "sm_banip <ip|#userid|name> <time> [reason]", "sourcebans");
        RegAdminCmd("sm_addban", CommandAddBan, ADMFLAG_RCON, "sm_addban <time> <steamid> [reason]", "sourcebans");
        RegAdminCmd("sm_unban", CommandUnban, ADMFLAG_UNBAN, "sm_unban <steamid|ip> [reason]", "sourcebans");
        RegAdminCmd("sb_reload",
                _CmdReload,
                ADMFLAG_RCON,
                "Reload sourcebans config and ban reason menu options",
                "sourcebans");

        RegConsoleCmd("say", ChatHook);
        RegConsoleCmd("say_team", ChatHook);

        if ((TimeMenuHandle = CreateMenu(MenuHandler_BanTimeList, MenuAction_Select|MenuAction_Cancel|MenuAction_DrawItem)) != INVALID_HANDLE)
        {
                SetMenuPagination(TimeMenuHandle, 8);
                SetMenuExitBackButton(TimeMenuHandle, true);
        }

        if ((ReasonMenuHandle = CreateMenu(ReasonSelected)) != INVALID_HANDLE)
        {
                SetMenuPagination(ReasonMenuHandle, 8);
                SetMenuExitBackButton(ReasonMenuHandle, true);
        }

        if ((HackingMenuHandle = CreateMenu(HackingSelected)) != INVALID_HANDLE)
        {
                SetMenuPagination(HackingMenuHandle, 8);
                SetMenuExitBackButton(HackingMenuHandle, true);
        }

        g_FlagLetters = CreateFlagLetters();

        BuildPath(Path_SM, logFile, sizeof(logFile), "logs/sourcebans.log");
        g_bConnecting = true;

        // Catch config error and show link to FAQ
        if (!SQL_CheckConfig("sourcebans"))
        {
                if (ReasonMenuHandle != INVALID_HANDLE)
                        CloseHandle(ReasonMenuHandle);
                if (HackingMenuHandle != INVALID_HANDLE)
                        CloseHandle(HackingMenuHandle);
                LogToFile(logFile, "Database failure: Could not find Database conf \"sourcebans\". See Docs: https://sbpp.github.io/docs/");
                SetFailState("Database failure: Could not find Database conf \"sourcebans\"");
                return;
        }

        Database.Connect(GotDatabase, "sourcebans");

        BuildPath(Path_SM, groupsLoc, sizeof(groupsLoc), "configs/sourcebans/sb_admin_groups.cfg");

        BuildPath(Path_SM, adminsLoc, sizeof(adminsLoc), "configs/sourcebans/sb_admins.cfg");

        BuildPath(Path_SM, overridesLoc, sizeof(overridesLoc), "configs/sourcebans/overrides_backup.cfg");

        InitializeBackupDB();

        // This timer is what processes the SQLite queue when the database is unavailable
        CreateTimer(float(ProcessQueueTime * 60), ProcessQueue);

        if (LateLoaded)
        {
                AccountForLateLoading();
        }

        #if defined _updater_included
        if (LibraryExists("updater"))
        {
                Updater_AddPlugin(UPDATE_URL);
        }
        #endif
}

#if defined _updater_included
public void OnLibraryAdded(const char[] name)
{
        if (StrEqual(name, "updater"))
        {
                Updater_AddPlugin(UPDATE_URL);
        }
}
#endif

public void OnAllPluginsLoaded()
{
        Handle topmenu;
        #if defined DEBUG
        LogToFile(logFile, "OnAllPluginsLoaded()");
        #endif

        if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
        {
                OnAdminMenuReady(topmenu);
        }
}

public void OnConfigsExecuted()
{
        char filename[200];
        BuildPath(Path_SM, filename, sizeof(filename), "plugins/basebans.smx");
        if (FileExists(filename))
        {
                char newfilename[200];
                BuildPath(Path_SM, newfilename, sizeof(newfilename), "plugins/disabled/basebans.smx");
                ServerCommand("sm plugins unload basebans");
                if (FileExists(newfilename))
                        DeleteFile(newfilename);
                RenameFile(newfilename, filename);
                LogToFile(logFile, "plugins/basebans.smx was unloaded and moved to plugins/disabled/basebans.smx");
        }
}

public void OnMapStart()
{
        ResetSettings();
}

public OnMapEnd()
{
        for (int i = 0; i <= MaxClients; i++)
        {
                if (PlayerDataPack[i] != INVALID_HANDLE)
                {
                        /* Need to close reason pack */
                        CloseHandle(PlayerDataPack[i]);
                        PlayerDataPack[i] = INVALID_HANDLE;
                }
        }
}

// CLIENT CONNECTION FUNCTIONS //

public Action OnClientPreAdminCheck(int client)
{
        if (!DB || GetUserAdmin(client) != INVALID_ADMIN_ID)
                return Plugin_Continue;

        return curLoading > 0 ? Plugin_Handled : Plugin_Continue;
}

public OnClientDisconnect(int client)
{
        if (PlayerRecheck[client] != INVALID_HANDLE)
        {
                KillTimer(PlayerRecheck[client]);
                PlayerRecheck[client] = INVALID_HANDLE;
        }
        g_ownReasons[client] = false;
}

public bool OnClientConnect(int client, char[] rejectmsg, int maxlen)
{
        PlayerStatus[client] = false;
        return true;
}

public void OnClientAuthorized(int client, const char[] auth)
{
        /* Do not check bots nor check player with lan steamid. */
        if (auth[0] == 'B' || auth[9] == 'L' || DB == INVALID_HANDLE)
        {
                PlayerStatus[client] = true;
                return;
        }

        char Query[256], ip[30];

        GetClientIP(client, ip, sizeof(ip));

        FormatEx(Query, sizeof(Query), "SELECT bid, ip FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", DatabasePrefix, auth[8], ip);

        #if defined DEBUG
        LogToFile(logFile, "Checking ban for: %s", auth);
        #endif

        DB.Query(VerifyBan, Query, GetClientUserId(client), DBPrio_High);
}

public void OnRebuildAdminCache(AdminCachePart part)
{
        loadPart = part;
        switch (loadPart)
        {
                case AdminCache_Overrides:
                loadOverrides = true;
                case AdminCache_Groups:
                loadGroups = true;
                case AdminCache_Admins:
                loadAdmins = true;
        }
        if (DB == INVALID_HANDLE) {
                if (!g_bConnecting) {
                        g_bConnecting = true;
                        Database.Connect(GotDatabase, "sourcebans");
                }
        }
        else {
                GotDatabase(DB, "", 0);
        }
}

// COMMAND CODE //

public Action ChatHook(int client, int args)
{
        // is this player preparing to ban someone
        if (g_ownReasons[client])
        {
                // get the reason
                char reason[512];
                GetCmdArgString(reason, sizeof(reason));
                StripQuotes(reason);

                g_ownReasons[client] = false;

                if (StrEqual(reason[0], "!noreason"))
                {
                        PrintToChat(client, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Chat Reason Aborted");
                        return Plugin_Handled;
                }

                // ban him!
                PrepareBan(client, g_BanTarget[client], g_BanTime[client], reason, sizeof(reason));

                // block the reason to be sent in chat
                return Plugin_Handled;
        }
        return Plugin_Continue;
}

public Action _CmdReload(int client, int args)
{
        ResetSettings();
        return Plugin_Handled;
}

public Action CommandBan(int client, int args)
{
        if (args < 2)
        {
                ReplyToCommand(client, "%sUsage: sm_ban <#userid|name> <time|0> [reason]", Prefix);
                return Plugin_Handled;
        }

        // This is mainly for me sanity since client used to be called admin and target used to be called client
        int admin = client;

        // Get the target, find target returns a message on failure so we do not
        char buffer[100];

        GetCmdArg(1, buffer, sizeof(buffer));

        int  target = FindTarget(client, buffer, true);

        if (target == -1)
        {
                return Plugin_Handled;
        }

        // Get the ban time
        GetCmdArg(2, buffer, sizeof(buffer));

        int time = StringToInt(buffer);

        if (!time && client && !(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)))
        {
                ReplyToCommand(client, "You do not have Perm Ban Permission");
                return Plugin_Handled;
        }

        // Get the reason
        char reason[128];

        if (args >= 3)
        {
                GetCmdArg(3, reason, sizeof(reason));
                for (new i = 4; i <= args; i++)
                {
                        GetCmdArg(i, buffer, sizeof(buffer));
                        Format(reason, sizeof(reason), "%s %s", reason, buffer);
                }
        }
        else
        {
                reason[0] = '\0';
        }

        g_BanTarget[client] = target;
        g_BanTime[client] = time;

        if (!PlayerStatus[target])
        {
                // The target has not been banned verify. It must be completed before you can ban anyone.
                ReplyToCommand(admin, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Ban Not Verified");
                return Plugin_Handled;
        }


        CreateBan(client, target, time, reason);
        return Plugin_Handled;
}

public Action CommandBanIp(int client, int args)
{
        if (args < 2)
        {
                ReplyToCommand(client, "%sUsage: sm_banip <ip|#userid|name> <time> [reason]", Prefix);
                return Plugin_Handled;
        }

        int len, next_len;
        char Arguments[256], arg[50], time[20];

        GetCmdArgString(Arguments, sizeof(Arguments));
        len = BreakString(Arguments, arg, sizeof(arg));

        if ((next_len = BreakString(Arguments[len], time, sizeof(time))) != -1)
        {
                len += next_len;
        }
        else
        {
                len = 0;
                Arguments[0] = '\0';
        }

        char target_name[MAX_TARGET_LENGTH];
        int target_list[1];
        bool tn_is_ml;

        int target = -1;

        if (ProcessTargetString(
                        arg,
                        client,
                        target_list,
                        1,
                        COMMAND_FILTER_CONNECTED | COMMAND_FILTER_NO_MULTI,
                        target_name,
                        sizeof(target_name),
                        tn_is_ml) > 0)
        {
                target = target_list[0];

                if (!IsFakeClient(target) && CanUserTarget(client, target))
                        GetClientIP(target, arg, sizeof(arg));
        }

        char adminIp[24], adminAuth[64];

        int minutes = StringToInt(time);

        if (!minutes && client && !(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)))
        {
                ReplyToCommand(client, "You do not have Perm Ban Permission");
                return Plugin_Handled;
        }
        if (!client)
        {
                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
                strcopy(adminIp, sizeof(adminIp), ServerIp);
        } else {
                GetClientIP(client, adminIp, sizeof(adminIp));
                GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        dataPack.WriteCell(client);
        dataPack.WriteCell(minutes);
        dataPack.WriteString(Arguments[len]);
        dataPack.WriteString(arg);
        dataPack.WriteString(adminAuth);
        dataPack.WriteString(adminIp);

        char sQuery[256];

        FormatEx(sQuery, sizeof(sQuery), "SELECT bid FROM %s_bans WHERE type = 1 AND ip    = '%s' AND (length = 0 OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL",
                DatabasePrefix, arg);

        SQL_TQuery(DB, SelectBanIpCallback, sQuery, dataPack, DBPrio_High);

        return Plugin_Handled;
}

public Action CommandUnban(int client, int args)
{
        if (args < 1)
        {
                ReplyToCommand(client, "%sUsage: sm_unban <steamid|ip> [reason]", Prefix);
                return Plugin_Handled;
        }

        if (CommandDisable & DISABLE_UNBAN)
        {
                // They must go to the website to unban people
                ReplyToCommand(client, "%s%t", Prefix, "Can Not Unban", WebsiteAddress);
                return Plugin_Handled;
        }

        int len;
        char Arguments[256], arg[50], adminAuth[64];

        GetCmdArgString(Arguments, sizeof(Arguments));

        if ((len = BreakString(Arguments, arg, sizeof(arg))) == -1)
        {
                len = 0;
                Arguments[0] = '\0';
        }
        if (!client)
        {
                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
        } else {
                GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        dataPack.WriteCell(client);
        dataPack.WriteString(Arguments[len]);
        dataPack.WriteString(arg);
        dataPack.WriteString(adminAuth);

        char query[200];

        if (strncmp(arg, "STEAM_", 6) == 0)
        {
                Format(query, sizeof(query), "SELECT bid FROM %s_bans WHERE (type = 0 AND authid = '%s') AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", DatabasePrefix, arg);
        } else {
                Format(query, sizeof(query), "SELECT bid FROM %s_bans WHERE (type = 1 AND ip    = '%s') AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", DatabasePrefix, arg);
        }

        SQL_TQuery(DB, SelectUnbanCallback, query, dataPack);

        return Plugin_Handled;
}

public Action CommandAddBan(int client, int args)
{
        if (args < 2)
        {
                ReplyToCommand(client, "%sUsage: sm_addban <time> <steamid> [reason]", Prefix);
                return Plugin_Handled;
        }

        if (CommandDisable & DISABLE_ADDBAN)
        {
                // They must go to the website to add bans
                ReplyToCommand(client, "%s%t", Prefix, "Can Not Add Ban", WebsiteAddress);
                return Plugin_Handled;
        }

        char arg_string[256], time[50], authid[50];

        GetCmdArgString(arg_string, sizeof(arg_string));

        int len, total_len;

        /* Get time */
        if ((len = BreakString(arg_string, time, sizeof(time))) == -1)
        {
                ReplyToCommand(client, "%sUsage: sm_addban <time> <steamid> [reason]", Prefix);
                return Plugin_Handled;
        }
        total_len += len;

        /* Get steamid */
        if ((len = BreakString(arg_string[total_len], authid, sizeof(authid))) != -1)
        {
                total_len += len;
        }
        else
        {
                total_len = 0;
                arg_string[0] = '\0';
        }

        char adminIp[24], adminAuth[64];

        int  minutes = StringToInt(time);

        if (!minutes && client && !(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)))
        {
                ReplyToCommand(client, "You do not have Perm Ban Permission");
                return Plugin_Handled;
        }
        if (!client)
        {
                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
                strcopy(adminIp, sizeof(adminIp), ServerIp);
        } else {
                GetClientIP(client, adminIp, sizeof(adminIp));
                GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        dataPack.WriteCell(client);
        dataPack.WriteCell(minutes);
        dataPack.WriteString(arg_string[total_len]);
        dataPack.WriteString(authid);
        dataPack.WriteString(adminAuth);
        dataPack.WriteString(adminIp);

        char sQuery[256];

        FormatEx(sQuery, sizeof sQuery, "SELECT bid FROM %s_bans WHERE type = 0 AND authid = '%s' AND (length = 0 OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL",
                DatabasePrefix, authid);

        SQL_TQuery(DB, SelectAddbanCallback, sQuery, dataPack, DBPrio_High);

        return Plugin_Handled;
}

public Action sm_rehash(int args)
{
        if (enableAdmins)
                DumpAdminCache(AdminCache_Groups, true);
        DumpAdminCache(AdminCache_Overrides, true);
        return Plugin_Handled;
}



// MENU CODE //

public void OnAdminMenuReady(Handle topmenu)
{
        #if defined DEBUG
        LogToFile(logFile, "OnAdminMenuReady()");
        #endif

        /* Block us from being called twice */
        if (topmenu == hTopMenu)
        {
                return;
        }

        /* Save the Handle */
        hTopMenu = topmenu;

        /* Find the "Player Commands" category */
        TopMenuObject player_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_PLAYERCOMMANDS);

        if (player_commands != INVALID_TOPMENUOBJECT)
        {
                // just to avoid "unused variable 'res'" warning
                #if defined DEBUG
                TopMenuObject res = AddToTopMenu(hTopMenu,
                        "sm_ban",  // Name
                        TopMenuObject_Item,  // We are a submenu
                        AdminMenu_Ban,  // Handler function
                        player_commands,  // We are a submenu of Player Commands
                        "sm_ban",  // The command to be finally called (Override checks)
                        ADMFLAG_BAN); // What flag do we need to see the menu option
                char temp[125];
                Format(temp, 125, "Result of AddToTopMenu: %d", res);
                LogToFile(logFile, temp);
                LogToFile(logFile, "Added Ban option to admin menu");
                #else
                AddToTopMenu(hTopMenu,
                        "sm_ban",  // Name
                        TopMenuObject_Item,  // We are a submenu
                        AdminMenu_Ban,  // Handler function
                        player_commands,  // We are a submenu of Player Commands
                        "sm_ban",  // The command to be finally called (Override checks)
                        ADMFLAG_BAN); // What flag do we need to see the menu option
                #endif
        }
}

public void AdminMenu_Ban(Handle topmenu,
        TopMenuAction action,  // Action being performed
        TopMenuObject object_id,  // The object ID (if used)
        int param,  // client idx of admin who chose the option (if used)
        char[] buffer,  // Output buffer (if used)
        int maxlength) // Output buffer (if used)
{
        /* Clear the Ownreason bool, so he is able to chat again;) */
        g_ownReasons[param] = false;

        #if defined DEBUG
        LogToFile(logFile, "AdminMenu_Ban()");
        #endif

        switch (action)
        {
                // We are only being displayed, We only need to show the option name
                case TopMenuAction_DisplayOption:
                {
                        FormatEx(buffer, maxlength, "%T", "Ban player", param);

                        #if defined DEBUG
                        LogToFile(logFile, "AdminMenu_Ban() -> Formatted the Ban option text");
                        #endif
                }

                case TopMenuAction_SelectOption:
                {
                        DisplayBanTargetMenu(param); // Someone chose to ban someone, show the list of users menu

                        #if defined DEBUG
                        LogToFile(logFile, "AdminMenu_Ban() -> DisplayBanTargetMenu()");
                        #endif
                }
        }
}

public int ReasonSelected(Handle menu, MenuAction action, int param1, int param2)
{
        switch (action)
        {
                case MenuAction_Select:
                {
                        char info[128], key[128];

                        GetMenuItem(menu, param2, key, sizeof(key), _, info, sizeof(info));

                        if (StrEqual("Hacking", key))
                        {
                                DisplayMenu(HackingMenuHandle, param1, MENU_TIME_FOREVER);
                                return;
                        }

                        else if (StrEqual("Own Reason", key)) // admin wants to use his own reason
                        {
                                g_ownReasons[param1] = true;
                                PrintToChat(param1, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Chat Reason");
                                return;
                        }

                        else if (g_BanTarget[param1] != -1 && g_BanTime[param1] != -1)
                                PrepareBan(param1, g_BanTarget[param1], g_BanTime[param1], info, sizeof(info));
                }

                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_Disconnected)
                        {
                                if (PlayerDataPack[param1] != INVALID_HANDLE)
                                {
                                        CloseHandle(PlayerDataPack[param1]);
                                        PlayerDataPack[param1] = INVALID_HANDLE;
                                }
                        }

                        else
                        {
                                DisplayBanTimeMenu(param1);
                        }
                }
        }
}

public int HackingSelected(Handle menu, MenuAction action, int param1, int param2)
{
        switch (action)
        {
                case MenuAction_Select:
                {
                        char info[128], key[128];

                        GetMenuItem(menu, param2, key, sizeof(key), _, info, sizeof(info));

                        if (g_BanTarget[param1] != -1 && g_BanTime[param1] != -1)
                                PrepareBan(param1, g_BanTarget[param1], g_BanTime[param1], info, sizeof(info));
                }

                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_Disconnected)
                        {
                                DataPack Pack = view_as<DataPack>(PlayerDataPack[param1]);

                                if (Pack != INVALID_HANDLE)
                                {
                                        Pack.ReadCell(); // admin index
                                        Pack.ReadCell(); // target index
                                        Pack.ReadCell(); // admin userid
                                        Pack.ReadCell(); // target userid
                                        Pack.ReadCell(); // time

                                        DataPack ReasonPack = Pack.ReadCell();

                                        if (ReasonPack != INVALID_HANDLE)
                                        {
                                                CloseHandle(ReasonPack);
                                        }

                                        CloseHandle(Pack);
                                        PlayerDataPack[param1] = INVALID_HANDLE;
                                }
                        }

                        else
                        {
                                DisplayMenu(ReasonMenuHandle, param1, MENU_TIME_FOREVER);
                        }
                }
        }
}

public int MenuHandler_BanPlayerList(Handle menu, MenuAction action, int param1, int param2)
{
        #if defined DEBUG
        LogToFile(logFile, "MenuHandler_BanPlayerList()");
        #endif

        switch (action)
        {
                case MenuAction_End:
                {
                        CloseHandle(menu);
                }

                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
                        {
                                DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
                        }
                }

                case MenuAction_Select:
                {
                        char info[32], name[32];
                        int userid, target;

                        GetMenuItem(menu, param2, info, sizeof(info), _, name, sizeof(name));
                        userid = StringToInt(info);

                        if ((target = GetClientOfUserId(userid)) == 0)
                        {
                                PrintToChat(param1, "%s%t", Prefix, "Player no longer available");
                        }
                        else if (!CanUserTarget(param1, target))
                        {
                                PrintToChat(param1, "%s%t", Prefix, "Unable to target");
                        }
                        else
                        {
                                g_BanTarget[param1] = target;
                                DisplayBanTimeMenu(param1);
                        }
                }
        }
}

public int MenuHandler_BanTimeList(Handle menu, MenuAction action, int param1, int param2)
{
        #if defined DEBUG
        LogToFile(logFile, "MenuHandler_BanTimeList()");
        #endif

        switch (action)
        {
                case MenuAction_Cancel:
                {
                        if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE)
                        {
                                DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory);
                        }
                }

                case MenuAction_Select:
                {
                        char info[32];

                        GetMenuItem(menu, param2, info, sizeof(info));
                        g_BanTime[param1] = StringToInt(info);

                        //DisplayBanReasonMenu(param1);
                        DisplayMenu(ReasonMenuHandle, param1, MENU_TIME_FOREVER);
                }

                case MenuAction_DrawItem:
                {
                        char time[16];

                        GetMenuItem(menu, param2, time, sizeof(time));

                        return (StringToInt(time) > 0 || CheckCommandAccess(param1, "sm_unban", ADMFLAG_UNBAN | ADMFLAG_ROOT)) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED;
                }
        }

        return 0;
}

stock void DisplayBanTargetMenu(int client)
{
        #if defined DEBUG
        LogToFile(logFile, "DisplayBanTargetMenu()");
        #endif

        Handle menu = CreateMenu(MenuHandler_BanPlayerList); // Create a new menu, pass it the handler.

        char title[100];

        FormatEx(title, sizeof(title), "%T:", "Ban player", client);

        SetMenuTitle(menu, title); // Set the title
        SetMenuExitBackButton(menu, true); // Yes we want back/exit

        AddTargetsToMenu(menu,  // Add clients to our menu
                client,  // The client that called the display
                false,  // We want to see people connecting
                false); // And dead people

        DisplayMenu(menu, client, MENU_TIME_FOREVER); // Show the menu to the client FOREVER!
}

stock void DisplayBanTimeMenu(int client)
{
        #if defined DEBUG
        LogToFile(logFile, "DisplayBanTimeMenu()");
        #endif

        char title[100];
        FormatEx(title, sizeof(title), "%T:", "Ban player", client);
        SetMenuTitle(TimeMenuHandle, title);

        DisplayMenu(TimeMenuHandle, client, MENU_TIME_FOREVER);
}

stock void ResetMenu()
{
        if (TimeMenuHandle != INVALID_HANDLE)
        {
                RemoveAllMenuItems(TimeMenuHandle);
        }

        if (ReasonMenuHandle != INVALID_HANDLE)
        {
                RemoveAllMenuItems(ReasonMenuHandle);
        }

        if (HackingMenuHandle != INVALID_HANDLE)
        {
                RemoveAllMenuItems(HackingMenuHandle);
        }
}

// QUERY CALL BACKS //

public void GotDatabase(Database db, const char[] error, any data)
{
        if (db == INVALID_HANDLE)
        {
                LogToFile(logFile, "Database failure: %s. See Docs: https://sbpp.github.io/docs/", error);
                g_bConnecting = false;

                // Parse the overrides backup!
                ParseBackupConfig_Overrides();
                return;
        }

        DB = db;

        char query[1024];

        SQL_SetCharset(DB, "utf8");

        InsertServerInfo();

        //CreateTimer(900.0, PruneBans);

        if (loadOverrides)
        {
                Format(query, 1024, "SELECT type, name, flags FROM %s_overrides", DatabasePrefix);
                SQL_TQuery(DB, OverridesDone, query);
                loadOverrides = false;
        }

        if (loadGroups && enableAdmins)
        {
                FormatEx(query, 1024, "SELECT name, flags, immunity, groups_immune  \
                                        FROM %s_srvgroups ORDER BY id", DatabasePrefix);
                curLoading++;
                SQL_TQuery(DB, GroupsDone, query);

                #if defined DEBUG
                LogToFile(logFile, "Fetching Group List");
                #endif
                loadGroups = false;
        }

        if (loadAdmins && enableAdmins)
        {
                char queryLastLogin[50] = "";

                if (requireSiteLogin)
                        queryLastLogin = "lastvisit IS NOT NULL AND lastvisit != '' AND";

                if (serverID == -1)
                {
                        FormatEx(query, 1024, "SELECT authid, srv_password, (SELECT name FROM %s_srvgroups WHERE name = srv_group AND flags != '') AS srv_group, srv_flags, user, immunity  \
                                                FROM %s_admins_servers_groups AS asg \
                                                LEFT JOIN %s_admins AS a ON a.aid = asg.admin_id \
                                                WHERE %s (server_id = (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1)  \
                                                OR srv_group_id = ANY (SELECT group_id FROM %s_servers_groups WHERE server_id = (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1))) \
                                                GROUP BY aid, authid, srv_password, srv_group, srv_flags, user",
                                DatabasePrefix, DatabasePrefix, DatabasePrefix, queryLastLogin, DatabasePrefix, ServerIp, ServerPort, DatabasePrefix, DatabasePrefix, ServerIp, ServerPort);
                } else {
                        FormatEx(query, 1024, "SELECT authid, srv_password, (SELECT name FROM %s_srvgroups WHERE name = srv_group AND flags != '') AS srv_group, srv_flags, user, immunity  \
                                                FROM %s_admins_servers_groups AS asg \
                                                LEFT JOIN %s_admins AS a ON a.aid = asg.admin_id \
                                                WHERE %s server_id = %d  \
                                                OR srv_group_id = ANY (SELECT group_id FROM %s_servers_groups WHERE server_id = %d) \
                                                GROUP BY aid, authid, srv_password, srv_group, srv_flags, user",
                                DatabasePrefix, DatabasePrefix, DatabasePrefix, queryLastLogin, serverID, DatabasePrefix, serverID);
                }
                curLoading++;
                SQL_TQuery(DB, AdminsDone, query);

                #if defined DEBUG
                LogToFile(logFile, "Fetching Admin List");
                LogToFile(logFile, query);
                #endif
                loadAdmins = false;
        }
        g_bConnecting = false;
}

public VerifyInsert(Handle:owner, Handle:hndl, const String:error[], DataPack dataPack)
{
        if (dataPack == INVALID_HANDLE)
        {
                LogToFile(logFile, "Ban Failed: %s", error);
                return;
        }

        if (hndl == INVALID_HANDLE || error[0])
        {
                LogToFile(logFile, "Verify Insert Query Failed: %s", error);

                int admin = dataPack.ReadCell();
                dataPack.ReadCell(); // target
                dataPack.ReadCell(); // admin userid
                dataPack.ReadCell(); // target userid
                int time = dataPack.ReadCell();

                DataPack reasonPack = dataPack.ReadCell();

                char reason[128], name[50], auth[30], ip[20], adminAuth[30], adminIp[20];

                reasonPack.ReadString(reason, sizeof reason);

                dataPack.ReadString(name, sizeof name);
                dataPack.ReadString(auth, sizeof auth);
                dataPack.ReadString(ip, sizeof ip);
                dataPack.ReadString(adminAuth, sizeof adminAuth);
                dataPack.ReadString(adminIp, sizeof adminIp);

                dataPack.Reset();
                reasonPack.Reset();

                PlayerDataPack[admin] = INVALID_HANDLE;
                UTIL_InsertTempBan(time, name, auth, ip, reason, adminAuth, adminIp, dataPack);
                return;
        }

        int admin = dataPack.ReadCell();
        int client = dataPack.ReadCell();

        if (!IsClientConnected(client) || IsFakeClient(client))
                return;

        dataPack.ReadCell(); // admin userid

        int UserId = dataPack.ReadCell();
        int time = dataPack.ReadCell();

        DataPack ReasonPack = dataPack.ReadCell();

        char Name[64], Reason[128];

        dataPack.ReadString(Name, sizeof(Name));
        ReasonPack.ReadString(Reason, sizeof(Reason));

        if (!time)
        {
                if (Reason[0] == '\0')
                {
                        ShowActivityEx(admin, Prefix, "%t", "Permabanned player", Name);
                } else {
                        ShowActivityEx(admin, Prefix, "%t", "Permabanned player reason", Name, Reason);
                }
        } else {
                if (Reason[0] == '\0')
                {
                        ShowActivityEx(admin, Prefix, "%t", "Banned player", Name, time);
                } else {
                        ShowActivityEx(admin, Prefix, "%t", "Banned player reason", Name, time, Reason);
                }
        }

        LogAction(admin, client, "\"%L\" banned \"%L\" (minutes \"%d\") (reason \"%s\")", admin, client, time, Reason);

        if (PlayerDataPack[admin] != INVALID_HANDLE)
        {
                CloseHandle(PlayerDataPack[admin]);
                CloseHandle(ReasonPack);
                PlayerDataPack[admin] = INVALID_HANDLE;
        }

        // Kick player
        if (GetClientUserId(client) == UserId)
                KickClient(client, "%t", "Banned Check Site", WebsiteAddress);
}

public SelectBanIpCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, minutes, String:adminAuth[30], String:adminIp[30], String:banReason[256], String:ip[16], String:Query[512];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        minutes = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, ip, sizeof(ip));
        ReadPackString(data, adminAuth, sizeof(adminAuth));
        ReadPackString(data, adminIp, sizeof(adminIp));
        SQL_EscapeString(DB, reason, banReason, sizeof(banReason));

        if (error[0])
        {
                LogToFile(logFile, "Ban IP Select Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%sFailed to ban %s.", Prefix, ip);
                else
                        PrintToServer("%sFailed to ban %s.", Prefix, ip);
                return;
        }
        if (SQL_GetRowCount(hndl))
        {
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%s%s is already banned.", Prefix, ip);
                else
                        PrintToServer("%s%s is already banned.", Prefix, ip);
                return;
        }
        if (serverID == -1)
        {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (type, ip, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                (1, '%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), ' ')",
                        DatabasePrefix, ip, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, DatabasePrefix, ServerIp, ServerPort);
        } else {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (type, ip, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                (1, '%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                %d, ' ')",
                        DatabasePrefix, ip, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, serverID);
        }

        SQL_TQuery(DB, InsertBanIpCallback, Query, data, DBPrio_High);
}

public InsertBanIpCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        // if the pack is good unpack it and close the handle
        new admin, minutes;
        new String:reason[128];
        decl String:arg[30];
        if (data != INVALID_HANDLE)
        {
                ResetPack(data);
                admin = ReadPackCell(data);
                minutes = ReadPackCell(data);
                ReadPackString(data, reason, sizeof(reason));
                ReadPackString(data, arg, sizeof(arg));
                CloseHandle(data);
        } else {
                // Technically this should not be possible
                ThrowError("Invalid Handle in InsertBanIpCallback");
        }

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Ban IP Insert Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%ssm_banip failed", Prefix);
                return;
        }

        LogAction(admin,
                -1,
                "\"%L\" added ban (minutes \"%d\") (ip \"%s\") (reason \"%s\")",
                admin,
                minutes,
                arg,
                reason);
        if (admin && IsClientInGame(admin))
                PrintToChat(admin, "%s%s successfully banned", Prefix, arg);
        else
                PrintToServer("%s%s successfully banned", Prefix, arg);
}

public SelectUnbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, String:arg[30], String:adminAuth[30], String:unbanReason[256];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, arg, sizeof(arg));
        ReadPackString(data, adminAuth, sizeof(adminAuth));
        SQL_EscapeString(DB, reason, unbanReason, sizeof(unbanReason));

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Unban Select Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%ssm_unban failed", Prefix);
                }
                return;
        }

        // If there was no results then a ban does not exist for that id
        if (hndl == INVALID_HANDLE || !SQL_GetRowCount(hndl))
        {
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%sNo active bans found for that filter", Prefix);
                } else {
                        PrintToServer("%sNo active bans found for that filter", Prefix);
                }
                return;
        }

        // There is ban
        if (hndl != INVALID_HANDLE && SQL_FetchRow(hndl))
        {
                // Get the values from the existing ban record
                new bid = SQL_FetchInt(hndl, 0);

                decl String:query[1000];
                Format(query, sizeof(query), "UPDATE %s_bans SET RemovedBy = (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), RemoveType = 'U', RemovedOn = UNIX_TIMESTAMP(), ureason = '%s' WHERE bid = %d",
                        DatabasePrefix, DatabasePrefix, adminAuth, adminAuth[8], unbanReason, bid);

                SQL_TQuery(DB, InsertUnbanCallback, query, data);
        }
        return;
}

public InsertUnbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        // if the pack is good unpack it and close the handle
        decl admin, String:arg[30];
        new String:reason[128];
        if (data != INVALID_HANDLE)
        {
                ResetPack(data);
                admin = ReadPackCell(data);
                ReadPackString(data, reason, sizeof(reason));
                ReadPackString(data, arg, sizeof(arg));
                CloseHandle(data);
        } else {
                // Technically this should not be possible
                ThrowError("Invalid Handle in InsertUnbanCallback");
        }

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Unban Insert Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%ssm_unban failed", Prefix);
                }
                return;
        }

        LogAction(admin, -1, "\"%L\" removed ban (filter \"%s\") (reason \"%s\")", admin, arg, reason);
        if (admin && IsClientInGame(admin))
        {
                PrintToChat(admin, "%s%s successfully unbanned", Prefix, arg);
        } else {
                PrintToServer("%s%s successfully unbanned", Prefix, arg);
        }
}

public SelectAddbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, minutes, String:adminAuth[30], String:adminIp[30], String:authid[20], String:banReason[256], String:Query[512];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        minutes = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, authid, sizeof(authid));
        ReadPackString(data, adminAuth, sizeof(adminAuth));
        ReadPackString(data, adminIp, sizeof(adminIp));
        SQL_EscapeString(DB, reason, banReason, sizeof(banReason));

        if (error[0])
        {
                LogToFile(logFile, "Add Ban Select Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%sFailed to ban %s.", Prefix, authid);
                else
                        PrintToServer("%sFailed to ban %s.", Prefix, authid);
                return;
        }
        if (SQL_GetRowCount(hndl))
        {
                if (admin && IsClientInGame(admin))
                        PrintToChat(admin, "%s%s is already banned.", Prefix, authid);
                else
                        PrintToServer("%s%s is already banned.", Prefix, authid);
                return;
        }
        if (serverID == -1)
        {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), ' ')",
                        DatabasePrefix, authid, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, DatabasePrefix, ServerIp, ServerPort);
        } else {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                %d, ' ')",
                        DatabasePrefix, authid, (minutes * 60), (minutes * 60), banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, serverID);
        }

        SQL_TQuery(DB, InsertAddbanCallback, Query, data, DBPrio_High);
}

public InsertAddbanCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl admin, minutes, String:authid[20];
        new String:reason[128];
        ResetPack(data);
        admin = ReadPackCell(data);
        minutes = ReadPackCell(data);
        ReadPackString(data, reason, sizeof(reason));
        ReadPackString(data, authid, sizeof(authid));

        // If error is not an empty string the query failed
        if (error[0] != '\0')
        {
                LogToFile(logFile, "Add Ban Insert Query Failed: %s", error);
                if (admin && IsClientInGame(admin))
                {
                        PrintToChat(admin, "%ssm_addban failed", Prefix);
                }
                return;
        }

        LogAction(admin,
                -1,
                "\"%L\" added ban (minutes \"%i\") (id \"%s\") (reason \"%s\")",
                admin,
                minutes,
                authid,
                reason);
        if (admin && IsClientInGame(admin))
        {
                PrintToChat(admin, "%s%s successfully banned", Prefix, authid);
        } else {
                PrintToServer("%s%s successfully banned", Prefix, authid);
        }
}

// ProcessQueueCallback is called as the result of selecting all the rows from the queue table
public ProcessQueueCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE || strlen(error) > 0)
        {
                LogToFile(logFile, "Failed to retrieve queued bans from sqlite database, %s", error);
                return;
        }

        decl String:auth[30];
        decl time;
        decl startTime;
        new String:reason[128];
        decl String:name[64];
        decl String:ip[20];
        decl String:adminAuth[30];
        decl String:adminIp[20];
        decl String:query[1024];
        decl String:banName[128];
        decl String:banReason[256];
        while (SQL_MoreRows(hndl))
        {
                // Oh noes! What happened?!
                if (!SQL_FetchRow(hndl))
                        continue;

                // if we get to here then there are rows in the queue pending processing
                SQL_FetchString(hndl, 0, auth, sizeof(auth));
                time = SQL_FetchInt(hndl, 1);
                startTime = SQL_FetchInt(hndl, 2);
                SQL_FetchString(hndl, 3, reason, sizeof(reason));
                SQL_FetchString(hndl, 4, name, sizeof(name));
                SQL_FetchString(hndl, 5, ip, sizeof(ip));
                SQL_FetchString(hndl, 6, adminAuth, sizeof(adminAuth));
                SQL_FetchString(hndl, 7, adminIp, sizeof(adminIp));
                SQL_EscapeString(SQLiteDB, name, banName, sizeof(banName));
                SQL_EscapeString(SQLiteDB, reason, banReason, sizeof(banReason));
                if (startTime + time * 60 > GetTime() || time == 0)
                {
                        // This ban is still valid and should be entered into the db
                        if (serverID == -1)
                        {
                                FormatEx(query, sizeof(query),
                                        "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid) VALUES  \
                                                ('%s', '%s', '%s', %d, %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1))",
                                        DatabasePrefix, ip, auth, banName, startTime, startTime + time * 60, time * 60, banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, DatabasePrefix, ServerIp, ServerPort);
                        }
                        else
                        {
                                FormatEx(query, sizeof(query),
                                        "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid) VALUES  \
                                                ('%s', '%s', '%s', %d, %d, %d, '%s', (SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '%s', \
                                                %d)",
                                        DatabasePrefix, ip, auth, banName, startTime, startTime + time * 60, time * 60, banReason, DatabasePrefix, adminAuth, adminAuth[8], adminIp, serverID);
                        }
                        new Handle:authPack = CreateDataPack();
                        WritePackString(authPack, auth);
                        ResetPack(authPack);
                        SQL_TQuery(DB, AddedFromSQLiteCallback, query, authPack);
                } else {
                        // The ban is no longer valid and should be deleted from the queue
                        FormatEx(query, sizeof(query), "DELETE FROM queue WHERE steam_id = '%s'", auth);
                        SQL_TQuery(SQLiteDB, ErrorCheckCallback, query);
                }
        }
        // We have finished processing the queue but should process again in ProcessQueueTime minutes
        CreateTimer(float(ProcessQueueTime * 60), ProcessQueue);
}

public AddedFromSQLiteCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        decl String:buffer[512];
        decl String:auth[40];
        ReadPackString(data, auth, sizeof(auth));
        if (error[0] == '\0')
        {
                // The insert was successful so delete the record from the queue
                FormatEx(buffer, sizeof(buffer), "DELETE FROM queue WHERE steam_id = '%s'", auth);
                SQL_TQuery(SQLiteDB, ErrorCheckCallback, buffer);

                // They are added to main banlist, so remove the temp ban
                RemoveBan(auth, BANFLAG_AUTHID);

        } else {
                // the insert failed so we leave the record in the queue and increase our temporary ban
                FormatEx(buffer, sizeof(buffer), "banid %d %s", ProcessQueueTime, auth);
                ServerCommand(buffer);
        }
        CloseHandle(data);
}

public ServerInfoCallback(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (error[0])
        {
                LogToFile(logFile, "Server Select Query Failed: %s", error);
                return;
        }

        if (hndl == INVALID_HANDLE || SQL_GetRowCount(hndl) == 0)
        {
                // get the game folder name used to determine the mod
                decl String:desc[64], String:query[200];
                GetGameFolderName(desc, sizeof(desc));
                FormatEx(query, sizeof(query), "INSERT INTO %s_servers (ip, port, rcon, modid) VALUES ('%s', '%s', '', (SELECT mid FROM %s_mods WHERE modfolder = '%s'))", DatabasePrefix, ServerIp, ServerPort, DatabasePrefix, desc);
                SQL_TQuery(DB, ErrorCheckCallback, query);
        }
}

public ErrorCheckCallback(Handle:owner, Handle:hndle, const String:error[], any:data)
{
        if (error[0])
        {
                LogToFile(logFile, "Query Failed: %s", error);
        }
}

public void VerifyBan(Database db, DBResultSet results, const char[] error, int userid)
{
        char clientName[64], clientAuth[64], clientIp[64];

        int client = GetClientOfUserId(userid);

        if (!client)
                return;

        /* Failure happen. Do retry with delay */
        if (results == null)
        {
                LogToFile(logFile, "Verify Ban Query Failed: %s", error);
                PlayerRecheck[client] = CreateTimer(RetryTime, ClientRecheck, client);
                return;
        }

        GetClientIP(client, clientIp, sizeof(clientIp));
        GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth));
        GetClientName(client, clientName, sizeof(clientName));

        if (results.RowCount > 0)
        {
                char buffer[40], Name[128], Query[512];

                // Amending to ban record's IP field
                if (results.FetchRow())
                {
                        char sIP[32];

                        int iBid = results.FetchInt(0);
                        results.FetchString(1, sIP, sizeof sIP);

                        if (StrEqual(sIP, ""))
                        {
                                char sQuery[256];

                                FormatEx(sQuery, sizeof sQuery, "UPDATE %s_bans SET `ip` = '%s' WHERE `bid` = '%d'", DatabasePrefix, clientIp, iBid);

                                DB.Query(SQL_OnIPMend, sQuery, client);
                        }
                }

                DB.Escape(clientName, Name, sizeof Name);

                if (serverID == -1)
                {
                        FormatEx(Query, sizeof(Query), "INSERT INTO %s_banlog (sid ,time ,name ,bid) VALUES  \
                                ((SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), UNIX_TIMESTAMP(), '%s', \
                                (SELECT bid FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND RemoveType IS NULL LIMIT 0,1))",
                                DatabasePrefix, DatabasePrefix, ServerIp, ServerPort, Name, DatabasePrefix, clientAuth[8], clientIp);
                }
                else
                {
                        FormatEx(Query, sizeof(Query), "INSERT INTO %s_banlog (sid ,time ,name ,bid) VALUES  \
                                (%d, UNIX_TIMESTAMP(), '%s', \
                                (SELECT bid FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND RemoveType IS NULL LIMIT 0,1))",
                                DatabasePrefix, serverID, Name, DatabasePrefix, clientAuth[8], clientIp);
                }

                SQL_TQuery(DB, ErrorCheckCallback, Query, client, DBPrio_High);

                FormatEx(buffer, sizeof(buffer), "banid 5 %s", clientAuth);
                ServerCommand(buffer);
                KickClient(client, "%t", "Banned Check Site", WebsiteAddress);

                return;
        }

        #if defined DEBUG
        LogToFile(logFile, "%s is NOT banned.", clientAuth);
        #endif

        PlayerStatus[client] = true;
}

public void SQL_OnIPMend(Database db, DBResultSet results, const char[] error, int iClient)
{
        if (results == null)
        {
                char sIP[32], sSteamID[32];

                GetClientAuthId(iClient, AuthId_Steam3, sSteamID, sizeof sSteamID);
                GetClientIP(iClient, sIP, sizeof sIP);

                LogToFile(logFile, "Failed to mend IP address for %s (%s): %s", sSteamID, sIP, error);
        }
}

public AdminsDone(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        //SELECT authid, srv_password , srv_group, srv_flags, user
        if (hndl == INVALID_HANDLE || strlen(error) > 0)
        {
                --curLoading;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve admins from the database, %s", error);
                return;
        }
        decl String:authType[] = "steam";
        decl String:identity[66];
        decl String:password[66];
        decl String:groups[256];
        decl String:flags[32];
        decl String:name[66];
        new admCount = 0;
        new Immunity = 0;
        new AdminId:curAdm = INVALID_ADMIN_ID;
        new Handle:adminsKV = CreateKeyValues("Admins");

        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups

                SQL_FetchString(hndl, 0, identity, 66);
                SQL_FetchString(hndl, 1, password, 66);
                SQL_FetchString(hndl, 2, groups, 256);
                SQL_FetchString(hndl, 3, flags, 32);
                SQL_FetchString(hndl, 4, name, 66);

                Immunity = SQL_FetchInt(hndl, 5);

                TrimString(name);
                TrimString(identity);
                TrimString(groups);
                TrimString(flags);

                // Disable writing to file if they chose to
                if (backupConfig)
                {
                        KvJumpToKey(adminsKV, name, true);

                        KvSetString(adminsKV, "auth", authType);
                        KvSetString(adminsKV, "identity", identity);

                        if (strlen(flags) > 0)
                                KvSetString(adminsKV, "flags", flags);

                        if (strlen(groups) > 0)
                                KvSetString(adminsKV, "group", groups);

                        if (strlen(password) > 0)
                                KvSetString(adminsKV, "password", password);

                        if (Immunity > 0)
                                KvSetNum(adminsKV, "immunity", Immunity);

                        KvRewind(adminsKV);
                }

                // find or create the admin using that identity
                if ((curAdm = FindAdminByIdentity(authType, identity)) == INVALID_ADMIN_ID)
                {
                        curAdm = CreateAdmin(name);
                        // That should never happen!
                        if (!BindAdminIdentity(curAdm, authType, identity))
                        {
                                LogToFile(logFile, "Unable to bind admin %s to identity %s", name, identity);
                                RemoveAdmin(curAdm);
                                continue;
                        }
                }

                #if defined DEBUG
                LogToFile(logFile, "Given %s (%s) admin", name, identity);
                #endif

                new curPos = 0;
                new GroupId:curGrp = INVALID_GROUP_ID;
                new numGroups;
                decl String:iterGroupName[64];

                // Who thought this comma seperated group parsing would be a good idea?!
                /*
                decl String:grp[64];
                new nextPos = 0;
                while ((nextPos = SplitString(groups[curPos],",",grp,64)) != -1)
                {
                        curPos += nextPos;
                        curGrp = FindAdmGroup(grp);
                        if (curGrp == INVALID_GROUP_ID)
                        {
                                LogToFile(logFile, "Unknown group \"%s\"",grp);
                        }
                        else
                        {
                                // Check, if he's not in the group already.
                                numGroups = GetAdminGroupCount(curAdm);
                                for(new i=0;i<numGroups;i++)
                                {
                                        GetAdminGroup(curAdm, i, iterGroupName, sizeof(iterGroupName));
                                        // Admin is already part of the group, so don't try to inherit its permissions.
                                        if(StrEqual(iterGroupName, grp))
                                        {
                                                numGroups = -2;
                                                break;
                                        }
                                }
                                // Only try to inherit the group, if it's a new one.
                                if (numGroups != -2 && !AdminInheritGroup(curAdm,curGrp))
                                {
                                        LogToFile(logFile, "Unable to inherit group \"%s\"",grp);
                                }
                        }
                }*/

                if (strcmp(groups[curPos], "") != 0)
                {
                        curGrp = FindAdmGroup(groups[curPos]);
                        if (curGrp == INVALID_GROUP_ID)
                        {
                                LogToFile(logFile, "Unknown group \"%s\"", groups[curPos]);
                        }
                        else
                        {
                                // Check, if he's not in the group already.
                                numGroups = GetAdminGroupCount(curAdm);
                                for (new i = 0; i < numGroups; i++)
                                {
                                        GetAdminGroup(curAdm, i, iterGroupName, sizeof(iterGroupName));
                                        // Admin is already part of the group, so don't try to inherit its permissions.
                                        if (StrEqual(iterGroupName, groups[curPos]))
                                        {
                                                numGroups = -2;
                                                break;
                                        }
                                }

                                // Only try to inherit the group, if it's a new one.
                                if (numGroups != -2 && !AdminInheritGroup(curAdm, curGrp))
                                {
                                        LogToFile(logFile, "Unable to inherit group \"%s\"", groups[curPos]);
                                }

                                if (GetAdminImmunityLevel(curAdm) < Immunity)
                                {
                                        SetAdminImmunityLevel(curAdm, Immunity);
                                }
                                #if defined DEBUG
                                LogToFile(logFile, "Admin %s (%s) has %d immunity", name, identity, Immunity);
                                #endif
                        }
                }

                if (strlen(password) > 0)
                        SetAdminPassword(curAdm, password);

                for (new i = 0; i < strlen(flags); ++i)
                {
                        if (flags[i] < 'a' || flags[i] > 'z')
                                continue;

                        if (g_FlagLetters[flags[i]-'a'] < Admin_Reservation)
                                continue;

                        SetAdminFlag(curAdm, g_FlagLetters[flags[i]-'a'], true);
                }
                ++admCount;
        }

        if (backupConfig)
                KeyValuesToFile(adminsKV, adminsLoc);
        CloseHandle(adminsKV);

        #if defined DEBUG
        LogToFile(logFile, "Finished loading %i admins.", admCount);
        #endif

        --curLoading;
        CheckLoadAdmins();
}

public GroupsDone(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                curLoading--;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve groups from the database, %s", error);
                return;
        }
        decl String:grpName[128], String:immuneGrpName[128];
        decl String:grpFlags[32];
        new Immunity;
        new grpCount = 0;
        new Handle:groupsKV = CreateKeyValues("Groups");

        new GroupId:curGrp = INVALID_GROUP_ID;
        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups
                SQL_FetchString(hndl, 0, grpName, 128);
                SQL_FetchString(hndl, 1, grpFlags, 32);
                Immunity = SQL_FetchInt(hndl, 2);
                SQL_FetchString(hndl, 3, immuneGrpName, 128);

                TrimString(grpName);
                TrimString(grpFlags);
                TrimString(immuneGrpName);

                // Ignore empty rows..
                if (!strlen(grpName))
                        continue;

                curGrp = CreateAdmGroup(grpName);

                if (backupConfig)
                {
                        KvJumpToKey(groupsKV, grpName, true);
                        if (strlen(grpFlags) > 0)
                                KvSetString(groupsKV, "flags", grpFlags);
                        if (Immunity > 0)
                                KvSetNum(groupsKV, "immunity", Immunity);

                        KvRewind(groupsKV);
                }

                if (curGrp == INVALID_GROUP_ID)
                {  //This occurs when the group already exists
                        curGrp = FindAdmGroup(grpName);
                }

                for (new i = 0; i < strlen(grpFlags); ++i)
                {
                        if (grpFlags[i] < 'a' || grpFlags[i] > 'z')
                                continue;

                        if (g_FlagLetters[grpFlags[i]-'a'] < Admin_Reservation)
                                continue;

                        SetAdmGroupAddFlag(curGrp, g_FlagLetters[grpFlags[i]-'a'], true);
                }

                // Set the group immunity.
                if (Immunity > 0)
                {
                        SetAdmGroupImmunityLevel(curGrp, Immunity);
                        #if defined DEBUG
                        LogToFile(logFile, "Group %s has %d immunity", grpName, Immunity);
                        #endif
                }

                grpCount++;
        }

        if (backupConfig)
                KeyValuesToFile(groupsKV, groupsLoc);
        CloseHandle(groupsKV);

        #if defined DEBUG
        LogToFile(logFile, "Finished loading %i groups.", grpCount);
        #endif

        // Load the group overrides
        decl String:query[512];
        FormatEx(query, 512, "SELECT sg.name, so.type, so.name, so.access FROM %s_srvgroups_overrides so LEFT JOIN %s_srvgroups sg ON sg.id = so.group_id ORDER BY sg.id", DatabasePrefix, DatabasePrefix);
        SQL_TQuery(DB, LoadGroupsOverrides, query);

        /*if (reparse)
        {
                decl String:query[512];
                FormatEx(query,512,"SELECT name, immunity, groups_immune FROM %s_srvgroups ORDER BY id",DatabasePrefix);
                SQL_TQuery(DB,GroupsSecondPass,query);
        }
        else
        {
                curLoading--;
                CheckLoadAdmins();
        }*/
}

// Reparse to apply inherited immunity
public GroupsSecondPass(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                curLoading--;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve groups from the database, %s", error);
                return;
        }
        decl String:grpName[128], String:immunityGrpName[128];

        new GroupId:curGrp = INVALID_GROUP_ID;
        new GroupId:immuneGrp = INVALID_GROUP_ID;
        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups

                SQL_FetchString(hndl, 0, grpName, 128);
                TrimString(grpName);
                if (strlen(grpName) == 0)
                        continue;

                SQL_FetchString(hndl, 2, immunityGrpName, sizeof(immunityGrpName));
                TrimString(immunityGrpName);

                curGrp = FindAdmGroup(grpName);
                if (curGrp == INVALID_GROUP_ID)
                        continue;

                immuneGrp = FindAdmGroup(immunityGrpName);
                if (immuneGrp == INVALID_GROUP_ID)
                        continue;

                SetAdmGroupImmuneFrom(curGrp, immuneGrp);

                #if defined DEBUG
                LogToFile(logFile, "Group %s inhertied immunity from group %s", grpName, immunityGrpName);
                #endif
        }
        --curLoading;
        CheckLoadAdmins();
}

public LoadGroupsOverrides(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                curLoading--;
                CheckLoadAdmins();
                LogToFile(logFile, "Failed to retrieve group overrides from the database, %s", error);
                return;
        }
        decl String:sGroupName[128], String:sType[16], String:sCommand[64], String:sAllowed[16];
        decl OverrideRule:iRule, OverrideType:iType;

        new Handle:groupsKV = CreateKeyValues("Groups");
        FileToKeyValues(groupsKV, groupsLoc);

        new GroupId:curGrp = INVALID_GROUP_ID;
        while (SQL_MoreRows(hndl))
        {
                SQL_FetchRow(hndl);
                if (SQL_IsFieldNull(hndl, 0))
                        continue; // Sometimes some rows return NULL due to some setups

                SQL_FetchString(hndl, 0, sGroupName, sizeof(sGroupName));
                TrimString(sGroupName);
                if (strlen(sGroupName) == 0)
                        continue;

                SQL_FetchString(hndl, 1, sType, sizeof(sType));
                SQL_FetchString(hndl, 2, sCommand, sizeof(sCommand));
                SQL_FetchString(hndl, 3, sAllowed, sizeof(sAllowed));

                curGrp = FindAdmGroup(sGroupName);
                if (curGrp == INVALID_GROUP_ID)
                        continue;

                iRule = StrEqual(sAllowed, "allow") ? Command_Allow : Command_Deny;
                iType = StrEqual(sType, "group") ? Override_CommandGroup : Override_Command;

                #if defined DEBUG
                PrintToServer("AddAdmGroupCmdOverride(%i, %s, %i, %i)", curGrp, sCommand, iType, iRule);
                #endif

                // Save overrides into admin_groups.cfg backup
                if (KvJumpToKey(groupsKV, sGroupName))
                {
                        KvJumpToKey(groupsKV, "Overrides", true);
                        if (iType == Override_Command)
                                KvSetString(groupsKV, sCommand, sAllowed);
                        else
                        {
                                Format(sCommand, sizeof(sCommand), "@%s", sCommand);
                                KvSetString(groupsKV, sCommand, sAllowed);
                        }
                        KvRewind(groupsKV);
                }

                AddAdmGroupCmdOverride(curGrp, sCommand, iType, iRule);
        }
        curLoading--;
        CheckLoadAdmins();

        if (backupConfig)
                KeyValuesToFile(groupsKV, groupsLoc);
        CloseHandle(groupsKV);
}

public OverridesDone(Handle:owner, Handle:hndl, const String:error[], any:data)
{
        if (hndl == INVALID_HANDLE)
        {
                LogToFile(logFile, "Failed to retrieve overrides from the database, %s", error);
                ParseBackupConfig_Overrides();
                return;
        }

        new Handle:hKV = CreateKeyValues("SB_Overrides");

        decl String:sFlags[32], String:sName[64], String:sType[64];
        while (SQL_FetchRow(hndl))
        {
                SQL_FetchString(hndl, 0, sType, sizeof(sType));
                SQL_FetchString(hndl, 1, sName, sizeof(sName));
                SQL_FetchString(hndl, 2, sFlags, sizeof(sFlags));

                // KeyValuesToFile won't add that key, if the value is ""..
                if (sFlags[0] == '\0')
                {
                        sFlags[0] = ' ';
                        sFlags[1] = '\0';
                }

                #if defined DEBUG
                LogToFile(logFile, "Adding override (%s, %s, %s)", sType, sName, sFlags);
                #endif

                if (StrEqual(sType, "command"))
                {
                        AddCommandOverride(sName, Override_Command, ReadFlagString(sFlags));
                        KvJumpToKey(hKV, "override_commands", true);
                        KvSetString(hKV, sName, sFlags);
                        KvGoBack(hKV);
                }
                else if (StrEqual(sType, "group"))
                {
                        AddCommandOverride(sName, Override_CommandGroup, ReadFlagString(sFlags));
                        KvJumpToKey(hKV, "override_groups", true);
                        KvSetString(hKV, sName, sFlags);
                        KvGoBack(hKV);
                }
        }

        KvRewind(hKV);

        if (backupConfig)
                KeyValuesToFile(hKV, overridesLoc);
        CloseHandle(hKV);
}

// TIMER CALL BACKS //

public Action:ClientRecheck(Handle:timer, any:client)
{
        decl String:Authid[64];
        if (!PlayerStatus[client] && IsClientConnected(client) && GetClientAuthId(client, AuthId_Steam2, Authid, sizeof(Authid)))
        {
                OnClientAuthorized(client, Authid);
        }

        PlayerRecheck[client] = INVALID_HANDLE;
        return Plugin_Stop;
}

/*
public Action:PruneBans(Handle:timer)
{
        decl String:Query[512];
        FormatEx(Query, sizeof(Query),
                        "UPDATE %s_bans SET RemovedBy = 0, RemoveType = 'E', RemovedOn = UNIX_TIMESTAMP() WHERE length != '0' AND ends < UNIX_TIMESTAMP()",
                        DatabasePrefix);

        SQL_TQuery(DB, ErrorCheckCallback, Query);
        return Plugin_Continue;
}
*/

public Action:ProcessQueue(Handle:timer, any:data)
{
        decl String:buffer[512];
        Format(buffer, sizeof(buffer), "SELECT steam_id, time, start_time, reason, name, ip, admin_id, admin_ip FROM queue");
        SQL_TQuery(SQLiteDB, ProcessQueueCallback, buffer);
}

// PARSER //

static InitializeConfigParser()
{
        if (ConfigParser == INVALID_HANDLE)
        {
                ConfigParser = SMC_CreateParser();
                SMC_SetReaders(ConfigParser, ReadConfig_NewSection, ReadConfig_KeyValue, ReadConfig_EndSection);
        }
}

static InternalReadConfig(const String:path[])
{
        ConfigState = ConfigStateNone;

        new SMCError:err = SMC_ParseFile(ConfigParser, path);

        if (err != SMCError_Okay)
        {
                decl String:buffer[64];
                PrintToServer("%s", SMC_GetErrorString(err, buffer, sizeof(buffer)) ? buffer : "Fatal parse error");
        }
}

public SMCResult:ReadConfig_NewSection(Handle:smc, const String:name[], bool:opt_quotes)
{
        if (name[0])
        {
                if (strcmp("Config", name, false) == 0) {
                        ConfigState = ConfigStateConfig;
                } else if (strcmp("BanReasons", name, false) == 0) {
                        ConfigState = ConfigStateReasons;
                } else if (strcmp("HackingReasons", name, false) == 0) {
                        ConfigState = ConfigStateHacking;
                } else if (strcmp("BanTime", name, false) == 0) {
                        ConfigState = ConfigStateTime;
                }
        }
        return SMCParse_Continue;
}

public SMCResult:ReadConfig_KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes)
{
        if (!key[0])
                return SMCParse_Continue;

        switch (ConfigState)
        {
                case ConfigStateConfig:
                {
                        if (strcmp("website", key, false) == 0)
                        {
                                strcopy(WebsiteAddress, sizeof(WebsiteAddress), value);
                        }
                        else if (strcmp("Addban", key, false) == 0)
                        {
                                if (StringToInt(value) == 0)
                                {
                                        CommandDisable |= DISABLE_ADDBAN;
                                }
                        }
                        else if (strcmp("AutoAddServer", key, false) == 0)
                        {
                                AutoAdd = StringToInt(value) == 1;
                        }
                        else if (strcmp("Unban", key, false) == 0)
                        {
                                if (StringToInt(value) == 0)
                                {
                                        CommandDisable |= DISABLE_UNBAN;
                                }
                        }
                        else if (strcmp("DatabasePrefix", key, false) == 0)
                        {
                                strcopy(DatabasePrefix, sizeof(DatabasePrefix), value);

                                if (DatabasePrefix[0] == '\0')
                                {
                                        DatabasePrefix = "sb";
                                }
                        }
                        else if (strcmp("RetryTime", key, false) == 0)
                        {
                                RetryTime = StringToFloat(value);
                                if (RetryTime < 15.0)
                                {
                                        RetryTime = 15.0;
                                } else if (RetryTime > 60.0) {
                                        RetryTime = 60.0;
                                }
                        }
                        else if (strcmp("ProcessQueueTime", key, false) == 0)
                        {
                                ProcessQueueTime = StringToInt(value);
                        }
                        else if (strcmp("BackupConfigs", key, false) == 0)
                        {
                                backupConfig = StringToInt(value) == 1;
                        }
                        else if (strcmp("EnableAdmins", key, false) == 0)
                        {
                                enableAdmins = StringToInt(value) == 1;
                        }
                        else if (strcmp("RequireSiteLogin", key, false) == 0)
                        {
                                requireSiteLogin = StringToInt(value) == 1;
                        }
                        else if (strcmp("ServerID", key, false) == 0)
                        {
                                serverID = StringToInt(value);
                        }
                }

                case ConfigStateReasons:
                {
                        if (ReasonMenuHandle != INVALID_HANDLE)
                        {
                                AddMenuItem(ReasonMenuHandle, key, value);
                        }
                }
                case ConfigStateHacking:
                {
                        if (HackingMenuHandle != INVALID_HANDLE)
                        {
                                AddMenuItem(HackingMenuHandle, key, value);
                        }
                }
                case ConfigStateTime:
                {
                        if (StringToInt(key) > -1 && TimeMenuHandle != INVALID_HANDLE)
                        {
                                AddMenuItem(TimeMenuHandle, key, value);
                        }
                }
        }
        return SMCParse_Continue;
}

public SMCResult:ReadConfig_EndSection(Handle:smc)
{
        return SMCParse_Continue;
}


/*********************************************************
 * Ban Player from server
 *
 * @param client        The client index of the player to ban
 * @param time                The time to ban the player for (in minutes, 0 = permanent)
 * @param reason        The reason to ban the player from the server
 * @noreturn
 *********************************************************/
public Native_SBBanPlayer(Handle plugin, int numParams)
{
        int client = GetNativeCell(1);
        int target = GetNativeCell(2);
        int time = GetNativeCell(3);
        char reason[128];
        GetNativeString(4, reason, 128);

        if (reason[0] == '\0')
                strcopy(reason, sizeof(reason), "Banned by SourceBans");

        if (client && IsClientInGame(client))
        {
                AdminId aid = GetUserAdmin(client);
                if (aid == INVALID_ADMIN_ID)
                {
                        ThrowNativeError(SP_ERROR_NATIVE, "Ban Error: Player is not an admin.");
                        return 0;
                }

                if (!GetAdminFlag(aid, Admin_Ban))
                {
                        ThrowNativeError(SP_ERROR_NATIVE, "Ban Error: Player does not have BAN flag.");
                        return 0;
                }
        }

        PrepareBan(client, target, time, reason, sizeof(reason));
        return true;
}

public int Native_SBReportPlayer(Handle plugin, int numParams)
{
        if (numParams < 3)
        {
                ThrowNativeError(SP_ERROR_NATIVE, "Invalid amount of arguments. Received %d arguments", numParams);
                return;
        }

        int iReporter = GetNativeCell(1)
          , iTarget = GetNativeCell(2)
          , iReasonLen;

        int iTime = GetTime();

        GetNativeStringLength(3, iReasonLen);

        iReasonLen++;

        char[] sReason = new char[iReasonLen];

        GetNativeString(3, sReason, iReasonLen);

        char sRAuth[32], sTAuth[32], sRName[MAX_NAME_LENGTH + 1], sTName[MAX_NAME_LENGTH + 1],
        sRIP[16], sTIP[16], sREscapedName[MAX_NAME_LENGTH * 2 + 1], sTEscapedName[MAX_NAME_LENGTH * 2 + 1];

        char[] sEscapedReason = new char[iReasonLen * 2 + 1];

        GetClientAuthId(iReporter, AuthId_Steam2, sRAuth, sizeof sRAuth);
        GetClientAuthId(iTarget, AuthId_Steam2, sTAuth, sizeof sTAuth);

        GetClientName(iReporter, sRName, sizeof sRName);
        GetClientName(iTarget, sTName, sizeof sTName);

        GetClientIP(iReporter, sRIP, sizeof sRIP);
        GetClientIP(iTarget, sTIP, sizeof sTIP);

        DB.Escape(sRName, sREscapedName, sizeof sREscapedName);
        DB.Escape(sTName, sTEscapedName, sizeof sTEscapedName);
        DB.Escape(sReason, sEscapedReason, iReasonLen * 2 + 1);

        char[] sQuery = new char[512 + (iReasonLen * 2 + 1)];

        Format(sQuery, 512 + (iReasonLen * 2 + 1), "INSERT INTO %s_submissions (`submitted`, `modid`, `SteamId`, `name`, `email`, `reason`, `ip`, `subname`, `sip`, `archiv`, `server`)"
        ... "VALUES ('%d', 0, '%s', '%s', '%s', '%s', '%s', '%s', '%s', 0, 0)", DatabasePrefix, iTime, sTAuth, sTEscapedName, sRAuth, sEscapedReason, sRIP, sREscapedName, sTIP);

        DataPack ForwardPack = new DataPack();

        ForwardPack.WriteCell(iReporter);
        ForwardPack.WriteCell(iTarget);
        ForwardPack.WriteCell(iReasonLen);
        ForwardPack.WriteString(sReason);

        DB.Query(SQL_OnReportPlayer, sQuery, ForwardPack);
}

public void SQL_OnReportPlayer(Database db, DBResultSet results, const char[] error, DataPack ForwardPack)
{
        if (results == null)
                LogToFile(logFile, "Failed to submit report: %s", error);
        else
        {
                ForwardPack.Reset();

                int iReporter = ForwardPack.ReadCell();
                int iTarget = ForwardPack.ReadCell();
                int iReasonLen = ForwardPack.ReadCell();

                char[] sReason = new char[iReasonLen];

                ForwardPack.ReadString(sReason, iReasonLen);

                Call_StartForward(g_hFwd_OnReportAdded);
                Call_PushCell(iReporter);
                Call_PushCell(iTarget);
                Call_PushString(sReason);
                Call_Finish();
        }
}

// STOCK FUNCTIONS //

public InitializeBackupDB()
{
        char error[255];

        SQLiteDB = SQLite_UseDatabase("sourcebans-queue", error, sizeof(error));
        if (SQLiteDB == INVALID_HANDLE)
                SetFailState(error);

        SQL_LockDatabase(SQLiteDB);
        SQL_FastQuery(SQLiteDB, "CREATE TABLE IF NOT EXISTS queue (steam_id TEXT PRIMARY KEY ON CONFLICT REPLACE, time INTEGER, start_time INTEGER, reason TEXT, name TEXT, ip TEXT, admin_id TEXT, admin_ip TEXT);");
        SQL_UnlockDatabase(SQLiteDB);
}

public bool CreateBan(int client, int target, int time, const char[] reason)
{
        char adminIp[24], adminAuth[64];
        int admin = client;

        // The server is the one calling the ban
        if (!admin)
        {
                if (reason[0] == '\0')
                {
                        // We cannot pop the reason menu if the command was issued from the server
                        PrintToServer("%s%T", Prefix, "Include Reason", LANG_SERVER);
                        return false;
                }

                // setup dummy adminAuth and adminIp for server
                strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER");
                strcopy(adminIp, sizeof(adminIp), ServerIp);
        } else {
                GetClientIP(admin, adminIp, sizeof(adminIp));
                GetClientAuthId(admin, AuthId_Steam2, adminAuth, sizeof(adminAuth));
        }

        // target information
        char ip[24], auth[64], name[64];

        GetClientName(target, name, sizeof(name));
        GetClientIP(target, ip, sizeof(ip));
        if (!GetClientAuthId(target, AuthId_Steam2, auth, sizeof(auth)))
                return false;

        int userid = admin ? GetClientUserId(admin) : 0;

        // Pack everything into a data pack so we can retain it
        DataPack dataPack = new DataPack();
        DataPack reasonPack = new DataPack();

        WritePackString(reasonPack, reason);

        dataPack.WriteCell(admin);
        dataPack.WriteCell(target);
        dataPack.WriteCell(userid);
        dataPack.WriteCell(GetClientUserId(target));
        dataPack.WriteCell(time);
        dataPack.WriteCell( _:reasonPack);
        dataPack.WriteString(name);
        dataPack.WriteString(auth);
        dataPack.WriteString(ip);
        dataPack.WriteString(adminAuth);
        dataPack.WriteString(adminIp);

        dataPack.Reset();
        reasonPack.Reset();

        if (reason[0] != '\0')
        {
                // if we have a valid reason pass move forward with the ban
                if (DB != INVALID_HANDLE)
                {
                        UTIL_InsertBan(time, name, auth, ip, reason, adminAuth, adminIp, dataPack);
                } else {
                        UTIL_InsertTempBan(time, name, auth, ip, reason, adminAuth, adminIp, dataPack);
                }
        } else {
                // We need a reason so offer the administrator a menu of reasons
                PlayerDataPack[admin] = dataPack;
                DisplayMenu(ReasonMenuHandle, admin, MENU_TIME_FOREVER);
                ReplyToCommand(admin, "%c[%cSourceBans%c]%c %t", GREEN, NAMECOLOR, GREEN, NAMECOLOR, "Check Menu");
        }

        Call_StartForward(g_hFwd_OnBanAdded);
        Call_PushCell(client);
        Call_PushCell(target);
        Call_PushCell(time);
        Call_PushString(reason);
        Call_Finish();

        return true;
}

stock UTIL_InsertBan(time, const String:Name[], const String:Authid[], const String:Ip[], const String:Reason[], const String:AdminAuthid[], const String:AdminIp[], Handle:Pack)
{
        //new Handle:dummy;
        //PruneBans(dummy);
        decl String:banName[128];
        decl String:banReason[256];
        decl String:Query[1024];
        SQL_EscapeString(DB, Name, banName, sizeof(banName));
        SQL_EscapeString(DB, Reason, banReason, sizeof(banReason));
        if (serverID == -1)
        {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'),'0'), '%s', \
                                                (SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s' LIMIT 0,1), ' ')",
                        DatabasePrefix, Ip, Authid, banName, (time * 60), (time * 60), banReason, DatabasePrefix, AdminAuthid, AdminAuthid[8], AdminIp, DatabasePrefix, ServerIp, ServerPort);
        } else {
                FormatEx(Query, sizeof(Query), "INSERT INTO %s_bans (ip, authid, name, created, ends, length, reason, aid, adminIp, sid, country) VALUES \
                                                ('%s', '%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'),'0'), '%s', \
                                                %d, ' ')",
                        DatabasePrefix, Ip, Authid, banName, (time * 60), (time * 60), banReason, DatabasePrefix, AdminAuthid, AdminAuthid[8], AdminIp, serverID);
        }

        SQL_TQuery(DB, VerifyInsert, Query, Pack, DBPrio_High);
}

stock UTIL_InsertTempBan(int time, const char[] name, const char[] auth, const char[] ip, const char[] reason, const char[] adminAuth, const char[] adminIp, DataPack dataPack)
{
        dataPack.ReadCell(); // admin index

        int client = ReadPackCell(dataPack);

        dataPack.ReadCell(); // admin userid
        dataPack.ReadCell(); // target userid
        dataPack.ReadCell(); // time

        DataPack reasonPack = view_as<DataPack>(dataPack.ReadCell());

        if (reasonPack != INVALID_HANDLE)
                CloseHandle(reasonPack);
        CloseHandle(dataPack);

        // we add a temporary ban and then add the record into the queue to be processed when the database is available
        char buffer[50];

        Format(buffer, sizeof(buffer), "banid %d %s", ProcessQueueTime, auth);

        ServerCommand(buffer);

        if (IsClientInGame(client))
                KickClient(client, "%t", "Banned Check Site", WebsiteAddress);

        char banName[128], banReason[256], query[512];

        SQL_EscapeString(SQLiteDB, name, banName, sizeof(banName));
        SQL_EscapeString(SQLiteDB, reason, banReason, sizeof(banReason));

        FormatEx(query, sizeof(query), "INSERT INTO queue VALUES ('%s', %i, %i, '%s', '%s', '%s', '%s', '%s')",
                auth, time, GetTime(), banReason, banName, ip, adminAuth, adminIp);

        SQL_TQuery(SQLiteDB, ErrorCheckCallback, query);
}

stock CheckLoadAdmins()
{
        for (new i = 1; i <= MaxClients; i++)
        {
                if (IsClientInGame(i) && IsClientAuthorized(i))
                {
                        RunAdminCacheChecks(i);
                        NotifyPostAdminCheck(i);
                }
        }
}

stock InsertServerInfo()
{
    if (DB == INVALID_HANDLE) {
        return;
    }
   
    char query[100];
    int pieces[4];
    int longip = GetConVarInt(CvarHostIp);

    pieces[0] = (longip >> 24) & 0x000000FF;
    pieces[1] = (longip >> 16) & 0x000000FF;
    pieces[2] = (longip >> 8) & 0x000000FF;
    pieces[3] = longip & 0x000000FF;

    FormatEx(ServerIp, sizeof(ServerIp), "%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]);
    GetConVarString(CvarPort, ServerPort, sizeof(ServerPort));

    if (AutoAdd != false) {
        FormatEx(query, sizeof(query), "SELECT sid FROM %s_servers WHERE ip = '%s' AND port = '%s'", DatabasePrefix, ServerIp, ServerPort);
        SQL_TQuery(DB, ServerInfoCallback, query);
    }
}

stock void PrepareBan(int client, int target, int time, char[] reason, int size)
{
        #if defined DEBUG
        LogToFile(logFile, "PrepareBan()");
        #endif
        if (!target || !IsClientInGame(target))
                return;
        char authid[64], name[32], bannedSite[512];
        if (!GetClientAuthId(target, AuthId_Steam2, authid, sizeof(authid)))
                return;
        GetClientName(target, name, sizeof(name));


        if (CreateBan(client, target, time, reason))
        {
                if (!time)
                {
                        if (reason[0] == '\0')
                        {
                                ShowActivity(client, "%t", "Permabanned player", name);
                        } else {
                                ShowActivity(client, "%t", "Permabanned player reason", name, reason);
                        }
                } else {
                        if (reason[0] == '\0')
                        {
                                ShowActivity(client, "%t", "Banned player", name, time);
                        } else {
                                ShowActivity(client, "%t", "Banned player reason", name, time, reason);
                        }
                }
                LogAction(client, target, "\"%L\" banned \"%L\" (minutes \"%d\") (reason \"%s\")", client, target, time, reason);

                if (time > 5 || time == 0)
                        time = 5;
                Format(bannedSite, sizeof(bannedSite), "%T", "Banned Check Site", target, WebsiteAddress);
                BanClient(target, time, BANFLAG_AUTO, bannedSite, bannedSite, "sm_ban", client);
        }

        g_BanTarget[client] = -1;
        g_BanTime[client] = -1;
}

stock void ReadConfig()
{
        InitializeConfigParser();

        if (ConfigParser == INVALID_HANDLE)
        {
                return;
        }

        char ConfigFile[PLATFORM_MAX_PATH];
        BuildPath(Path_SM, ConfigFile, sizeof(ConfigFile), "configs/sourcebans/sourcebans.cfg");

        if (FileExists(ConfigFile))
        {
                InternalReadConfig(ConfigFile);
                PrintToServer("%sLoading configs/sourcebans.cfg config file", Prefix);
        } else {
                char Error[PLATFORM_MAX_PATH + 64];
                FormatEx(Error, sizeof(Error), "%sFATAL *** ERROR *** can not find %s", Prefix, ConfigFile);
                LogToFile(logFile, "FATAL *** ERROR *** can not find %s", ConfigFile);
                SetFailState(Error);
        }
}

stock void ResetSettings()
{
        CommandDisable = 0;

        ResetMenu();
        ReadConfig();
}

stock void ParseBackupConfig_Overrides()
{
        Handle hKV = CreateKeyValues("SB_Overrides");

        if (!FileToKeyValues(hKV, overridesLoc))
                return;

        if (!KvGotoFirstSubKey(hKV))
                return;

        char sSection[16], sFlags[32], sName[64];
        OverrideType type;

        do
        {
                KvGetSectionName(hKV, sSection, sizeof(sSection));
                if (StrEqual(sSection, "override_commands"))
                        type = Override_Command;
                else if (StrEqual(sSection, "override_groups"))
                        type = Override_CommandGroup;
                else
                        continue;

                if (KvGotoFirstSubKey(hKV, false))
                {
                        do
                        {
                                KvGetSectionName(hKV, sName, sizeof(sName));
                                KvGetString(hKV, NULL_STRING, sFlags, sizeof(sFlags));
                                AddCommandOverride(sName, type, ReadFlagString(sFlags));
                                #if defined _DEBUG
                                PrintToServer("Adding override (%s, %s, %s)", sSection, sName, sFlags);
                                #endif
                        } while (KvGotoNextKey(hKV, false));
                        KvGoBack(hKV);
                }
        }
        while (KvGotoNextKey(hKV));
        CloseHandle(hKV);
}

stock AdminFlag CreateFlagLetters()
{
        AdminFlag FlagLetters[FLAG_LETTERS_SIZE];

        FlagLetters['a'-'a'] = Admin_Reservation;
        FlagLetters['b'-'a'] = Admin_Generic;
        FlagLetters['c'-'a'] = Admin_Kick;
        FlagLetters['d'-'a'] = Admin_Ban;
        FlagLetters['e'-'a'] = Admin_Unban;
        FlagLetters['f'-'a'] = Admin_Slay;
        FlagLetters['g'-'a'] = Admin_Changemap;
        FlagLetters['h'-'a'] = Admin_Convars;
        FlagLetters['i'-'a'] = Admin_Config;
        FlagLetters['j'-'a'] = Admin_Chat;
        FlagLetters['k'-'a'] = Admin_Vote;
        FlagLetters['l'-'a'] = Admin_Password;
        FlagLetters['m'-'a'] = Admin_RCON;
        FlagLetters['n'-'a'] = Admin_Cheats;
        FlagLetters['o'-'a'] = Admin_Custom1;
        FlagLetters['p'-'a'] = Admin_Custom2;
        FlagLetters['q'-'a'] = Admin_Custom3;
        FlagLetters['r'-'a'] = Admin_Custom4;
        FlagLetters['s'-'a'] = Admin_Custom5;
        FlagLetters['t'-'a'] = Admin_Custom6;
        FlagLetters['z'-'a'] = Admin_Root;

        return FlagLetters;
}

stock AccountForLateLoading()
{
        char auth[30];

        for (new i = 1; i <= GetMaxClients(); i++)
        {
                if (IsClientConnected(i) && !IsFakeClient(i))
                {
                        PlayerStatus[i] = false;
                }
                if (IsClientInGame(i) && !IsFakeClient(i) && IsClientAuthorized(i) && GetClientAuthId(i, AuthId_Steam2, auth, sizeof(auth)))
                {
                        OnClientAuthorized(i, auth);
                }
        }
}
//Yarr!


I really don't know why you were not able to compile it. But i compiled it for you with 0 errors.
Please use official version from here https://github.com/sbpp/sourcebans-pp/releases


All times are GMT -4. The time now is 01:03.

Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.