#pragma semicolon 1
#pragma tabsize 0
#include <sourcemod>
#include <sdktools>
#include <adminmenu>
#define MAX_STEAMID_LENGTH 32
#define MAX_IP_LENGTH 64
#define Prefix "[Offline Ban]"
#define DEBUG 0
new bool:map_clear = false;
new Handle:g_Name = INVALID_HANDLE,
Handle:g_SteamID = INVALID_HANDLE,
Handle:g_IP = INVALID_HANDLE,
Handle:g_time = INVALID_HANDLE,
Handle:g_adminMenu = INVALID_HANDLE,
Handle:h_Database,
Handle:h_CvarHostIp,
Handle:h_FormatTime,
Handle:h_map_clear,
Handle:h_max_stored_players,
Handle:h_CvarPort;
new String:s_ServerIP[32],
String:s_ServerPort[8],
String:logFile[256],
String:DatabasePrefix[10] = "sb",
String:s_Time[32] = "",
String:s_Target[32] = "",
String:s_Reason[125] = "";
new serverID = -1,
max_stored_players = 21;
new State:ConfigState;
new Handle:ConfigParser;
enum State /* ConfigState */
{
ConfigStateNone = 0,
ConfigStateConfig,
ConfigStateReasons,
ConfigStateHacking
}
new Handle:ReasonMenuHandle,
Handle:HackingMenuHandle;
public Plugin:myinfo =
{
name = "Offline Ban list",
author = "Grey™",
description = "",
version = "1.1",
url = "hlmod.ru"
};
public OnPluginStart()
{
LoadTranslations("offlineban.phrases");
h_FormatTime = CreateConVar("sm_offban_timeformat", "%d.%m|%H:%M", "Format time");
h_max_stored_players = CreateConVar("sm_offban_max_stored", "21", "Max stored players");
h_map_clear = CreateConVar("sm_offban_map_clear", "0", "Map clear");
RegAdminCmd("sm_offban_clear", CommandClearBan, ADMFLAG_ROOT, "Clear history");
g_Name = CreateArray(MAX_NAME_LENGTH);
g_SteamID = CreateArray(MAX_STEAMID_LENGTH);
g_IP = CreateArray(MAX_IP_LENGTH);
g_time = CreateArray(50);
decl String:filename[200];
BuildPath(Path_SM, logFile, sizeof(logFile), "logs/offlineban.log");
BuildPath(Path_SM, filename, sizeof(filename), "plugins/sourcebans.smx");
if (FileExists(filename, false))
{
SQL_TConnect(GotDatabase, "sourcebans");
InsertServerInfo();
if((ReasonMenuHandle = CreateMenu(ReasonSelected)) != INVALID_HANDLE)
{
SetMenuPagination(ReasonMenuHandle, 8);
SetMenuExitBackButton(ReasonMenuHandle, true);
}
if((HackingMenuHandle = CreateMenu(HackingSelected)) != INVALID_HANDLE)
{
SetMenuPagination(HackingMenuHandle, 8);
SetMenuExitBackButton(HackingMenuHandle, true);
}
}
if(!SQL_CheckConfig("sourcebans"))
{
LogToFile(logFile, "Database failure: Could not find Database conf \"sourcebans\". See FAQ: http://sourcebans.net/node/19");
SetFailState("Database failure: Could not find Database conf \"sourcebans\"");
return;
}
map_clear = GetConVarBool(h_map_clear);
max_stored_players = GetConVarInt(h_max_stored_players);
}
public OnConfigsExecuted()
{
map_clear = GetConVarBool(h_map_clear);
max_stored_players = GetConVarInt(h_max_stored_players);
}
public Action:CommandClearBan(client, args)
{
ClearArray(g_Name);
ClearArray(g_SteamID);
ClearArray(g_IP);
ClearArray(g_time);
PrintToChat(client, "%s %t", Prefix, "Clear history");
}
public OnAllPluginsLoaded()
{
new Handle:topmenu;
if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
OnAdminMenuReady(topmenu);
}
public OnMapStart()
{
ResetSettings();
if(map_clear)
{
ClearArray(g_Name);
ClearArray(g_SteamID);
ClearArray(g_IP);
ClearArray(g_time);
}
}
// удаление игроков вошедших в игру
public OnClientPostAdminCheck(client)
{
if (!IsClientInGame(client) || IsFakeClient(client))
return;
decl String:steamID[MAX_STEAMID_LENGTH];
GetClientAuthString(client, steamID, sizeof(steamID));
new index = FindStringInArray(g_SteamID, steamID);
if (index != -1)
{
RemoveFromArray(g_Name, index);
RemoveFromArray(g_SteamID, index);
RemoveFromArray(g_IP, index);
RemoveFromArray(g_time, index);
}
}
//зачисление в список игроков вышедших из игры
public OnClientDisconnect(client)
{
if (!IsClientInGame(client) || IsFakeClient(client))
return;
new AdminId:adminId = GetUserAdmin(client);
if (adminId != INVALID_ADMIN_ID)
{
if (GetAdminImmunityLevel(adminId) > 0)
return;
}
while (GetArraySize(g_SteamID) >= max_stored_players)
{
RemoveFromArray(g_Name, 0);
RemoveFromArray(g_SteamID, 0);
RemoveFromArray(g_IP, 0);
RemoveFromArray(g_time, 0);
}
decl String:buffer[MAX_NAME_LENGTH];
GetClientName(client, buffer, sizeof(buffer));
PushArrayString(g_Name, buffer);
GetClientAuthString(client, buffer, sizeof(buffer));
PushArrayString(g_SteamID, buffer);
GetClientIP(client, buffer, sizeof(buffer));
PushArrayString(g_IP, buffer);
new time = GetTime();
new String:nowtime[50],
String:s_times[50];
GetConVarString(h_FormatTime, s_times, sizeof(s_times));
FormatTime(nowtime, sizeof(nowtime), s_times, time);
PushArrayString(g_time, nowtime);
}
//меню
public OnLibraryRemoved(const String:name[])
{
if (StrEqual(name, "adminmenu"))
g_adminMenu = INVALID_HANDLE;
}
public OnAdminMenuReady(Handle:topmenu)
{
if (topmenu != g_adminMenu)
{
g_adminMenu = topmenu;
new TopMenuObject:player_commands = FindTopMenuCategory(g_adminMenu, ADMINMENU_PLAYERCOMMANDS);
if (player_commands != INVALID_TOPMENUOBJECT)
AddToTopMenu(g_adminMenu, "OfflineBans", TopMenuObject_Item, AdminMenu_Ban,
player_commands, "OfflineBans", ADMFLAG_BAN);
}
}
public AdminMenu_Ban(Handle:topmenu, TopMenuAction:action, TopMenuObject:object_id, param, String:buffer[], maxlength)
{
if (action == TopMenuAction_DisplayOption)
Format(buffer, maxlength, "%T", "OfflineBansTitle", param);
else if (action == TopMenuAction_SelectOption)
ShowBanList(param);
}
//меню выбора игрока
ShowBanList(client)
{
new iSize = GetArraySize(g_SteamID);
if (iSize == 0)
PrintToChat(client, "%s %t", Prefix, "No players history");
else
{
new Handle:menu = CreateMenu(MenuHandler_BanList);
decl String:title[64],
String:name[MAX_NAME_LENGTH],
String:time[50],
String:steamID[MAX_STEAMID_LENGTH];
Format(title, sizeof(title), "%T", "SelectPlayerTitle", client);
SetMenuTitle(menu, title);
for (new i = iSize-1; i > 0; i--)
{
GetArrayString(g_Name, i, name, sizeof(name));
GetArrayString(g_SteamID, i, steamID, sizeof(steamID));
GetArrayString(g_time, i, time, sizeof(time));
Format(title, sizeof(title), "%s (%s)", name, time, client);
AddMenuItem(menu, steamID, title);
}
SetMenuExitBackButton(menu, true);
DisplayMenu(menu, client, MENU_TIME_FOREVER);
}
}
public MenuHandler_BanList(Handle:menu, MenuAction:action, param1, param2)
{
if (action == MenuAction_Select)
{
decl String:steamID[MAX_STEAMID_LENGTH];
GetMenuItem(menu, param2, steamID, sizeof(steamID));
strcopy(s_Target, sizeof(s_Target), steamID);
ShowBanTimeMenu(param1);
}
else if (action == MenuAction_Cancel)
{
if (param2 == MenuCancel_ExitBack)
DisplayTopMenu(g_adminMenu, param1, TopMenuPosition_LastCategory);
else if (param2 == MenuCancel_Exit)
CloseHandle(menu);
}
}
//меню выбора времени бана
ShowBanTimeMenu(client)
{
new Handle:menu = CreateMenu(MenuHandler_BanTimeMenu);
decl String:title[100];
Format(title, sizeof(title), "%T:", "SelectTimeTitle", client);
SetMenuTitle(menu, title);
if(CheckCommandAccess(client, "sm_unban", ADMFLAG_UNBAN|ADMFLAG_ROOT))
{
Format(title, sizeof(title), "%T", "Permanent", client);
AddMenuItem(menu, "0", title);
}
Format(title, sizeof(title), "%T", "5 minutes", client);
AddMenuItem(menu, "5", title);
Format(title, sizeof(title), "%T", "30 minutes", client);
AddMenuItem(menu, "30", title);
Format(title, sizeof(title), "%T", "1 hour", client);
AddMenuItem(menu, "60", title);
Format(title, sizeof(title), "%T", "1 day", client);
AddMenuItem(menu, "1440", title);
Format(title, sizeof(title), "%T", "1 week", client);
AddMenuItem(menu, "10080", title);
Format(title, sizeof(title), "%T", "1 month", client);
AddMenuItem(menu, "43200", title);
Format(title, sizeof(title), "%T", "3 month", client);
AddMenuItem(menu, "129600", title);
SetMenuExitBackButton(menu, true);
DisplayMenu(menu, client, MENU_TIME_FOREVER);
}
public MenuHandler_BanTimeMenu(Handle:menu, MenuAction:action, param1, param2)
{
if (action == MenuAction_Select)
{
decl String:info[32];
GetMenuItem(menu, param2, info, sizeof(info));
strcopy(s_Time, sizeof(s_Time), info);
DisplayMenu(ReasonMenuHandle, param1, MENU_TIME_FOREVER);
}
else if (action == MenuAction_Cancel)
{
if (param2 == MenuCancel_ExitBack)
ShowBanList(param1);
else if (param2 == MenuCancel_Exit)
CloseHandle(menu);
}
}
//меню выбора причины бана
public ReasonSelected(Handle:menu, MenuAction:action, param1, param2)
{
if (action == MenuAction_Select)
{
decl String:info[128],
String:key[128];
GetMenuItem(menu, param2, key, sizeof(key), _, info, sizeof(info));
strcopy(s_Reason, sizeof(s_Reason), key);
if(StrEqual("Hacking", key))
{
DisplayMenu(HackingMenuHandle, param1, MENU_TIME_FOREVER);
return;
}
CheckAdmins(param1);
}
else if (action == MenuAction_Cancel)
{
if (param2 == MenuCancel_ExitBack)
ShowBanTimeMenu(param1);
}
}
public HackingSelected(Handle:menu, MenuAction:action, param1, param2)
{
if (action == MenuAction_Select)
{
decl String:info[128],
String:key[128];
GetMenuItem(menu, param2, key, sizeof(key), _, info, sizeof(info));
strcopy(s_Reason, sizeof(s_Reason), key);
CheckAdmins(param1);
}
else if (action == MenuAction_Cancel)
{
if (param2 == MenuCancel_ExitBack)
DisplayMenu(ReasonMenuHandle, param1, MENU_TIME_FOREVER);
}
}
//проверка есть-ли админ в бд
stock CheckAdmins(client)
{
decl String:admin_steamID[MAX_STEAMID_LENGTH],
String:adminIp[MAX_IP_LENGTH],
String:s_Query[1024];
GetClientAuthString(client, admin_steamID, sizeof(admin_steamID));
Format(s_Query, sizeof(s_Query), "SELECT * FROM %s_admins WHERE `authid` = '%s'",
DatabasePrefix, admin_steamID);
new Handle:hQuery = SQL_Query(h_Database, s_Query);
if (SQL_FetchRow(hQuery))
GetClientIP(client, adminIp, sizeof(adminIp));
else
{
strcopy(admin_steamID, sizeof(admin_steamID), "STEAM_ID_SERVER");
strcopy(adminIp, sizeof(adminIp), s_ServerIP);
}
#if DEBUG
LogToFile(logFile,": %s", s_Query);
#endif
CreateBanSB(client, admin_steamID, adminIp);
}
//занесение бана в бд
stock CreateBanSB(client, String:admin_steamID[], String:adminIp[])
{
decl String:name[MAX_NAME_LENGTH],
String:bname[MAX_NAME_LENGTH],
String:ip[MAX_IP_LENGTH],
String:of_reason[256],
String:s_Query[1024];
new time = StringToInt(s_Time);
new index = FindStringInArray(g_SteamID, s_Target);
GetArrayString(g_Name, index, name, sizeof(name));
GetArrayString(g_IP, index, ip, sizeof(ip));
Format(of_reason, sizeof(of_reason), "%s %s", Prefix, s_Reason);
SQL_EscapeString(h_Database, name, bname, sizeof(bname));
if(serverID == -1)
{
FormatEx(s_Query, sizeof(s_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', \
(SELECT `aid` FROM %s_admins WHERE `authid` = '%s' LIMIT 0,1), '%s', \
(SELECT `sid` FROM %s_servers WHERE `ip` = '%s' AND `port` = '%s' LIMIT 0,1), ' ')",
DatabasePrefix, ip, s_Target, bname, (time*60), (time*60), of_reason, DatabasePrefix, admin_steamID, adminIp, DatabasePrefix, s_ServerIP, s_ServerPort);
CheckUTF8(h_Database);
SQL_TQuery(h_Database, VerifyInsert, s_Query, client, DBPrio_High);
#if DEBUG
LogToFile(logFile,": %s", s_Query);
#endif
}
else
{
FormatEx(s_Query, sizeof(s_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', \
(SELECT `aid` FROM %s_admins WHERE `authid` = '%s' LIMIT 0,1), '%s', \
%d, ' ')", DatabasePrefix, ip, s_Target, bname, (time*60), (time*60), of_reason, DatabasePrefix, admin_steamID, adminIp, serverID);
CheckUTF8(h_Database);
SQL_TQuery(h_Database, VerifyInsert, s_Query, client, DBPrio_High);
#if DEBUG
LogToFile(logFile,": %s", s_Query);
#endif
}
LogAction(client,-1, "\"%L\" banned \"%s (%s IP%s)\" (minutes \"%d\") (reason \"%s\")", client, name, s_Target, ip, time, s_Reason);
return true;
}
public CheckUTF8(Handle:hndl)
{
h_Database = hndl;
decl String:s_Query[1024];
FormatEx(s_Query, sizeof(s_Query), "SET NAMES 'utf8'");
SQL_TQuery(h_Database, ErrorCheckCallback, s_Query, any:0, DBPrio_High);
}
public GotDatabase(Handle:owner, Handle:hndl, const String:error[], any:data)
{
if (hndl == INVALID_HANDLE)
{
LogToFile(logFile, "Database failure: %s", error);
return;
}
CheckUTF8(hndl);
}
public ErrorCheckCallback(Handle:owner, Handle:hndle, const String:error[], any:data)
{
if(error[0])
LogToFile(logFile, "Query Failed: %s", error);
}
public VerifyInsert(Handle:owner, Handle:hndl, const String:error[], any:client)
{
decl String:name[MAX_NAME_LENGTH];
new index = FindStringInArray(g_SteamID, s_Target);
GetArrayString(g_Name, index, name, sizeof(name));
if (hndl == INVALID_HANDLE || error[0])
{
LogToFile(logFile, "Verify Insert Query Failed: %s", error);
PrintToChat(client, "\x01%s \x07ff0033%t", Prefix, "Failed to ban", name, s_Target);
return;
}
else
{
PrintToChat(client, "\x01%s \x0734c924%t", Prefix, "Added to ban", name, s_Target);
RemoveFromArray(g_Name, index);
RemoveFromArray(g_SteamID, index);
RemoveFromArray(g_IP, index);
}
s_Target = "";
s_Time = "";
s_Reason = "";
}
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];
if (SMC_GetErrorString(err, buffer, sizeof(buffer)))
{
PrintToServer(buffer);
LogToFile(logFile, buffer);
}
else
{
PrintToServer("Fatal parse error");
LogToFile(logFile, "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;
}
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("DatabasePrefix", key, false) == 0)
{
strcopy(DatabasePrefix, sizeof(DatabasePrefix), value);
if(DatabasePrefix[0] == '\0')
DatabasePrefix = "sb";
}
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);
}
}
return SMCParse_Continue;
}
public SMCResult:ReadConfig_EndSection(Handle:smc)
return SMCParse_Continue;
stock ReadConfig()
{
InitializeConfigParser();
if (ConfigParser == INVALID_HANDLE)
return;
decl String:ConfigFile[PLATFORM_MAX_PATH];
BuildPath(Path_SM, ConfigFile, sizeof(ConfigFile), "configs/sourcebans/sourcebans.cfg");
if(FileExists(ConfigFile))
InternalReadConfig(ConfigFile);
else
{
decl String: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 ResetSettings()
{
ResetMenu();
ReadConfig();
}
stock ResetMenu()
{
if(ReasonMenuHandle != INVALID_HANDLE)
RemoveAllMenuItems(ReasonMenuHandle);
}
stock InsertServerInfo()
{
new i_Pieces[4],
i_LongIP;
h_CvarHostIp = FindConVar("hostip");
h_CvarPort = FindConVar("hostport");
i_LongIP = GetConVarInt(h_CvarHostIp);
i_Pieces[0] = (i_LongIP >> 24) & 0x000000FF;
i_Pieces[1] = (i_LongIP >> 16) & 0x000000FF;
i_Pieces[2] = (i_LongIP >> 8) & 0x000000FF;
i_Pieces[3] = i_LongIP & 0x000000FF;
FormatEx(s_ServerIP, sizeof(s_ServerIP), "%d.%d.%d.%d", i_Pieces[0], i_Pieces[1], i_Pieces[2], i_Pieces[3]);
GetConVarString(h_CvarPort, s_ServerPort, sizeof(s_ServerPort));
}