diff --git a/src/zr/config.inc b/src/zr/config.inc new file mode 100644 index 0000000..10d3b44 --- /dev/null +++ b/src/zr/config.inc @@ -0,0 +1,331 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: config.inc + * Description: Config API and executing. + * + * ============================================================================ + */ + +/** + * @section List of config files under this modules control. + */ +#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 +/** + * @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 + */ + +/** + * The max length of a config/value string. + */ +#define CONFIG_OPTION_MAX_LENGTH 32 + +enum ConfigKeyvalueAction +{ + Create, /** Create a key. */ + Delete, /** Delete a key. */ + Set, /** Modify setting of a key. */ + Get, /** Get setting of a key. */ +} + +/** + * @section Global data handle initializations. + */ +new Handle:arrayModelsList = INVALID_HANDLE; +new Handle:kvClassData = INVALID_HANDLE; +new Handle:kvWeapons = INVALID_HANDLE; +new Handle:kvWeaponGroups = INVALID_HANDLE; +new Handle:kvHitgroups = INVALID_HANDLE; +/** + * Load plugin configs. + */ +ConfigLoad() +{ + decl String:mapconfig[PLATFORM_MAX_PATH]; + + // Get map name and format into config path. + GetCurrentMap(mapconfig, sizeof(mapconfig)); + Format(mapconfig, sizeof(mapconfig), "sourcemod/zombiereloaded/%s.cfg", mapconfig); + + // Prepend cfg to path. + decl String:path[PLATFORM_MAX_PATH]; + Format(path, sizeof(path), "cfg/%s", mapconfig); + + // File doesn't exist, then stop. + if (!FileExists(path)) + { + return; + } + + // Execute config file. + ServerCommand("exec %s", mapconfig); + + // Log action. + if (LogCheckFlag(LOG_CORE_EVENTS)) + { + LogMessageFormatted(-1, "", "", "Executed map config file: %s.", LOG_FORMAT_TYPE_SIMPLE, mapconfig); + } +} + +/** + * 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[]) +{ + // Get cvar's path. + decl String:filepath[PLATFORM_MAX_PATH]; + GetConVarString(g_hCvarsList[cvar], filepath, sizeof(filepath)); + + // Build full path in return string. + BuildPath(Path_SM, path, PLATFORM_MAX_PATH, filepath); + + return FileExists(path); +} + +/** + * 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, + * using this function everywhere would be EXTREMELY inefficient. + * + * @param config Config index of config to modify. (see CONFIG_FILE_* defines) + * @param action Action to perform on keyvalue tree. (see enum ConfigKeyvalueAction) + * @param keys Array containing keys to traverse into. + * @param keysMax The size of the 'keys' array. + * @param setting (Optional) The name of the setting to modify. + * @param value (Optional) The new value to set. + * @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) +{ + // Retrieve handle of the keyvalue tree. + new Handle:hConfig = ConfigGetFileHandle(config); + + // If handle is invalid, then stop. + if (hConfig == INVALID_HANDLE) + { + return false; + } + + // Rewind keyvalue tree. + KvRewind(hConfig); + + // x = keys index. + // Traverse into the keygroup, stop if it fails. + for (new x = 0; x < keysMax; x++) + { + // If key is empty, then break the loop. + if (!keys[x][0]) + { + break; + } + + // Try to jump to next level in the transversal stack, create key if specified. + new bool:exists = KvJumpToKey(hConfig, keys[x], (action == Create)); + + // If exists is false, then stop. + if (!exists) + { + // Key doesn't exist. + return false; + } + } + + switch(action) + { + case Create: + { + if (!setting[0] || !value[0]) + { + // We created the key already, so return true. + return true; + } + + // Set new value. + KvSetString(hConfig, setting, value); + } + case Delete: + { + // Return deletion result. + return KvDeleteKey(hConfig, setting); + } + case Set: + { + // Set new value. + KvSetString(hConfig, setting, value); + } + case Get: + { + // Get current value. + KvGetString(hConfig, setting, value, maxlen); + } + } + + // We successfully set or got the value. + return true; +} + +/** + * Return handle to array or keygroup for globally stored data. + * + * @param configindex Index of the config. (see CONFIG_FILE_* defines) + */ +Handle:ConfigGetFileHandle(config) +{ + switch(config) + { + 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; + } + } + + // Invalid config index. + return INVALID_HANDLE; +} + +/** + * Iterate through a file and store each line in an array. + * + * @param path Path to the file to iterate through. + * @return The handle of the array, don't forget to call CloseHandle + * on it when finished! + */ +Handle:ConfigLinesToArray(const String:path[]) +{ + new Handle:arrayLines = CreateArray(PLATFORM_MAX_PATH); + decl String:line[PLATFORM_MAX_PATH]; + + // Open file. + new Handle:hFile = OpenFile(path, "r"); + + // If file couldn't be opened, then stop. + if (hFile == INVALID_HANDLE) + { + return INVALID_HANDLE; + } + + while(!IsEndOfFile(hFile)) + { + // Get current line text. + ReadFileLine(hFile, line, sizeof(line)); + + // If line contains a ";", then stop. + if (StrContains(line, ";") > -1) + { + continue; + } + + // Cut out comments at the end of a line. + if (StrContains(line, "//") > -1) + { + SplitString(line, "//", line, sizeof(line)); + } + + // Trim off whitespace. + TrimString(line); + + // If line is empty, then stop. + if (!line[0]) + { + continue; + } + + // Push line into array. + PushArrayString(arrayLines, line); + } + + // Close file handle. + CloseHandle(hFile); + + // Return array handle. + return arrayLines; +} + +/** + * Converts string of "yes" or "no" to a boolean value. + * + * @param option "yes" or "no" string to be converted. + * @return True if string is "yes", false otherwise. + */ +bool:ConfigSettingToBool(const String:option[]) +{ + // If option is equal to "yes," then return true. + if (StrEqual(option, "yes", false)) + { + return true; + } + + // Option isn't "yes." + return false; +} + +/** + * Converts boolean value to "yes" or "no". + * + * @param bOption True/false value to be converted to "yes"/"no", respectively. + * @param option Variable to store "yes" or "no" in. + * @param maxlen Max length of return string, (can't be more than 4) + */ +ConfigBoolToSetting(bool:bOption, String:option[], maxlen) +{ + // If option is true, then copy "yes" to return string. + if (bOption) + { + strcopy(option, maxlen, "yes"); + } + // If option is false, then copy "no" to return string. + else + { + strcopy(option, maxlen, "no"); + } +} \ No newline at end of file diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index 19d3589..c7a61d2 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -35,7 +35,6 @@ enum CvarsList Handle:CVAR_CLASSES_DEFAULT_ZOMBIE, Handle:CVAR_CLASSES_DEFAULT_HUMAN, Handle:CVAR_CLASSES_DEFAULT_ADMIN, - Handle:CVAR_CLASSES_FILE, Handle:CVAR_WEAPONS, Handle:CVAR_WEAPONS_RESTRICT, Handle:CVAR_WEAPONS_ZMARKET_BUYZONE, @@ -222,8 +221,6 @@ CvarsCreate() // Old Desc: g_hCvarsList[CVAR_CLASSES_DEFAULT_ADMIN] = CreateConVar("zr_classes_default_admin", "random", ""); // Old Desc: Default admin-only class selected for admins when they connect. Use \"random\" to select a random class, or blank to use class config defaults. - g_hCvarsList[CVAR_CLASSES_FILE] = CreateConVar("zr_classes_file", "configs/zr/playerclasses.txt", ""); - // Old Desc: Class data file to read from, in Valves key/values format. The path is relative to the \"sourcemod\" folder. // =========================== // Weapons (core) diff --git a/src/zr/hitgroups.inc b/src/zr/hitgroups.inc index 18ae3d4..7f28552 100644 --- a/src/zr/hitgroups.inc +++ b/src/zr/hitgroups.inc @@ -10,9 +10,10 @@ */ /** - * Array to store keyvalue data. + * Keyvalue handle to store hitgroups data. + * + * @redir config.inc */ -new Handle:kvHitgroups = INVALID_HANDLE; /** * @section Player hitgroup values. @@ -155,7 +156,7 @@ bool:HitgroupsCanDamageHitgroup(hitgroup) KvGetString(kvHitgroups, "damage", damage, sizeof(damage), "yes"); // Return hitgroup's damage setting. - return ZRConfigSettingToBool(damage); + return ConfigSettingToBool(damage); } } while (KvGotoNextKey(kvHitgroups)); } diff --git a/src/zr/models.inc b/src/zr/models.inc index 268f76d..9d88ff9 100644 --- a/src/zr/models.inc +++ b/src/zr/models.inc @@ -21,8 +21,9 @@ /** * Array that stores a list of validated models. + * + * @redir config.inc */ -new Handle:arrayModelsList = INVALID_HANDLE; ModelsLoad() { diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index d7d3565..f46785b 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -233,7 +233,11 @@ enum ClassAttributes Float:class_jump_distance } -new Handle:kvClassData; +/** + * Keyvalue handle to store class data. + * + * @redir config.inc + */ /** * The original class data. This array only changed when class data is loaded. @@ -302,23 +306,30 @@ ClassLoad() } kvClassData = CreateKeyValues("classes"); - decl String:classfile[PLATFORM_MAX_PATH]; - GetConVarString(g_hCvarsList[CVAR_CLASSES_FILE], classfile, sizeof(classfile)); + // Get weapons config path. + decl String:pathclasses[PLATFORM_MAX_PATH]; + new bool:exists = ConfigGetFilePath(CVAR_CONFIG_PATH_PLAYERCLASSES, pathclasses); - // Try to load the class configuration file. - decl String:path[PLATFORM_MAX_PATH]; - BuildPath(Path_SM, path, sizeof(path), classfile); - - if (!FileToKeyValues(kvClassData, path)) + // If file doesn't exist, then log and stop. + if (!exists) { - SetFailState("Could not load class data file (\"%s\"). Check path in zr_classes_file in the configuration file.", path); + // Log failure. + if (LogCheckFlag(LOG_CORE_EVENTS, LOG_MODULE_WEAPONS)) + { + LogMessageFormatted(-1, "Classes", "Config Validation", "Missing playerclasses config file: %s", LOG_FORMAT_TYPE_FATALERROR, pathclasses); + } + + return; } + // Put file data into memory. + FileToKeyValues(kvClassData, pathclasses); + // Try to find the first class. KvRewind(kvClassData); if (!KvGotoFirstSubKey(kvClassData)) { - SetFailState("Cannot find any classes in \"%s\".", path); + LogMessageFormatted(-1, "Classes", "Config Validation", "Can't find any classes in %s", LOG_FORMAT_TYPE_FATALERROR, pathclasses); } decl String:name[64]; @@ -401,7 +412,7 @@ ClassLoad() ClassData[ClassCount][class_enabled] = false; if (LogCheckFlag(LOG_CORE_EVENTS, LOG_MODULE_CLASSES)) { - LogMessageFormatted(-1, "Classes", "Load", "Warning: Invalid class at index %d, disabled class. Class error flags: %d.", LOG_FORMAT_TYPE_ERROR, ClassCount, ClassErrorFlags); + LogMessageFormatted(-1, "Classes", "Config Validation", "Warning: Invalid class at index %d, disabled class. Class error flags: %d.", LOG_FORMAT_TYPE_ERROR, ClassCount, ClassErrorFlags); } } @@ -412,13 +423,13 @@ ClassLoad() // Validate team requirements. if (!ClassValidateTeamRequirements()) { - SetFailState("The class configuration doesn't match the team requirements."); + LogMessageFormatted(-1, "Classes", "Config Validation", "The class configuration doesn't match the team requirements.", LOG_FORMAT_TYPE_FATALERROR); } // Validate team default requirements. if (!ClassValidateTeamDefaults()) { - SetFailState("Couldn't find a default class for one or more teams. At least one class per team must be marked as default."); + LogMessageFormatted(-1, "Classes", "Config Validation", "Couldn't find a default class for one or more teams. At least one class per team must be marked as default.", LOG_FORMAT_TYPE_FATALERROR); } // Cache class data. diff --git a/src/zr/roundend.inc b/src/zr/roundend.inc index a1233f4..47f8de7 100644 --- a/src/zr/roundend.inc +++ b/src/zr/roundend.inc @@ -252,7 +252,7 @@ RoundEndOutcome:RoundEndReasonToOutcome(reason) public Action:RoundEndTimer(Handle:timer) { // If there aren't clients on both teams, then stop. - if (ZRTeamHasClients()) + if (!ZRTeamHasClients()) { return; } diff --git a/src/zr/weapons/menu_weapons.inc b/src/zr/weapons/menu_weapons.inc index 29753df..9826322 100644 --- a/src/zr/weapons/menu_weapons.inc +++ b/src/zr/weapons/menu_weapons.inc @@ -413,7 +413,7 @@ WeaponsMenuMarket(client) decl String:togglebuyzone[64]; decl String:curSetting[8]; - ZRBoolToConfigSetting(GetConVarBool(g_hCvarsList[CVAR_WEAPONS_ZMARKET_BUYZONE]), curSetting, sizeof(curSetting)); + ConfigBoolToSetting(GetConVarBool(g_hCvarsList[CVAR_WEAPONS_ZMARKET_BUYZONE]), curSetting, sizeof(curSetting)); Format(togglebuyzone, sizeof(togglebuyzone), "%t", "Weapons menu market toggle buyzone", curSetting); diff --git a/src/zr/weapons/restrict.inc b/src/zr/weapons/restrict.inc index 29c5c01..b3cac08 100644 --- a/src/zr/weapons/restrict.inc +++ b/src/zr/weapons/restrict.inc @@ -12,9 +12,10 @@ new Handle:gRestrictedWeapons = INVALID_HANDLE; /** - * Array to store keyvalue data. + * Keyvalue handle to store weapon groups data. + * + * @redir config.inc */ -new Handle:kvWeaponGroups = INVALID_HANDLE; /** * Array that stores the "HookID" to be later unhooked on player disconnect. @@ -125,7 +126,7 @@ RestrictDefaultRestrictions() decl String:restrict[8]; KvGetString(kvWeapons, "restrict", restrict, sizeof(restrict), "no"); - if (ZRConfigSettingToBool(restrict)) + if (ConfigSettingToBool(restrict)) { new WpnRestrictQuery:output = RestrictRestrict(weapon, display); RestrictPrintRestrictOutput(0, output, display, true); diff --git a/src/zr/weapons/weapons.inc b/src/zr/weapons/weapons.inc index 676da91..bb479fd 100644 --- a/src/zr/weapons/weapons.inc +++ b/src/zr/weapons/weapons.inc @@ -27,18 +27,19 @@ */ enum WeaponsType { - Type_Invalid = -1, - Type_Primary = 0, - Type_Secondary = 1, - Type_Melee = 2, - Type_Projectile = 3, - Type_Explosive = 4, + Type_Invalid = -1, /** Invalid weapon (slot). */ + Type_Primary = 0, /** Primary weapon slot. */ + Type_Secondary = 1, /** Secondary weapon slot. */ + Type_Melee = 2, /** Melee (knife) weapon slot. */ + Type_Projectile = 3, /** Projectile (grenades, flashbangs, etc) weapon slot. */ + Type_Explosive = 4, /** Explosive (c4) weapon slot. */ } - + /** - * Array to store keyvalue data. + * Keyvalue handle to store weapon data. + * + * @redir config.inc */ -new Handle:kvWeapons = INVALID_HANDLE; #include "zr/weapons/restrict" #include "zr/weapons/markethandler" @@ -263,7 +264,7 @@ bool:WeaponsIsWeaponMenu(const String:weapon[]) KvGetString(kvWeapons, "menu", menu, sizeof(menu), "yes"); // Return weapon's setting. - return ZRConfigSettingToBool(menu); + return ConfigSettingToBool(menu); } } while (KvGotoNextKey(kvWeapons)); } diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc index a6aa2ee..6c81ece 100644 --- a/src/zr/zombiereloaded.inc +++ b/src/zr/zombiereloaded.inc @@ -24,45 +24,6 @@ new dxLevel[MAXPLAYERS + 1]; */ new bool:g_bZombieSpawned; -/** - * Converts string of "yes" or "no" to a boolean value. - * - * @param option "yes" or "no" string to be converted. - * @return True if string is "yes", false otherwise. - */ -bool:ZRConfigSettingToBool(const String:option[]) -{ - // If option is equal to "yes," then return true. - if (StrEqual(option, "yes", false)) - { - return true; - } - - // Option isn't "yes." - return false; -} - -/** - * Converts boolean value to "yes" or "no". - * - * @param bOption True/false value to be converted to "yes"/"no", respectively. - * @param option Variable to store "yes" or "no" in. - * @param maxlen Max length of return string, (can't be more than 4) - */ -ZRBoolToConfigSetting(bool:bOption, String:option[], maxlen) -{ - // If option is true, then copy "yes" to return string. - if (bOption) - { - strcopy(option, maxlen, "yes"); - } - // If option is false, then copy "no" to return string. - else - { - strcopy(option, maxlen, "no"); - } -} - /** * Create an array populated with eligible clients to be zombie. *