diff --git a/core/AdminCache.cpp b/core/AdminCache.cpp index 37b68713..eacb1eec 100644 --- a/core/AdminCache.cpp +++ b/core/AdminCache.cpp @@ -37,8 +37,181 @@ #include "ForwardSys.h" #include "PlayerManager.h" #include "ConCmdManager.h" +#include "TextParsers.h" +#include "Logger.h" +#include "sourcemod.h" +#include "sm_stringutil.h" + +#define LEVEL_STATE_NONE 0 +#define LEVEL_STATE_LEVELS 1 +#define LEVEL_STATE_FLAGS 2 AdminCache g_Admins; +AdminFlag g_FlagLetters[26]; +bool g_FlagSet[26]; + +/* Default flags */ +AdminFlag g_DefaultFlags[26] = +{ + Admin_Reservation, Admin_Generic, Admin_Kick, Admin_Ban, Admin_Unban, + Admin_Slay, Admin_Changemap, Admin_Convars, Admin_Config, Admin_Chat, + Admin_Vote, Admin_Password, Admin_RCON, Admin_Cheats, Admin_Custom1, + Admin_Custom2, Admin_Custom3, Admin_Custom4, Admin_Custom5, Admin_Custom6, + Admin_Generic, Admin_Generic, Admin_Generic, Admin_Generic, Admin_Generic, + Admin_Root +}; + + +class FlagReader : public ITextListener_SMC +{ +public: + void LoadLevels() + { + if (!Parse()) + { + memcpy(g_FlagLetters, g_DefaultFlags, sizeof(AdminFlag) * 26); + for (unsigned int i=0; i<20; i++) + { + g_FlagSet[i] = true; + } + g_FlagSet[25] = true; + } + } +private: + bool Parse() + { + unsigned int line = 0; + SMCParseError error; + + m_Line = 0; + m_bFileNameLogged = false; + g_SourceMod.BuildPath(Path_SM, m_File, sizeof(m_File), "configs/admin_levels.cfg"); + + if ((error = g_TextParser.ParseFile_SMC(m_File, this, &line, NULL)) + != SMCParse_Okay) + { + const char *err_string = g_TextParser.GetSMCErrorString(error); + if (!err_string) + { + err_string = "Unknown error"; + } + ParseError("Error %d (%s)", error, err_string); + return false; + } + + return true; + } + void ReadSMC_ParseStart() + { + m_LevelState = LEVEL_STATE_NONE; + m_IgnoreLevel = 0; + memset(g_FlagSet, 0, sizeof(g_FlagSet)); + } + SMCParseResult ReadSMC_NewSection(const char *name, bool opt_quotes) + { + if (m_IgnoreLevel) + { + m_IgnoreLevel++; + return SMCParse_Continue; + } + + if (m_LevelState == LEVEL_STATE_NONE) + { + if (strcmp(name, "Levels") == 0) + { + m_IgnoreLevel = LEVEL_STATE_LEVELS; + } else { + m_IgnoreLevel++; + } + } else if (m_IgnoreLevel == LEVEL_STATE_LEVELS) { + if (strcmp(name, "Flags") == 0) + { + m_IgnoreLevel = LEVEL_STATE_FLAGS; + } else { + m_IgnoreLevel++; + } + } else { + m_IgnoreLevel++; + } + + return SMCParse_Continue; + } + SMCParseResult ReadSMC_KeyValue(const char *key, const char *value, bool key_quotes, bool value_quotes) + { + if (m_IgnoreLevel != LEVEL_STATE_FLAGS || m_IgnoreLevel) + { + return SMCParse_Continue; + } + + unsigned char c = (unsigned)value[0]; + + if (c < (unsigned)'a' || c > (unsigned)'z') + { + ParseError("Flag \"%c\" is not a lower-case ASCII letter", c); + return SMCParse_Continue; + } + + c -= (unsigned)'a'; + + assert(c >= 0 && c < 26); + + if (!g_Admins.FindFlag(key, &g_FlagLetters[c])) + { + ParseError("Unrecognized admin level \"%s\"", key); + return SMCParse_Continue; + } + + g_FlagSet[c] = true; + + return SMCParse_Continue; + } + SMCParseResult ReadSMC_LeavingSection() + { + if (m_IgnoreLevel) + { + m_IgnoreLevel--; + return SMCParse_Continue; + } + + if (m_LevelState == LEVEL_STATE_FLAGS) + { + m_LevelState = LEVEL_STATE_LEVELS; + return SMCParse_Halt; + } else if (m_LevelState == LEVEL_STATE_LEVELS) { + m_LevelState = LEVEL_STATE_NONE; + } + + return SMCParse_Continue; + } + SMCParseResult ReadSMC_RawLine(const char *line, unsigned int curline) + { + m_Line = curline; + return SMCParse_Continue; + } + void ParseError(const char *message, ...) + { + va_list ap; + char buffer[256]; + + va_start(ap, message); + UTIL_FormatArgs(buffer, sizeof(buffer), message, ap); + va_end(ap); + + if (!m_bFileNameLogged) + { + g_Logger.LogError("[SM] Parse error(s) detected in file \"%s\":", m_File); + m_bFileNameLogged = true; + } + + g_Logger.LogError("[SM] (Line %d): %s", m_Line, buffer); + } +private: + bool m_bFileNameLogged; + char m_File[PLATFORM_MAX_PATH]; + int m_LevelState; + int m_IgnoreLevel; + unsigned int m_Line; +} s_FlagReader; AdminCache::AdminCache() { @@ -54,6 +227,7 @@ AdminCache::AdminCache() m_pAuthTables = sm_trie_create(); m_InvalidatingAdmins = false; m_destroying = false; + m_pLevelNames = sm_trie_create(); } AdminCache::~AdminCache() @@ -81,6 +255,8 @@ AdminCache::~AdminCache() sm_trie_destroy(m_pAuthTables); delete m_pStrings; + + sm_trie_destroy(m_pLevelNames); } void AdminCache::OnSourceModStartup(bool late) @@ -88,6 +264,28 @@ void AdminCache::OnSourceModStartup(bool late) RegisterAuthIdentType("steam"); RegisterAuthIdentType("name"); RegisterAuthIdentType("ip"); + + NameFlag("reservation", Admin_Reservation); + NameFlag("kick", Admin_Kick); + NameFlag("generic", Admin_Generic); + NameFlag("ban", Admin_Ban); + NameFlag("unban", Admin_Unban); + NameFlag("slay", Admin_Slay); + NameFlag("changemap", Admin_Changemap); + NameFlag("cvars", Admin_Convars); + NameFlag("config", Admin_Config); + NameFlag("chat", Admin_Chat); + NameFlag("vote", Admin_Vote); + NameFlag("password", Admin_Password); + NameFlag("rcon", Admin_RCON); + NameFlag("cheats", Admin_Cheats); + NameFlag("root", Admin_Root); + NameFlag("custom1", Admin_Custom1); + NameFlag("custom2", Admin_Custom2); + NameFlag("custom3", Admin_Custom3); + NameFlag("custom4", Admin_Custom4); + NameFlag("custom5", Admin_Custom5); + NameFlag("custom6", Admin_Custom6); } void AdminCache::OnSourceModAllInitialized() @@ -96,6 +294,12 @@ void AdminCache::OnSourceModAllInitialized() g_ShareSys.AddInterface(NULL, this); } +void AdminCache::OnSourceModLevelChange(const char *mapName) +{ + /* For now, we only read these once per level. */ + s_FlagReader.LoadLevels(); +} + void AdminCache::OnSourceModShutdown() { g_Forwards.ReleaseForward(m_pCacheFwd); @@ -108,6 +312,27 @@ void AdminCache::OnSourceModPluginsLoaded() DumpAdminCache(AdminCache_Groups, true); } +void AdminCache::NameFlag(const char *str, AdminFlag flag) +{ + sm_trie_insert(m_pLevelNames, str, (void *)flag); +} + +bool AdminCache::FindFlag(const char *str, AdminFlag *pFlag) +{ + void *obj; + if (!sm_trie_retrieve(m_pLevelNames, str, &obj)) + { + return false; + } + + if (pFlag) + { + *pFlag = (AdminFlag)(int)obj; + } + + return true; +} + void AdminCache::AddCommandOverride(const char *cmd, OverrideType type, FlagBits flags) { Trie *pTrie = NULL; @@ -1289,3 +1514,41 @@ bool AdminCache::CanAdminTarget(AdminId id, AdminId target) return true; } + +bool AdminCache::FindFlag(char c, AdminFlag *pAdmFlag) +{ + if (c < 'a' || c > 'z') + { + return false; + } + + if (*pAdmFlag) + { + *pAdmFlag = g_FlagLetters[(unsigned)c - (unsigned)'a']; + } + + return true; +} + +FlagBits AdminCache::ReadFlagString(const char *flags, const char **end) +{ + FlagBits bits = 0; + + while (flags && (*flags != '\0')) + { + AdminFlag flag; + if (!FindFlag(*flags, &flag)) + { + break; + } + bits |= FlagArrayToBits(&flag, 1); + flags++; + } + + if (end) + { + *end = flags; + } + + return bits; +} diff --git a/core/AdminCache.h b/core/AdminCache.h index 3aaa3bd0..99fe6ed3 100644 --- a/core/AdminCache.h +++ b/core/AdminCache.h @@ -105,6 +105,7 @@ public: public: //SMGlobalClass void OnSourceModStartup(bool late); void OnSourceModAllInitialized(); + void OnSourceModLevelChange(const char *mapName); void OnSourceModShutdown(); void OnSourceModPluginsLoaded(); public: //IAdminSystem @@ -151,6 +152,9 @@ public: //IAdminSystem bool CheckAdminFlags(AdminId id, FlagBits bits); bool CanAdminTarget(AdminId id, AdminId target); void SetAdminFlags(AdminId id, AccessMode mode, FlagBits bits); + bool FindFlag(const char *str, AdminFlag *pFlag); + bool FindFlag(char c, AdminFlag *pAdmFlag); + FlagBits ReadFlagString(const char *flags, const char **end); public: bool IsValidAdmin(AdminId id); private: @@ -161,6 +165,7 @@ private: void DumpCommandOverrideCache(OverrideType type); Trie *GetMethodByIndex(unsigned int index); bool GetMethodIndex(const char *name, unsigned int *_index); + void NameFlag(const char *str, AdminFlag flag); public: BaseStringTable *m_pStrings; BaseMemTable *m_pMemory; @@ -179,6 +184,7 @@ public: int m_FreeUserList; bool m_InvalidatingAdmins; bool m_destroying; + Trie *m_pLevelNames; }; extern AdminCache g_Admins; diff --git a/core/smn_admin.cpp b/core/smn_admin.cpp index 7bc43dca..056c540c 100644 --- a/core/smn_admin.cpp +++ b/core/smn_admin.cpp @@ -451,6 +451,57 @@ static cell_t CreateAuthMethod(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t FindFlagByName(IPluginContext *pContext, const cell_t *params) +{ + char *flag; + pContext->LocalToString(params[1], &flag); + + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + + AdminFlag admflag; + if (!g_Admins.FindFlag(flag, &admflag)) + { + return 0; + } + + *addr = (cell_t)admflag; + + return 1; +} + +static cell_t FindFlagByChar(IPluginContext *pContext, const cell_t *params) +{ + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + + AdminFlag admflag; + if (!g_Admins.FindFlag((char)params[1], &admflag)) + { + return 0; + } + + *addr = (cell_t)admflag; + + return 1; +} + +static cell_t ReadFlagString(IPluginContext *pContext, const cell_t *params) +{ + char *flag; + pContext->LocalToString(params[1], &flag); + + cell_t *addr; + pContext->LocalToPhysAddr(params[2], &addr); + + const char *end = flag; + FlagBits bits = g_Admins.ReadFlagString(flag, &end); + + *addr = end - flag; + + return bits; +} + REGISTER_NATIVES(adminNatives) { {"DumpAdminCache", DumpAdminCache}, @@ -489,6 +540,9 @@ REGISTER_NATIVES(adminNatives) {"FlagBitsToArray", FlagBitsToArray}, {"CanAdminTarget", CanAdminTarget}, {"CreateAuthMethod", CreateAuthMethod}, + {"FindFlagByName", FindFlagByName}, + {"FindFlagByChar", FindFlagByChar}, + {"ReadFlagString", ReadFlagString}, /* -------------------------------------------------- */ {NULL, NULL}, }; diff --git a/plugins/admin-flatfile/admin-flatfile.sp b/plugins/admin-flatfile/admin-flatfile.sp index ce90e871..85df901a 100644 --- a/plugins/admin-flatfile/admin-flatfile.sp +++ b/plugins/admin-flatfile/admin-flatfile.sp @@ -35,14 +35,11 @@ public Plugin:myinfo = }; /** Various parsing globals */ -new bool:g_FlagsSet[26]; /* Maps whether flags are set */ -new AdminFlag:g_FlagLetters[26]; /* Maps the flag letters */ new bool:g_LoggedFileName = false; /* Whether or not the file name has been logged */ new g_ErrorCount = 0; /* Current error count */ new g_IgnoreLevel = 0; /* Nested ignored section count, so users can screw up files safely */ new String:g_Filename[PLATFORM_MAX_PATH]; /* Used for error messages */ -#include "admin-levels.sp" #include "admin-overrides.sp" #include "admin-groups.sp" #include "admin-users.sp" @@ -50,7 +47,6 @@ new String:g_Filename[PLATFORM_MAX_PATH]; /* Used for error messages */ public OnRebuildAdminCache(AdminCachePart:part) { - RefreshLevels(); if (part == AdminCache_Overrides) { ReadOverrides(); diff --git a/plugins/admin-flatfile/admin-groups.sp b/plugins/admin-flatfile/admin-groups.sp index 5fc7aac1..571a7cc9 100644 --- a/plugins/admin-flatfile/admin-groups.sp +++ b/plugins/admin-flatfile/admin-groups.sp @@ -92,15 +92,10 @@ public SMCResult:ReadGroups_KeyValue(Handle:smc, new len = strlen(value); for (new i=0; i 'z') + if (!FindFlagByChar(value[i], flag)) { continue; } - if (!g_FlagsSet[value[i] - 'a']) - { - continue; - } - flag = g_FlagLetters[value[i] - 'a']; SetAdmGroupAddFlag(g_CurGrp, flag, true); } } else if (StrEqual(key, "immunity")) { diff --git a/plugins/admin-flatfile/admin-levels.sp b/plugins/admin-flatfile/admin-levels.sp deleted file mode 100644 index 8c654957..00000000 --- a/plugins/admin-flatfile/admin-levels.sp +++ /dev/null @@ -1,217 +0,0 @@ -/* - * admin-levels.sp - * Reads access flags from the admin_levels.cfg file. Do not compile this directly. - * This file is part of SourceMod, Copyright (C) 2004-2007 AlliedModders LLC - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Version: $Id$ - */ - -#define LEVEL_STATE_NONE 0 -#define LEVEL_STATE_LEVELS 1 -#define LEVEL_STATE_FLAGS 2 - -static Handle:g_hLevelParser = INVALID_HANDLE; -static g_LevelState = LEVEL_STATE_NONE; - -/* :TODO: log line numbers? */ - -LoadDefaultLetters() -{ - for (new i='t'; i<'z'; i++) - { - g_FlagsSet[i-'a'] = false; - } - - g_FlagLetters['a'-'a'] = Admin_Reservation; - g_FlagLetters['b'-'a'] = Admin_Generic; - g_FlagLetters['c'-'a'] = Admin_Kick; - g_FlagLetters['d'-'a'] = Admin_Ban; - g_FlagLetters['e'-'a'] = Admin_Unban; - g_FlagLetters['f'-'a'] = Admin_Slay; - g_FlagLetters['g'-'a'] = Admin_Changemap; - g_FlagLetters['h'-'a'] = Admin_Convars; - g_FlagLetters['i'-'a'] = Admin_Config; - g_FlagLetters['j'-'a'] = Admin_Chat; - g_FlagLetters['k'-'a'] = Admin_Vote; - g_FlagLetters['l'-'a'] = Admin_Password; - g_FlagLetters['m'-'a'] = Admin_RCON; - g_FlagLetters['n'-'a'] = Admin_Cheats; - g_FlagLetters['o'-'a'] = Admin_Custom1; - g_FlagLetters['p'-'a'] = Admin_Custom2; - g_FlagLetters['q'-'a'] = Admin_Custom3; - g_FlagLetters['r'-'a'] = Admin_Custom4; - g_FlagLetters['s'-'a'] = Admin_Custom5; - g_FlagLetters['t'-'a'] = Admin_Custom6; - g_FlagLetters['z'-'a'] = Admin_Root; -} - -public SMCResult:ReadLevels_NewSection(Handle:smc, const String:name[], bool:opt_quotes) -{ - if (g_IgnoreLevel) - { - g_IgnoreLevel++; - return SMCParse_Continue; - } - - if (g_LevelState == LEVEL_STATE_NONE) - { - if (StrEqual(name, "Levels")) - { - g_LevelState = LEVEL_STATE_LEVELS; - } else { - g_IgnoreLevel++; - } - } else if (g_LevelState == LEVEL_STATE_LEVELS) { - if (StrEqual(name, "Flags")) - { - g_LevelState = LEVEL_STATE_FLAGS; - } else { - g_IgnoreLevel++; - } - } else { - g_IgnoreLevel++; - } - - return SMCParse_Continue; -} - -public SMCResult:ReadLevels_KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) -{ - if (g_LevelState == LEVEL_STATE_FLAGS && !g_IgnoreLevel) - { - new chr = value[0]; - - if (chr < 'a' || chr > 'z') - { - ParseError("Unrecognized character: \"%s\"", value); - return SMCParse_Continue; - } - - chr -= 'a'; - - new AdminFlag:flag; - - if (StrEqual(key, "reservation")) - { - flag = Admin_Reservation; - } else if (StrEqual(key, "kick")) { - flag = Admin_Kick; - } else if (StrEqual(key, "generic")) { - flag = Admin_Generic; - } else if (StrEqual(key, "ban")) { - flag = Admin_Ban; - } else if (StrEqual(key, "unban")) { - flag = Admin_Unban; - } else if (StrEqual(key, "slay")) { - flag = Admin_Slay; - } else if (StrEqual(key, "changemap")) { - flag = Admin_Changemap; - } else if (StrEqual(key, "cvars")) { - flag = Admin_Convars; - } else if (StrEqual(key, "config")) { - flag = Admin_Config; - } else if (StrEqual(key, "chat")) { - flag = Admin_Chat; - } else if (StrEqual(key, "vote")) { - flag = Admin_Vote; - } else if (StrEqual(key, "password")) { - flag = Admin_Password; - } else if (StrEqual(key, "rcon")) { - flag = Admin_RCON; - } else if (StrEqual(key, "cheats")) { - flag = Admin_Cheats; - } else if (StrEqual(key, "root")) { - flag = Admin_Root; - } else if (StrEqual(key, "custom1")) { - flag = Admin_Custom1; - } else if (StrEqual(key, "custom2")) { - flag = Admin_Custom2; - } else if (StrEqual(key, "custom3")) { - flag = Admin_Custom3; - } else if (StrEqual(key, "custom4")) { - flag = Admin_Custom4; - } else if (StrEqual(key, "custom5")) { - flag = Admin_Custom5; - } else if (StrEqual(key, "custom6")) { - flag = Admin_Custom6; - } else { - ParseError("Unrecognized flag type: %s", key); - } - - g_FlagLetters[chr] = flag; - g_FlagsSet[chr] = true; - } - - return SMCParse_Continue; -} - -public SMCResult:ReadLevels_EndSection(Handle:smc) -{ - /* If we're ignoring, skip out */ - if (g_IgnoreLevel) - { - g_IgnoreLevel--; - return SMCParse_Continue; - } - - if (g_LevelState == LEVEL_STATE_FLAGS) - { - /* We're totally done parsing */ - g_LevelState = LEVEL_STATE_LEVELS; - return SMCParse_Halt; - } else if (g_LevelState == LEVEL_STATE_LEVELS) { - g_LevelState = LEVEL_STATE_NONE; - } - - return SMCParse_Continue; -} - -static InitializeLevelParser() -{ - if (g_hLevelParser == INVALID_HANDLE) - { - g_hLevelParser = SMC_CreateParser(); - SMC_SetReaders(g_hLevelParser, - ReadLevels_NewSection, - ReadLevels_KeyValue, - ReadLevels_EndSection); - } -} - -RefreshLevels() -{ - LoadDefaultLetters(); - InitializeLevelParser(); - - BuildPath(Path_SM, g_Filename, sizeof(g_Filename), "configs/admin_levels.cfg"); - - /* Set states */ - InitGlobalStates(); - g_LevelState = LEVEL_STATE_NONE; - - new SMCError:err = SMC_ParseFile(g_hLevelParser, g_Filename); - if (err != SMCError_Okay) - { - decl String:buffer[64]; - if (SMC_GetErrorString(err, buffer, sizeof(buffer))) - { - ParseError("%s", buffer); - } else { - ParseError("Fatal parse error"); - } - } -} diff --git a/plugins/admin-flatfile/admin-overrides.sp b/plugins/admin-flatfile/admin-overrides.sp index e715a274..d9f5ab56 100644 --- a/plugins/admin-flatfile/admin-overrides.sp +++ b/plugins/admin-flatfile/admin-overrides.sp @@ -69,27 +69,7 @@ public SMCResult:ReadOverrides_KeyValue(Handle:smc, return SMCParse_Continue; } - new AdminFlag:array[AdminFlags_TOTAL]; - new flags_total; - - new len = strlen(value); - for (new i=0; i 'z') - { - ParseError("Invalid flag detected: %c", value[i]); - continue; - } - new val = value[i] - 'a'; - if (!g_FlagsSet[val]) - { - ParseError("Invalid flag detected: %c", value[i]); - continue; - } - array[flags_total++] = g_FlagLetters[val]; - } - - new flags = FlagArrayToBits(array, flags_total); + new flags = ReadFlagString(value); if (key[0] == '@') { diff --git a/plugins/admin-flatfile/admin-simple.sp b/plugins/admin-flatfile/admin-simple.sp index fe80e9b2..9e70b505 100644 --- a/plugins/admin-flatfile/admin-simple.sp +++ b/plugins/admin-flatfile/admin-simple.sp @@ -115,23 +115,19 @@ ReadAdminLine(const String:line[]) new bool:is_default = false; for (new i=0; i 'z') + if (flags[i] == '$') { - if (flags[i] == '$') + is_default = true; + } else { + new AdminFlag:flag; + + if (!FindFlagByChar(flags[i], flag)) { - is_default = true; - } else { ParseError("Invalid flag detected: %c", flags[i]); + continue; } - continue; + SetAdminFlag(admin, flag, true); } - new val = flags[i] - 'a'; - if (!g_FlagsSet[val]) - { - ParseError("Invalid flag detected: %c", flags[i]); - continue; - } - SetAdminFlag(admin, g_FlagLetters[val], true); } if (is_default) diff --git a/plugins/admin-flatfile/admin-users.sp b/plugins/admin-flatfile/admin-users.sp index dc4ab71b..7e9ef06b 100644 --- a/plugins/admin-flatfile/admin-users.sp +++ b/plugins/admin-flatfile/admin-users.sp @@ -93,21 +93,15 @@ public SMCResult:ReadUsers_KeyValue(Handle:smc, } } else if (StrEqual(key, "flags")) { new len = strlen(value); + new AdminFlag:flag; for (new i=0; i 'z') + if (!FindFlagByChar(value[i], flag)) { ParseError("Invalid flag detected: %c", value[i]); - continue; } - new val = value[i] - 'a'; - if (!g_FlagsSet[val]) - { - ParseError("Invalid flag detected: %c", value[i]); - continue; - } - SetAdminFlag(g_CurUser, g_FlagLetters[val], true); + SetAdminFlag(g_CurUser, flag, true); } } diff --git a/plugins/include/admin.inc b/plugins/include/admin.inc index 0d1b9b87..ae9b43d1 100644 --- a/plugins/include/admin.inc +++ b/plugins/include/admin.inc @@ -488,6 +488,33 @@ native FlagArrayToBits(const AdminFlag:array[], numFlags); */ native FlagBitsToArray(bits, AdminFlag:array[], maxSize); +/** + * Finds a flag by its string name. + * + * @param name Flag name (like "kick"), case sensitive. + * @param flag Variable to store flag in. + * @return True on success, false if not found. + */ +native bool:FindFlagByName(const String:name[], &AdminFlag:flag); + +/** + * Finds a flag by a given character. + * + * @param c Flag ASCII character/token. + * @param flag Variable to store flag in. + * @return True on success, false if not found. + */ +native bool:FindFlagByChar(c, &AdminFlag:flag); + +/** + * Converts a string of flag characters to a bit string. + * + * @param flags Flag ASCII string. + * @param numchars Optional variable to store the number of bytes read. + * @return Bit string of ADMFLAG values. + */ +native ReadFlagString(const String:flags[], &numchars=0); + /** * Tests whether one admin can target another. * diff --git a/public/IAdminSystem.h b/public/IAdminSystem.h index 26acb55b..c1c1537a 100644 --- a/public/IAdminSystem.h +++ b/public/IAdminSystem.h @@ -36,7 +36,7 @@ #include #define SMINTERFACE_ADMINSYS_NAME "IAdminSys" -#define SMINTERFACE_ADMINSYS_VERSION 1 +#define SMINTERFACE_ADMINSYS_VERSION 2 /** * @file IAdminSystem.h @@ -599,6 +599,34 @@ namespace SourceMod * @return True if this admin has permission to target the other admin. */ virtual bool CanAdminTarget(AdminId id, AdminId target) =0; + + /** + * @brief Returns a flag from a named string. + * + * @param flagname Case sensitive flag name string (like "kick"). + * @param pAdmFlag Pointer to store the found admin flag in. + * @return True on success, false on failure. + */ + virtual bool FindFlag(const char *flagname, AdminFlag *pAdmFlag) =0; + + /** + * @brief Reads a single character as a flag. + * + * @param flag Flag character. + * @param pAdmFlag Pointer to store the admin flag. + * @return True on success, false if invalid. + */ + virtual bool FindFlag(char c, AdminFlag *pAdmFlag) =0; + + /** + * @brief Reads a string of flag letters and returns its access value. + * + * @param flags Flag string. + * @param end Pointer to store the last value read. On success, + * this will store a pointer to the null terminator. + * @return FlagBits value of the flags. + */ + virtual FlagBits ReadFlagString(const char *flags, const char **end) =0; }; }