From 25b0caa68a888b1a79704551128809726d17546a Mon Sep 17 00:00:00 2001 From: Greyscale Date: Mon, 18 May 2009 06:26:13 +0200 Subject: [PATCH] Added config reloading support. Revision also includes: * Added syntax checking on recoded/new console commands. * Fixed sv_skyname handle being used when invalid. * Fixed unhooking error on Weapon_Drop. * Prefixed some API-style functions with 'stock' to stop compiler warning. * Separated downloads into its own file. --- .../translations/zombiereloaded.phrases.txt | 71 +++- src/zombiereloaded.sp | 2 + src/zr/commands.inc | 1 + src/zr/config.inc | 401 +++++++++++++++--- src/zr/cvars.inc | 4 +- src/zr/downloads.inc | 110 +++++ src/zr/hitgroups.inc | 29 +- src/zr/models.inc | 92 +--- src/zr/playerclasses/playerclasses.inc | 24 +- src/zr/visualeffects.inc | 42 +- src/zr/weapons/restrict.inc | 39 +- src/zr/weapons/weaponalpha.inc | 12 +- src/zr/weapons/weapons.inc | 64 ++- 13 files changed, 675 insertions(+), 216 deletions(-) create mode 100644 src/zr/downloads.inc diff --git a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt index 8f49323..38b8172 100644 --- a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt @@ -77,7 +77,48 @@ } // =========================== - // Player Classes + // Config (core) + // =========================== + + // Commands + + "Config command reload syntax" + { + "#format" "{1:s},{2:s},{3:s},{4:s},{5:s},{6:s}" + "en" "Syntax: zr_reloadconfig - Reloads a config file.\n File Aliases:\n * \"{1}\"\n * \"{2}\"\n * \"{3}\"\n * \"{4}\"\n * \"{5}\"\n * \"{6}\"" + } + + "Config command reload invalid" + { + "#format" "{1:s}" + "en" "Invalid file alias: \"{1}\"" + } + + "Config command reload not loaded" + { + "#format" "{1:s}" + "en" "File failed to load: \"{1}\" (Either disabled or invalid file content.)" + } + + "Config command reload all stats begin" + { + "en" "Reloading all Zombie:Reloaded config files...\n ------------------------------------------" + } + + "Config command reload all stats successful" + { + "#format" "{1:s}" + "en" "\"{1}\" - Successful." + } + + "Config command reload all stats failed" + { + "#format" "{1:s}" + "en" "\"{1}\" - Failed. (Either disabled or invalid file content.)" + } + + // =========================== + // Classes (core) // =========================== // General @@ -130,7 +171,7 @@ } // =========================== - // (core) Overlays + // Overlays (core) // =========================== "Overlays not supported" @@ -141,7 +182,7 @@ } // =========================== - // (core) Infect + // Infect (core) // =========================== // General @@ -158,7 +199,7 @@ } // =========================== - // (core) Damage + // Damage (core) // =========================== "Damage suicide intercept" @@ -215,7 +256,7 @@ } // =========================== - // (core) Weapons + // Weapons (core) // =========================== // General @@ -310,6 +351,18 @@ "ru" "Купить снова" } + // Commands + + "Weapons command restrict syntax" + { + "en" "Syntax: zr_restrict (\"weapon_\" prefix is ignored)" + } + + "Weapons command unrestrict syntax" + { + "en" "Syntax: zr_unrestrict (\"weapon_\" prefix is ignored)" + } + // Menu "Weapons menu main title" @@ -370,7 +423,7 @@ } // =========================== - // Spawn Protect + // Spawn Protect (module) // =========================== // General @@ -397,7 +450,7 @@ } // =========================== - // ZSpawn + // ZSpawn (module) // =========================== "ZSpawn double spawn" @@ -412,7 +465,7 @@ } // =========================== - // ZTele + // ZTele (module) // =========================== // General @@ -459,7 +512,7 @@ } // =========================== - // ZHP + // ZHP (module) // =========================== // General diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index 51f28ca..cfcb3a7 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -31,6 +31,7 @@ #include "zr/sayhooks" #include "zr/tools" #include "zr/models" +#include "zr/downloads" #include "zr/overlays" #include "zr/playerclasses/playerclasses" #include "zr/weapons/weapons" @@ -165,6 +166,7 @@ public OnConfigsExecuted() // Forward event to modules. ConfigLoad(); ModelsLoad(); + DownloadsLoad(); WeaponsLoad(); HitgroupsLoad(); InfectLoad(); diff --git a/src/zr/commands.inc b/src/zr/commands.inc index 688da55..2008702 100644 --- a/src/zr/commands.inc +++ b/src/zr/commands.inc @@ -16,6 +16,7 @@ CommandsInit() { // Forward event to modules. (create commands) + ConfigOnCommandsCreate(); WeaponsOnCommandsCreate(); // Forward event to modules. (hook commands) diff --git a/src/zr/config.inc b/src/zr/config.inc index e89ab0a..7a5d577 100644 --- a/src/zr/config.inc +++ b/src/zr/config.inc @@ -11,59 +11,84 @@ */ /** - * @section List of config files under this modules control. + * The max length of any config string value. */ -#define CONFIG_FILE_MODELS 0 -#define CONFIG_FILE_DOWNLOADS 1 -#define CONFIG_FILE_PLAYERCLASSES 2 -#define CONFIG_FILE_WEAPONS 3 -#define CONFIG_FILE_WEAPONGROUPS 4 -#define CONFIG_FILE_HITGROUPS 5 +#define CONFIG_MAX_LENGTH 32 + +/** + * @section Config file reference aliases. + */ +#define CONFIG_FILE_ALIAS_MODELS "models" +#define CONFIG_FILE_ALIAS_DOWNLOADS "downloads" +#define CONFIG_FILE_ALIAS_CLASSES "classes" +#define CONFIG_FILE_ALIAS_WEAPONS "weapons" +#define CONFIG_FILE_ALIAS_WEAPONGROUPS "weapongroups" +#define CONFIG_FILE_ALIAS_HITGROUPS "hitgroups" /** * @endsection */ /** - * @section Config file flags. - */ -#define CONFIG_FILE_FLAG_MODELS 1 -#define CONFIG_FILE_FLAG_DOWNLOADS 2 -#define CONFIG_FILE_FLAG_PLAYERCLASSES 4 -#define CONFIG_FILE_FLAG_WEAPONS 8 -#define CONFIG_FILE_FLAG_WEAPONGROUPS 16 -#define CONFIG_FILE_FLAG_HITGROUPS 32 -/** - * @endsection + * List of config files used by the plugin. */ +enum ConfigFile +{ + ConfigInvalid = -1, /** Invalid config file. */ + ConfigModels, /** /configs/zr/models.txt (default) */ + ConfigDownloads, /** /configs/zr/downloads.txt (default) */ + ConfigClasses, /** /configs/zr/playerclasses.txt (default) */ + ConfigWeapons, /** /configs/zr/weapons/weapons.txt/weapongroups.txt (default) */ + ConfigHitgroups, /** /configs/zr/hitgroups.txt (default) */ +} /** - * The max length of a config/value string. + * Data container for each config file. */ -#define CONFIG_OPTION_MAX_LENGTH 32 +enum ConfigData +{ + bool:ConfigLoaded, /** True if config is loaded, false if not. */ + Function:ConfigReloadFunc, /** Function to call to reload config. */ + Handle:ConfigHandle, /** Handle of the config file. */ + String:ConfigPath[PLATFORM_MAX_PATH], /** Full path to config file. */ + String:ConfigAlias[CONFIG_MAX_LENGTH], /** Config file alias, used for client interaction. */ +} + +/** + * Stores all config data. + */ +new g_ConfigData[ConfigFile][ConfigData]; /** * Actions to use when working on key/values. */ enum ConfigKeyvalueAction { - Create, /** Create a key. */ - Delete, /** Delete a key. */ - Set, /** Modify setting of a key. */ - Get, /** Get setting of a key. */ + ConfigKVCreate, /** Create a key. */ + ConfigKVDelete, /** Delete a key. */ + ConfigKVSet, /** Modify setting of a key. */ + ConfigKVGet, /** Get setting of a key. */ } -/** - * @endsection - */ /** * @section Global data handle initializations. */ new Handle:arrayModelsList = INVALID_HANDLE; +new Handle:arrayDownloadsList = INVALID_HANDLE; new Handle:kvClassData = INVALID_HANDLE; new Handle:kvWeapons = INVALID_HANDLE; new Handle:kvWeaponGroups = INVALID_HANDLE; new Handle:kvHitgroups = INVALID_HANDLE; +/** + * Create commands related to config here. + */ +ConfigOnCommandsCreate() +{ + // Create config admin commands. + RegAdminCmd("zr_reloadconfig", ConfigReloadCommand, ADMFLAG_GENERIC, "zr_reloadconfig - Reloads a config file."); + RegAdminCmd("zr_reloadconfigall", ConfigReloadAllCommand, ADMFLAG_GENERIC, "zr_reloadconfigall - Reloads all config files."); +} + /** * Load plugin configs. */ @@ -130,13 +155,187 @@ ConfigOnModulesLoaded() LogPrintToLog(LOG_FORMAT_TYPE_NORMAL, "Config", "Map Configs", "Executed post map config file: %s", path); } +/** + * Used by modules that rely on configs to register their config file info. + * + * @param file Config file entry to register. + * @param loaded True if the config should be loaded, false if not. + * @param path (Optional) Full path to config file. + * @param alias (Optional) Config file alias, used for client interaction. + */ +stock ConfigRegisterConfig(ConfigFile:file, bool:loaded, Function:reloadfunc, Handle:filehandle = INVALID_HANDLE, const String:path[] = "", const String:alias[] = "") +{ + // Copy file info to data container. + g_ConfigData[file][ConfigLoaded] = loaded; + g_ConfigData[file][ConfigHandle] = filehandle; + g_ConfigData[file][ConfigReloadFunc] = reloadfunc; + strcopy(g_ConfigData[file][ConfigPath], PLATFORM_MAX_PATH, path); + strcopy(g_ConfigData[file][ConfigAlias], CONFIG_MAX_LENGTH, alias); +} + +/** + * Set the loaded state of a config file entry. + * + * @param config Config file to set load state of. + * @param loaded True to set as loaded, false to set as unloaded. + */ +stock ConfigSetConfigLoaded(ConfigFile:config, bool:loaded) +{ + // Set load state. + g_ConfigData[config][ConfigLoaded] = loaded; +} + +/** + * Set the reload function of a config file entry. + * + * @param config Config file to set reload function of. + * @param reloadfunc Reload function. + */ +stock ConfigSetConfigReloadFunc(ConfigFile:config, Function:reloadfunc) +{ + // Set reload function. + g_ConfigData[config][ConfigReloadFunc] = reloadfunc; +} + +/** + * Set the file handle of a config file entry. + * + * @param config Config file to set handle of. + * @param loaded Config file handle. + */ +stock ConfigSetConfigHandle(ConfigFile:config, Handle:file) +{ + // Set file handle. + g_ConfigData[config][ConfigHandle] = file; +} + +/** + * Set the config file path of a config file entry. + * + * @param config Config file to set file path of. + * @param loaded File path. + */ +stock ConfigSetConfigPath(ConfigFile:config, const String:path[]) +{ + // Set config file path. + strcopy(g_ConfigData[config][ConfigPath], PLATFORM_MAX_PATH, path); +} + +/** + * Set the alias of a config file entry. + * + * @param config Config file to set alias of. + * @param loaded Alias of the config file entry. + */ +stock ConfigSetConfigAlias(ConfigFile:config, const String:alias[]) +{ + // Set config alias. + strcopy(g_ConfigData[config][ConfigAlias], CONFIG_MAX_LENGTH, alias); +} + +/** + * Returns if a config was successfully loaded. + * + * @param config Config file to check load status of. + * @return True if config is loaded, false otherwise. + */ +stock bool:ConfigIsConfigLoaded(ConfigFile:config) +{ + // Return load status. + return g_ConfigData[config][ConfigLoaded]; +} + +/** + * Returns config's reload function. + * + * @param config Config file to get reload function of. + * @return Config reload function. + */ +stock Function:ConfigGetConfigReloadFunc(ConfigFile:config) +{ + // Return load status. + return g_ConfigData[config][ConfigReloadFunc]; +} + +/** + * Returns config's file handle. + * + * @param config Config file to get file handle of. + * @return Config file handle. + */ +stock Handle:ConfigGetConfigHandle(ConfigFile:config) +{ + // Return load status. + return g_ConfigData[config][ConfigHandle]; +} + +/** + * Returns the path for a given config file entry. + * + * @param config Config file to get path of. (see ConfigFile enum) + */ +stock ConfigGetConfigPath(ConfigFile:config, String:path[], maxlen) +{ + // Copy path to return string. + strcopy(path, maxlen, g_ConfigData[config][ConfigPath]); +} + +/** + * Returns the alias for a given config file entry. + * + * @param config Config file to get alias of. (see ConfigFile enum) + */ +stock ConfigGetConfigAlias(ConfigFile:config, String:alias[], maxlen) +{ + // Copy alias to return string. + strcopy(alias, maxlen, g_ConfigData[config][ConfigAlias]); +} + +/** + * Reload a config file. + * + * @param config The config file entry to reload. + * @return True if the config is loaded, false if not. + */ +stock bool:ConfigReloadFile(ConfigFile:config) +{ + // If file isn't loaded, then stop. + new bool:loaded = ConfigIsConfigLoaded(config); + if (!loaded) + { + return false; + } + + // Call reload function + new Function:reloadfunc = ConfigGetConfigReloadFunc(config); + + if (reloadfunc == INVALID_FUNCTION) + { + // Get config alias. + decl String:configalias[CONFIG_MAX_LENGTH]; + ConfigGetConfigAlias(config, configalias, sizeof(configalias)); + + // Print reload failure to logs. + LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Config", "Reload Function", "Invalid reload function for config: \"%s\"", configalias); + + return true; + } + + // Call reload function. + Call_StartFunction(GetMyHandle(), reloadfunc); + Call_PushCell(config); + Call_Finish(); + + return true; +} + /** * Load config file. * * @param file The cvar define of the path to the file. * @return True if the file exists, false if not. */ -bool:ConfigGetFilePath(CvarsList:cvar, String:path[]) +stock bool:ConfigGetCvarFilePath(CvarsList:cvar, String:path[]) { // Get cvar's path. decl String:filepath[PLATFORM_MAX_PATH]; @@ -148,6 +347,36 @@ bool:ConfigGetFilePath(CvarsList:cvar, String:path[]) return FileExists(path); } +/** + * Finds a config file entry, (see ConfigFile enum) for a given alias. + * + * @param alias The alias to find config file entry of. + * @return Config file entry, ConfigInvalid is returned if alias was not found. + */ +stock ConfigFile:ConfigAliasToConfigFile(const String:alias[]) +{ + decl String:checkalias[CONFIG_MAX_LENGTH]; + + // x = config file entry index. + for (new x = 0; x < sizeof(g_ConfigData); x++) + { + // Get config alias. + ConfigGetConfigAlias(ConfigFile:x, checkalias, sizeof(checkalias)); + + // If alias doesn't match, then stop. + if (!StrEqual(alias, checkalias, false)) + { + continue; + } + + // Return config file entry. + return ConfigFile:x; + } + + // Invalid config file. + return ConfigInvalid; +} + /** * Creates, deletes, sets, or gets any key/setting of any ZR config keyvalue file in memory. * Only use when interacting with a command or manipulating single keys/values, @@ -162,10 +391,10 @@ bool:ConfigGetFilePath(CvarsList:cvar, String:path[]) * @param maxlen (Optional) The maxlength of the gotten value. * @return True if the change was made successfully, false otherwise. */ -bool:ConfigKeyvalueTreeSetting(config, ConfigKeyvalueAction:action = Create, const String:keys[][], keysMax, const String:setting[] = "", String:value[] = "", maxlen = 0) +stock bool:ConfigKeyvalueTreeSetting(config, ConfigKeyvalueAction:action = ConfigKVCreate, const String:keys[][], keysMax, const String:setting[] = "", String:value[] = "", maxlen = 0) { // Retrieve handle of the keyvalue tree. - new Handle:hConfig = ConfigGetFileHandle(config); + new Handle:hConfig = ConfigGetConfigHandle(config); // If handle is invalid, then stop. if (hConfig == INVALID_HANDLE) @@ -199,7 +428,7 @@ bool:ConfigKeyvalueTreeSetting(config, ConfigKeyvalueAction:action = Create, con switch(action) { - case Create: + case ConfigKVCreate: { if (!setting[0] || !value[0]) { @@ -210,17 +439,17 @@ bool:ConfigKeyvalueTreeSetting(config, ConfigKeyvalueAction:action = Create, con // Set new value. KvSetString(hConfig, setting, value); } - case Delete: + case ConfigKVDelete: { // Return deletion result. return KvDeleteKey(hConfig, setting); } - case Set: + case ConfigKVSet: { // Set new value. KvSetString(hConfig, setting, value); } - case Get: + case ConfigKVGet: { // Get current value. KvGetString(hConfig, setting, value, maxlen); @@ -232,48 +461,82 @@ bool:ConfigKeyvalueTreeSetting(config, ConfigKeyvalueAction:action = Create, con } /** - * Return handle to array or keygroup for globally stored data. + * Command callback (zr_reloadconfig) + * Reloads a config file and forwards event to modules. * - * @param configindex Index of the config. (see CONFIG_FILE_* defines) + * @param client The client index. + * @param argc Argument count. */ -Handle:ConfigGetFileHandle(config) +public Action:ConfigReloadCommand(client, argc) { - switch(config) + // If not enough arguments given, then stop. + if (argc < 1) { - case CONFIG_FILE_MODELS: - { - // Return model list array handle. - return arrayModelsList; - } - case CONFIG_FILE_DOWNLOADS: - { - // We don't store download data. - return INVALID_HANDLE; - } - case CONFIG_FILE_PLAYERCLASSES: - { - // Return class config keyvalue file handle. - return kvClassData; - } - case CONFIG_FILE_WEAPONS: - { - // Return weapon config keyvalue file handle. - return kvWeapons; - } - case CONFIG_FILE_WEAPONGROUPS: - { - // Return weapon groups config keyvalue file handle. - return kvWeaponGroups; - } - case CONFIG_FILE_HITGROUPS: - { - // Return hitgroups config keyvalue file handle. - return kvHitgroups; - } + TranslationReplyToCommand(client, "Config command reload syntax", CONFIG_FILE_ALIAS_MODELS, CONFIG_FILE_ALIAS_DOWNLOADS, CONFIG_FILE_ALIAS_CLASSES, CONFIG_FILE_ALIAS_WEAPONS, CONFIG_FILE_ALIAS_WEAPONGROUPS, CONFIG_FILE_ALIAS_HITGROUPS); + return Plugin_Handled; } - // Invalid config index. - return INVALID_HANDLE; + // arg1 = file alias being reloaded. + decl String:arg1[32]; + GetCmdArg(1, arg1, sizeof(arg1)); + + // If alias is invalid, then stop. + new ConfigFile:config = ConfigAliasToConfigFile(arg1); + if (config == ConfigInvalid) + { + TranslationReplyToCommand(client, "Config command reload invalid", arg1); + return Plugin_Handled; + } + + // Reload config file. + new bool:loaded = ConfigReloadFile(config); + + // Get config file path. + decl String:path[PLATFORM_MAX_PATH]; + ConfigGetConfigPath(config, path, sizeof(path)); + + // If file isn't loaded then tell client, then stop. + if (!loaded) + { + TranslationReplyToCommand(client, "Config command reload not loaded", path); + return Plugin_Handled; + } + + return Plugin_Handled; +} + +/** + * Command callback (zr_reloadconfigall) + * Reloads all config files and forwards event to all modules. + * + * @param client The client index. + * @param argc Argument count. + */ +public Action:ConfigReloadAllCommand(client, argc) +{ + // Begin statistics. + TranslationReplyToCommand(client, "Config command reload all stats begin"); + + decl String:configalias[CONFIG_MAX_LENGTH]; + + // x = config file entry index. + for (new x = 0; x < sizeof(g_ConfigData); x++) + { + // Reload config file. + new bool:successful = ConfigReloadFile(ConfigFile:x); + + // Get config's alias. + ConfigGetConfigAlias(ConfigFile:x, configalias, sizeof(configalias)); + + if (successful) + { + TranslationReplyToCommand(client, "Config command reload all stats successful", configalias); + } + else + { + TranslationReplyToCommand(client, "Config command reload all stats failed", configalias); + } + } } /** diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index 3ba234c..b2cd842 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -36,7 +36,7 @@ enum CvarsList Handle:CVAR_LOG_PRINT_CONSOLE, Handle:CVAR_CONFIG_PATH_MODELS, Handle:CVAR_CONFIG_PATH_DOWNLOADS, - Handle:CVAR_CONFIG_PATH_PLAYERCLASSES, + Handle:CVAR_CONFIG_PATH_CLASSES, Handle:CVAR_CONFIG_PATH_WEAPONS, Handle:CVAR_CONFIG_PATH_WEAPONGROUPS, Handle:CVAR_CONFIG_PATH_HITGROUPS, @@ -207,7 +207,7 @@ CvarsCreate() g_hCvarsList[CVAR_CONFIG_PATH_MODELS] = CreateConVar("zr_config_path_models", "configs/zr/models.txt", "Path, relative to root sourcemod directory, to models config file."); g_hCvarsList[CVAR_CONFIG_PATH_DOWNLOADS] = CreateConVar("zr_config_path_downloads", "configs/zr/downloads.txt", "Path, relative to root sourcemod directory, to downloads file."); - g_hCvarsList[CVAR_CONFIG_PATH_PLAYERCLASSES] = CreateConVar("zr_config_path_playerclasses", "configs/zr/playerclasses.txt", "Path, relative to root sourcemod directory, to playerclasses config file."); + g_hCvarsList[CVAR_CONFIG_PATH_CLASSES] = CreateConVar("zr_config_path_playerclasses", "configs/zr/playerclasses.txt", "Path, relative to root sourcemod directory, to playerclasses config file."); g_hCvarsList[CVAR_CONFIG_PATH_WEAPONS] = CreateConVar("zr_config_path_weapons", "configs/zr/weapons/weapons.txt", "Path, relative to root sourcemod directory, to weapons config file."); g_hCvarsList[CVAR_CONFIG_PATH_WEAPONGROUPS] = CreateConVar("zr_config_path_weapongroups", "configs/zr/weapons/weapongroups.txt", "Path, relative to root sourcemod directory, to weapongroups config file."); g_hCvarsList[CVAR_CONFIG_PATH_HITGROUPS] = CreateConVar("zr_config_path_hitgroups", "configs/zr/hitgroups.txt", "Path, relative to root sourcemod directory, to hitgroups config file."); diff --git a/src/zr/downloads.inc b/src/zr/downloads.inc new file mode 100644 index 0000000..615a4a3 --- /dev/null +++ b/src/zr/downloads.inc @@ -0,0 +1,110 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: downloads.inc + * Type: Core + * Description: Download validation. + * + * ============================================================================ + */ + +/** + * Array that stores a list of downloadable files. + * + * @redir config.inc + */ + +/** + * Prepare all model/download data. + */ +DownloadsLoad() +{ + // Get downloads file path. + decl String:pathdownloads[PLATFORM_MAX_PATH]; + new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_DOWNLOADS, pathdownloads); + + // Register config info. + ConfigRegisterConfig(ConfigDownloads, false, GetFunctionByName(GetMyHandle(), "DownloadsOnConfigReload"), _, pathdownloads, CONFIG_FILE_ALIAS_DOWNLOADS); + + // If file doesn't exist, then log. + if (!exists) + { + // Log error, then stop. + LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Downloads", "Config Validation", "Missing downloads file: \"%s\"", pathdownloads); + + return; + } + + // If download array exists, then destroy it. + if (arrayDownloadsList != INVALID_HANDLE) + { + CloseHandle(arrayDownloadsList); + } + + arrayDownloadsList = ConfigLinesToArray(pathdownloads); + + // If array couldn't be created, then fail. + if (arrayDownloadsList == INVALID_HANDLE) + { + LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Downloads", "Config Validation", "Error parsing \"%s\"", pathdownloads); + } + + new downloadcount; + new downloadvalidcount; + + decl String:downloadpath[PLATFORM_MAX_PATH]; + + new downloads = downloadcount = GetArraySize(arrayDownloadsList); + + // x = download array index. + for (new x = 0; x < downloads; x++) + { + // Get base model path (rawline in models.txt) + GetArrayString(arrayDownloadsList, x, downloadpath, sizeof(downloadpath)); + + // If file doesn't exist, then remove, log, and stop. + if (!FileExists(downloadpath)) + { + // Remove client from array. + RemoveFromArray(arrayDownloadsList, x); + + // Subtract one from count. + downloads--; + + // Backtrack one index, because we deleted it out from under the loop. + x--; + + LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Downloads", "Config Validation", "Missing file \"%s\"", downloadpath); + continue; + } + + // Increment downloadvalidcount + downloadvalidcount++; + + // Precache model file and add to downloads table. + AddFileToDownloadsTable(downloadpath); + } + + // Log model validation info. + LogPrintToLog(LOG_FORMAT_TYPE_NORMAL, "Downloads", "Config Validation", "Total: %d | Successful: %d | Unsuccessful: %d", downloadcount, downloadvalidcount, downloadcount - downloadvalidcount); + + // Set config data. + ConfigSetConfigLoaded(ConfigDownloads, true); + ConfigSetConfigHandle(ConfigDownloads, arrayDownloadsList); +} + +/** + * Called when configs are being reloaded. + * + * @param config The config being reloaded. (only if 'all' is false) + */ +public DownloadsOnConfigReload(ConfigFile:config) +{ + // Reload download config. + if (config == ConfigDownloads) + { + DownloadsLoad(); + } +} \ No newline at end of file diff --git a/src/zr/hitgroups.inc b/src/zr/hitgroups.inc index 8fe4ef3..d1f5ad6 100644 --- a/src/zr/hitgroups.inc +++ b/src/zr/hitgroups.inc @@ -54,6 +54,13 @@ HitgroupsLoad() // Clear hitgroup data HitgroupsClearData(); + // Get hitgroups config path. + decl String:pathhitgroups[PLATFORM_MAX_PATH]; + new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_HITGROUPS, pathhitgroups); + + // Register config info. + ConfigRegisterConfig(ConfigHitgroups, false, GetFunctionByName(GetMyHandle(), "HitgroupsOnConfigReload"), _, pathhitgroups, CONFIG_FILE_ALIAS_HITGROUPS); + // If module is disabled, then stop. new bool:hitgroups = GetConVarBool(g_hCvarsList[CVAR_HITGROUPS]); if (!hitgroups) @@ -61,10 +68,6 @@ HitgroupsLoad() return; } - // Get hitgroups config path. - decl String:pathhitgroups[PLATFORM_MAX_PATH]; - new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_HITGROUPS, pathhitgroups); - // If file doesn't exist, then log and stop. if (!exists) { @@ -78,6 +81,24 @@ HitgroupsLoad() // Validate hitgroups config. HitgroupsValidateConfig(); + + // Set config data. + ConfigSetConfigLoaded(ConfigHitgroups, true); + ConfigSetConfigHandle(ConfigHitgroups, kvHitgroups); +} + +/** + * Called when configs are being reloaded. + * + * @param config The config being reloaded. (only if 'all' is false) + */ +public HitgroupsOnConfigReload(ConfigFile:config) +{ + // Reload hitgroups config. + if (config == ConfigHitgroups) + { + HitgroupsLoad(); + } } /** diff --git a/src/zr/models.inc b/src/zr/models.inc index fc235ee..3596b26 100644 --- a/src/zr/models.inc +++ b/src/zr/models.inc @@ -5,7 +5,7 @@ * * File: models.inc * Type: Core - * Description: Model validation and API + * Description: Model validation. * * ============================================================================ */ @@ -26,23 +26,17 @@ * @redir config.inc */ -ModelsLoad() -{ - // Add models to downloads table and validate. - ModelsPrepModels(); - - // Add download entries to downloads table and validate. - ModelsPrepDownloads(); -} - /** - * Validate model paths and add to downloads table. + * Prepare all model/download data. */ -ModelsPrepModels() +ModelsLoad() { // Get models file path. decl String:pathmodels[PLATFORM_MAX_PATH]; - new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_MODELS, pathmodels); + new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_MODELS, pathmodels); + + // Register config info. + ConfigRegisterConfig(ConfigModels, false, GetFunctionByName(GetMyHandle(), "ModelsOnConfigReload"), _, pathmodels, CONFIG_FILE_ALIAS_MODELS); // If file doesn't exist, then log and stop. if (!exists) @@ -164,70 +158,22 @@ ModelsPrepModels() { LogPrintToLog(LOG_FORMAT_TYPE_FATALERROR, "Models", "Config Validation", "Fatal Error: No usable model paths in %s", pathmodels); } + + // Set config data. + ConfigSetConfigLoaded(ConfigModels, true); + ConfigSetConfigHandle(ConfigModels, arrayModelsList); } /** - * Validate custom download paths and add to downloads table. + * Called when configs are being reloaded. + * + * @param config The config being reloaded. (only if 'all' is false) */ -ModelsPrepDownloads() +public ModelsOnConfigReload(ConfigFile:config) { - // Get downloads file path. - decl String:pathdownloads[PLATFORM_MAX_PATH]; - new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_DOWNLOADS, pathdownloads); - - // If file doesn't exist, then log. - if (!exists) + // Reload model config. + if (config == ConfigModels) { - // Log error, then stop. - LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Downloads", "Config Validation", "Missing downloads file: \"%s\"", pathdownloads); - - return; + ModelsLoad(); } - - new Handle:arrayDownloadsList = ConfigLinesToArray(pathdownloads); - - // If array couldn't be created, then fail. - if (arrayModelsList == INVALID_HANDLE) - { - LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Downloads", "Config Validation", "Error parsing \"%s\"", pathdownloads); - } - - new downloadcount; - new downloadvalidcount; - - decl String:downloadpath[PLATFORM_MAX_PATH]; - - new downloads = downloadcount = GetArraySize(arrayDownloadsList); - - // x = download array index. - for (new x = 0; x < downloads; x++) - { - // Get base model path (rawline in models.txt) - GetArrayString(arrayDownloadsList, x, downloadpath, sizeof(downloadpath)); - - // If file doesn't exist, then remove, log, and stop. - if (!FileExists(downloadpath)) - { - // Remove client from array. - RemoveFromArray(arrayDownloadsList, x); - - // Subtract one from count. - downloads--; - - // Backtrack one index, because we deleted it out from under the loop. - x--; - - LogPrintToLog(LOG_FORMAT_TYPE_ERROR, "Downloads", "Config Validation", "Missing file \"%s\"", downloadpath); - continue; - } - - // Increment downloadvalidcount - downloadvalidcount++; - - // Precache model file and add to downloads table. - AddFileToDownloadsTable(downloadpath); - } - - // Log model validation info. - LogPrintToLog(LOG_FORMAT_TYPE_NORMAL, "Downloads", "Config Validation", "Total: %d | Successful: %d | Unsuccessful: %d", downloadcount, downloadvalidcount, downloadcount - downloadvalidcount); -} +} \ No newline at end of file diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index 052ee8a..15a52c8 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -318,7 +318,7 @@ new ClassPlayerNextAdminClass[MAXPLAYERS + 1]; /** * Loads class attributes from the class file into ClassData array. If any - * error occour the plugin load will fail, and errors will be logged. + * error occur the plugin load will fail, and errors will be logged. */ ClassLoad() { @@ -331,7 +331,10 @@ ClassLoad() // Get weapons config path. decl String:pathclasses[PLATFORM_MAX_PATH]; - new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_PLAYERCLASSES, pathclasses); + new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_CLASSES, pathclasses); + + // Register config info. + ConfigRegisterConfig(ConfigClasses, false, GetFunctionByName(GetMyHandle(), "ClassOnConfigReload"), _, pathclasses, CONFIG_FILE_ALIAS_CLASSES); // If file doesn't exist, then log and stop. if (!exists) @@ -458,9 +461,24 @@ ClassLoad() // Log summary. LogPrintToLog(LOG_FORMAT_TYPE_NORMAL, "Classes", "Config Validation", "Total: %d | Successful: %d | Unsuccessful: %d", ClassCount, ClassCount - failedcount, failedcount); + + // Set config data. + ConfigSetConfigLoaded(ConfigClasses, true); + ConfigSetConfigHandle(ConfigClasses, kvClassData); } - +/** + * Called when configs are being reloaded. + * + * @param config The config being reloaded. (only if 'all' is false) + */ +public ClassOnConfigReload(ConfigFile:config) +{ + // Reload class config. + if (config == ConfigClasses) + { + } +} /** * Updates the class data cache. Original values are retrieved from ClassData. diff --git a/src/zr/visualeffects.inc b/src/zr/visualeffects.inc index 4b56744..5803ca6 100644 --- a/src/zr/visualeffects.inc +++ b/src/zr/visualeffects.inc @@ -10,11 +10,6 @@ * ============================================================================ */ -/** - * Handle of cvar "sv_skyname." - */ -new Handle:g_hSkyname = INVALID_HANDLE; - /** * Default sky of current map. */ @@ -25,17 +20,16 @@ new String:g_VEffectsDefaultSky[PLATFORM_MAX_PATH]; */ VEffectsLoad() { - // Get sv_skyname's convar handle, if invalid, log error, then stop. - g_hSkyname = FindConVar("sv_skyname"); - if (g_hSkyname == INVALID_HANDLE) - { - // TODO LOG. - - return; - } + // Apply all visual effects now + VEffectsApplyAll(); - // Store map's default sky before applying new one. - GetConVarString(g_hSkyname, g_VEffectsDefaultSky, sizeof(g_VEffectsDefaultSky)); + // Find map's default sky. + new Handle:hSkyname = FindConVar("sv_skyname"); + if (hSkyname != INVALID_HANDLE) + { + // Store map's default sky before applying new one. + GetConVarString(hSkyname, g_VEffectsDefaultSky, sizeof(g_VEffectsDefaultSky)); + } // If sky is disabled, then stop. new bool:sky = GetConVarBool(g_hCvarsList[CVAR_VEFFECTS_SKY]); @@ -55,9 +49,6 @@ VEffectsLoad() // Add skybox file to downloads table. AddFileToDownloadsTable(downloadpath); - - // Apply all visual effects now - VEffectsApplyAll(); } /** @@ -239,11 +230,20 @@ VEffectsApplyLightStyle(bool:disable = false) VEffectsApplySky(bool:disable = false) { + // if we can't find the sv_skyname cvar, then stop. + new Handle:hSkyname = FindConVar("sv_skyname"); + if (hSkyname == INVALID_HANDLE) + { + return; + } + // If default, then set to default sky. if (disable) { - // Set new sky on all clients. - SetConVarString(g_hSkyname, g_VEffectsDefaultSky, true); + if (g_VEffectsDefaultSky[0]) + { + SetConVarString(hSkyname, g_VEffectsDefaultSky, true); + } return; } @@ -253,7 +253,7 @@ VEffectsApplySky(bool:disable = false) GetConVarString(g_hCvarsList[CVAR_VEFFECTS_SKY_PATH], skypath, sizeof(skypath)); // Set new sky on all clients. - SetConVarString(g_hSkyname, skypath, true); + SetConVarString(hSkyname, skypath, true); } VEffectsApplySunDisable(bool:disable = false) diff --git a/src/zr/weapons/restrict.inc b/src/zr/weapons/restrict.inc index 79369f3..1172232 100644 --- a/src/zr/weapons/restrict.inc +++ b/src/zr/weapons/restrict.inc @@ -35,7 +35,7 @@ enum WpnRestrictQuery Successful_Group, /** Group (un)restrict query was successful. */ Failed_Weapon, /** Weapon (un)restrict was unsuccessful */ Failed_Group, /** Group (un)restrict was unsuccessful */ - Invalid, /** Weapon/Group invalid */ + WeaponInvalid, /** Weapon/Group invalid */ } /** @@ -57,7 +57,7 @@ RestrictInit() */ RestrictOnCommandsCreate() { - // Create admin commands. + // Create weapon admin commands. RegAdminCmd("zr_restrict", RestrictRestrictCommand, ADMFLAG_GENERIC, "zr_restrict - Restrict a weapon."); RegAdminCmd("zr_unrestrict", RestrictUnrestrictCommand, ADMFLAG_GENERIC, "zr_unrestrict - Unrestrict a weapon."); } @@ -82,7 +82,7 @@ RestrictClearData() /** * Loads weapon data from file. */ -RestrictOnMapStart() +RestrictLoad() { // Clear weapon restrict data. RestrictClearData(); @@ -99,7 +99,7 @@ RestrictOnMapStart() // Get weapon groups config path. decl String:pathweapongroups[PLATFORM_MAX_PATH]; - new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_WEAPONGROUPS, pathweapongroups); + new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_WEAPONGROUPS, pathweapongroups); // If file doesn't exist, then log and stop. if (!exists) @@ -221,7 +221,6 @@ RestrictClientInit(client) RestrictOnClientDisconnect(client) { // Unhook "Weapon_CanUse" callback, and reset variable. - if (g_iCanUseHookID[client] != -1) { ZRTools_UnhookWeapon_CanUse(g_iCanUseHookID[client]); @@ -287,7 +286,7 @@ public Action:RestrictBuyCommand(client, argc) * Successful_Group: The call successfully restricted a weapon group. * Failed_Weapon: The call failed to restrict a weapon. * Failed_Group: The call failed to restrict a weapon group. - * Invalid: The call was unsuccessful due to invalid weapon. + * WeaponInvalid: The call was unsuccessful due to invalid weapon. */ WpnRestrictQuery:RestrictRestrict(const String:weapon[], String:display[] = "") { @@ -340,7 +339,7 @@ WpnRestrictQuery:RestrictRestrict(const String:weapon[], String:display[] = "") strcopy(display, WEAPONS_MAX_LENGTH, weapon); // Weapon name was invalid. - return Invalid; + return WeaponInvalid; } // Get display name of the weapon. @@ -368,7 +367,7 @@ WpnRestrictQuery:RestrictRestrict(const String:weapon[], String:display[] = "") * Successful_Group: The call successfully restricted a weapon group. * Failed_Weapon: The call failed to restrict a weapon. * Failed_Group: The call failed to restrict a weapon group. - * Invalid: The call was unsuccessful due to invalid weapon. + * WeaponInvalid: The call was unsuccessful due to invalid weapon. */ WpnRestrictQuery:RestrictUnrestrict(const String:weapon[], String:display[] = "") { @@ -425,7 +424,7 @@ WpnRestrictQuery:RestrictUnrestrict(const String:weapon[], String:display[] = "" { strcopy(display, WEAPONS_MAX_LENGTH, weapon); - return Invalid; + return WeaponInvalid; } // Get display name of the weapon. @@ -502,7 +501,7 @@ RestrictPrintRestrictOutput(client, WpnRestrictQuery:output, const String:weapon } } // Weapon name was invalid. - case Invalid: + case WeaponInvalid: { if (reply) { @@ -571,7 +570,7 @@ RestrictPrintUnrestrictOutput(client, WpnRestrictQuery:output, const String:weap } } // Weapon name was invalid. - case Invalid: + case WeaponInvalid: { if (reply) { @@ -886,6 +885,10 @@ public ZRTools_Action:RestrictCanUse(client, weapon) return ZRTools_Continue; } +/** + * Command callbacks. + */ + /** * Command callback (zr_restrict) * Restricts a weapon or group @@ -895,6 +898,13 @@ public ZRTools_Action:RestrictCanUse(client, weapon) */ public Action:RestrictRestrictCommand(client, argc) { + // If not enough arguments given, then stop. + if (argc < 1) + { + TranslationReplyToCommand(client, "Weapons command restrict syntax"); + return Plugin_Handled; + } + // If weapons module is disabled, then stop. new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]); if (!weapons) @@ -937,6 +947,13 @@ public Action:RestrictRestrictCommand(client, argc) */ public Action:RestrictUnrestrictCommand(client, argc) { + // If not enough arguments given, then stop. + if (argc < 1) + { + TranslationReplyToCommand(client, "Weapons command unrestrict syntax"); + return Plugin_Handled; + } + // If weapons module is disabled, then stop. new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]); if (!weapons) diff --git a/src/zr/weapons/weaponalpha.inc b/src/zr/weapons/weaponalpha.inc index 339737c..ab3e1b0 100644 --- a/src/zr/weapons/weaponalpha.inc +++ b/src/zr/weapons/weaponalpha.inc @@ -18,7 +18,7 @@ /** * Array that stores the "HookID" to be later unhooked on player disconnect. */ -new g_iWeaponDropHookID[MAXPLAYERS + 1]; +new g_iWeaponDropHookID[MAXPLAYERS + 1] = {-1, ...}; /** * Client is joining the server. @@ -32,14 +32,18 @@ WeaponAlphaClientInit(client) } /** - * Unhook Weapon_CanUse function on a client. + * Client is leaving the server. * * @param client The client index. */ WeaponAlphaOnClientDisconnect(client) { - // Unhook "Weapon_Drop" on client. - ZRTools_UnhookWeapon_Drop(g_iWeaponDropHookID[client]); + // Unhook "Weapon_Drop" callback, and reset variable. + if (g_iWeaponDropHookID[client] != -1) + { + ZRTools_UnhookWeapon_Drop(g_iWeaponDropHookID[client]); + g_iWeaponDropHookID[client] = -1; + } } /** diff --git a/src/zr/weapons/weapons.inc b/src/zr/weapons/weapons.inc index f23aa0f..58fe732 100644 --- a/src/zr/weapons/weapons.inc +++ b/src/zr/weapons/weapons.inc @@ -56,6 +56,15 @@ WeaponsInit() RestrictInit(); } +/** + * Create commands related to weapons here. + */ +WeaponsOnCommandsCreate() +{ + // Forward event to sub-modules. + RestrictOnCommandsCreate(); +} + /** * Clears weapon data. */ @@ -78,6 +87,13 @@ WeaponsLoad() // Clear weapon data. WeaponsClearData(); + // Get weapons config path. + decl String:pathweapons[PLATFORM_MAX_PATH]; + new bool:exists = ConfigGetCvarFilePath(CVAR_CONFIG_PATH_WEAPONS, pathweapons); + + // Register config info. + ConfigRegisterConfig(ConfigWeapons, false, GetFunctionByName(GetMyHandle(), "WeaponsOnConfigReload"), _, pathweapons, CONFIG_FILE_ALIAS_WEAPONS); + // If module is disabled, then stop. new bool:weapons = GetConVarBool(g_hCvarsList[CVAR_WEAPONS]); if (!weapons) @@ -85,10 +101,6 @@ WeaponsLoad() return; } - // Get weapons config path. - decl String:pathweapons[PLATFORM_MAX_PATH]; - new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_WEAPONS, pathweapons); - // If file doesn't exist, then log and stop. if (!exists) { @@ -104,8 +116,26 @@ WeaponsLoad() // Validate weapons config. WeaponsValidateConfig(); + // Set config data. + ConfigSetConfigLoaded(ConfigWeapons, true); + ConfigSetConfigHandle(ConfigWeapons, kvWeapons); + // Forward event to sub-module. - RestrictOnMapStart(); + RestrictLoad(); +} + +/** + * Called when configs are being reloaded. + * + * @param config The config being reloaded. (only if 'all' is false) + */ +public WeaponsOnConfigReload(ConfigFile:config) +{ + // Reload weapons config. + if (config == ConfigWeapons) + { + WeaponsLoad(); + } } /** @@ -120,12 +150,6 @@ WeaponsValidateConfig() } } -WeaponsOnCommandsCreate() -{ - // Forward event to sub-modules. - RestrictOnCommandsCreate(); -} - /** * Client is joining the server. * @@ -156,7 +180,7 @@ WeaponsOnClientDisconnect(client) * on it when finished! * @return The size of the array. */ -WeaponsCreateWeaponArray(&Handle:arrayWeapons, maxlen = WEAPONS_MAX_LENGTH) +stock WeaponsCreateWeaponArray(&Handle:arrayWeapons, maxlen = WEAPONS_MAX_LENGTH) { // Initialize array handle. arrayWeapons = CreateArray(maxlen); @@ -189,7 +213,7 @@ WeaponsCreateWeaponArray(&Handle:arrayWeapons, maxlen = WEAPONS_MAX_LENGTH) * @param weapon The weapon name. * @return Returns true if valid, false it not. */ -bool:WeaponsIsValidWeapon(const String:weapon[]) +stock bool:WeaponsIsValidWeapon(const String:weapon[]) { // Reset keyvalue's traversal stack. KvRewind(kvWeapons); @@ -219,7 +243,7 @@ bool:WeaponsIsValidWeapon(const String:weapon[]) * @param weapon The weapon name. * @param display Returns with the display name, is not changed if weapon is invalid. */ -WeaponsGetDisplayName(const String:weapon[], String:display[]) +stock WeaponsGetDisplayName(const String:weapon[], String:display[]) { // Reset keyvalue's traversal stack. KvRewind(kvWeapons); @@ -246,7 +270,7 @@ WeaponsGetDisplayName(const String:weapon[], String:display[]) * @param weapon The weapon name. * @return Returns true if restricted, false it not. */ -bool:WeaponsIsWeaponMenu(const String:weapon[]) +stock bool:WeaponsIsWeaponMenu(const String:weapon[]) { // Reset keyvalue's traversal stack. KvRewind(kvWeapons); @@ -278,7 +302,7 @@ bool:WeaponsIsWeaponMenu(const String:weapon[]) * @param weapon The weapon name. * @return The float value of the knockback multiplier, 1.0 if not found. */ -Float:WeaponGetWeaponKnockback(const String:weapon[]) +stock Float:WeaponGetWeaponKnockback(const String:weapon[]) { // Reset keyvalue's traversal stack. KvRewind(kvWeapons); @@ -312,7 +336,7 @@ Float:WeaponGetWeaponKnockback(const String:weapon[]) * @param weapons The weapon index array. * -1 if no weapon in slot. */ -WeaponsGetClientWeapons(client, weapons[WeaponsType]) +stock WeaponsGetClientWeapons(client, weapons[WeaponsType]) { // x = weapon slot. for (new x = 0; x < WEAPONS_SLOTS_MAX; x++) @@ -328,7 +352,7 @@ WeaponsGetClientWeapons(client, weapons[WeaponsType]) * @return The weapon index of the deployed weapon. * -1 if no weapon is deployed. */ -WeaponsGetDeployedWeaponIndex(client) +stock WeaponsGetDeployedWeaponIndex(client) { // Return the client's active weapon. return GetEntDataEnt2(client, offsActiveWeapon); @@ -340,7 +364,7 @@ WeaponsGetDeployedWeaponIndex(client) * @param client The client index. * @return The slot number of deployed weapon. */ -WeaponsType:WeaponsGetDeployedWeaponSlot(client) +stock WeaponsType:WeaponsGetDeployedWeaponSlot(client) { // Get all client's weapon indexes. new weapons[WeaponsType]; @@ -373,7 +397,7 @@ WeaponsType:WeaponsGetDeployedWeaponSlot(client) * @param client The client index. * @param weapon The weapon index to force client to drop. */ -WeaponsForceClientDrop(client, weapon) +stock WeaponsForceClientDrop(client, weapon) { // Force client to drop weapon. SDKCall(g_hToolsCSWeaponDrop, client, weapon, true, false);