From 0404230fc814f730bd2bc2972240a599dec7137e Mon Sep 17 00:00:00 2001 From: Greyscale Date: Fri, 1 May 2009 07:09:18 +0200 Subject: [PATCH] Fixed humans winning on round end when no humans present, removed classes file cvar and made it use the config module, replaced SetFailState with LogMessageFormatted with fail flag, made the base config manipulator function. --- src/zr/config.inc | 331 +++++++++++++++++++++++++ src/zr/cvars.inc | 3 - src/zr/hitgroups.inc | 7 +- src/zr/models.inc | 3 +- src/zr/playerclasses/playerclasses.inc | 37 ++- src/zr/roundend.inc | 2 +- src/zr/weapons/menu_weapons.inc | 2 +- src/zr/weapons/restrict.inc | 7 +- src/zr/weapons/weapons.inc | 21 +- src/zr/zombiereloaded.inc | 39 --- 10 files changed, 378 insertions(+), 74 deletions(-) create mode 100644 src/zr/config.inc 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. *