Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #define PLUGIN_VERSION "1.1"
- /*=======================================================================================
- Plugin Info:
- * Name : [ANY] ConVars Anomaly Fixer
- * Author : Alex Dragokas
- * Descr. : Check in-game ConVars that differ from the cfg-files values (due to the Valve bug) and fix them before map start
- * Link : https://forums.alliedmods.net/showthread.php?p=2593843
- ========================================================================================
- Change Log:
- 1.1 (27-May-2018)
- - Improved log format for fix/error msg a little bit.
- - Added "convar_cvar_check_areas" ConVar to choose the area you need to check for (1 - difference in values, 2 - nonexistent convars, 3 - overflows, 4 - All {by default}).
- - Little checks, like cfg file presence.
- 1.0 (25-May-2018)
- - Initial release
- ========================================================================================
- Description:
- Cfg Console Variables anomaly happens time by time, and caused by Valve bug:
- values can not be read from cfg-files and remains by default if you reach some limit
- of the number of lines in server.cfg / or cfg-files in total.
- Also, this plugin check:
- - if some cfg files contain wrong values that exceed max/min value allowed.
- - unused ConVars.
- Commands:
- 1. "sm_convar_anomaly_show" - to compare values in cfg files with actual in-game Cvar to find anomalies/unused cvars.
- 2. "sm_convar_anomaly_fix" - to attempt to fix Cvar anomalies.
- By default, log-file is located at: "addons/sourcemod/logs/CVar_Anomaly.log"
- Settings (ConVars):
- 1. "convar_anomaly_autofix" - fix Cvar anomaly automatically before each map start? (0 - off {default}, 1 - on).
- 2. "convar_anomaly_logpos" - where to write log? (1 - client conlose, 2 - server console, 4 - file, 1+2+4=7 - all places).
- 3. "convar_cfg_files_include" - if you need more cfg-files to process, place them here, separated by star (*)
- 4. "convar_cfg_files_exclude" - if you need exclude some cfg-files from processing, place them here, separated by star (*)
- 5. "convar_cvar_names_exclude" - if you need concrete ConVars to exclude from processing, place them here, separated by star (*)
- Warning: some plugins / game specificially change its ConVars in-game process, so the final report is not necessarily accurate.
- Using:
- To enable automatic fix, set "convar_anomaly_autofix" in cfg/sourcemod/sm_convar_anomaly.cfg file to 1.
- ======================================================================================*/
- #pragma semicolon 1
- #pragma newdecls required
- #include <sourcemod>
- #include <regex>
- #define MAX_CVAR_NAME_LENGTH 100
- #define MAX_CVAR_VALUE_LENGTH 400
- #define MAX_CVAR_ITEMS 50 // for parsing own .cfg
- #define CVAR_FLAGS FCVAR_NOTIFY
- ArrayList g_hArrayCvarList, g_hArrayCvarValues, g_hArrayCfg, g_hArrayCvarExclude, g_hArrayCfgExclude;
- ConVar g_hCvarAutoFix, g_hCvarLogPos, g_hCvarCfgsInclude, g_hCvarCfgsExclude, g_hCvarCVarExclude, g_hCvarAreas;
- int g_iLogPos, g_iCheckAreas;
- bool g_bAutoFix;
- File g_hLog;
- char g_sLogPath[PLATFORM_MAX_PATH] = "addons/sourcemod/logs/CVar_Anomaly.log";
- /* ====================================================================================================
- PLUGIN INFO / START / END
- ====================================================================================================*/
- public Plugin myinfo =
- {
- name = "[ANY] ConVars Anomaly Fixer",
- author = "Dragokas",
- description = "Check in-game ConVars that differ from the cfg-files values (due to the Valve bug) and fix them before each map start",
- version = PLUGIN_VERSION,
- url = "https://forums.alliedmods.net/showthread.php?p=2593843"
- }
- public void OnPluginStart()
- {
- // Commands
- //
- RegAdminCmd("sm_convar_anomaly_show", CmdConfigsCompare2, ADMFLAG_ROOT, "Compares cfg files values with actual Cvar values to find anomalies and print difference/missing/overflow Cvars.");
- RegAdminCmd("sm_convar_anomaly_fix", CmdConfigsFix, ADMFLAG_ROOT, "Attempting to overwrite anomalous ConVars by cfg files values.");
- // CVars
- //
- g_hCvarAutoFix = CreateConVar( "convar_anomaly_autofix", "0", "Fix Cvar anomaly automatically before each map start? (0 - off, 1 - on)", CVAR_FLAGS );
- g_hCvarLogPos = CreateConVar( "convar_anomaly_logpos", "7", "Where to write log? (1 - client conlose, 2 - server console, 4 - file, 1+2+4=7 - all places)", CVAR_FLAGS );
- g_hCvarCfgsInclude = CreateConVar( "convar_cfg_files_include", "cfg/server.cfg*cfg/autoexec.cfg", "List of additional cfg-files to process, separated by star (*)", CVAR_FLAGS );
- g_hCvarCfgsExclude = CreateConVar( "convar_cfg_files_exclude", "", "List of cfg-files to exclude from processing, separated by star (*)", CVAR_FLAGS );
- g_hCvarCVarExclude = CreateConVar( "convar_cvar_names_exclude", "sv_tags*sm*exec*setmaster", "List of ConVar names (or service commands from server.cfg) to exclude from processing, separated by star (*)", CVAR_FLAGS );
- g_hCvarAreas = CreateConVar( "convar_cvar_check_areas", "4", "Areas to check for (1 - difference in values, 2 - nonexistent convars, 3 - overflows, 4 - All).", CVAR_FLAGS );
- CreateConVar( "convar_anomaly_version", PLUGIN_VERSION, "ConVars Anomaly Fixer plugin version", FCVAR_DONTRECORD);
- // Init ArrayLists
- //
- g_hArrayCfg = new ArrayList(PLATFORM_MAX_PATH);
- g_hArrayCfgExclude = new ArrayList(PLATFORM_MAX_PATH);
- g_hArrayCvarExclude = new ArrayList(MAX_CVAR_NAME_LENGTH);
- g_hArrayCvarList = new ArrayList(MAX_CVAR_NAME_LENGTH);
- g_hArrayCvarValues = new ArrayList(MAX_CVAR_VALUE_LENGTH);
- AutoExecConfigSmart(true, "sm_convar_anomaly");
- HookConVarChange(g_hCvarAutoFix, ConVarChanged);
- HookConVarChange(g_hCvarLogPos, ConVarChanged);
- HookConVarChange(g_hCvarCfgsInclude, ConVarChanged);
- HookConVarChange(g_hCvarCfgsExclude, ConVarChanged);
- HookConVarChange(g_hCvarCVarExclude, ConVarChanged);
- HookConVarChange(g_hCvarAreas, ConVarChanged);
- GetCvars();
- }
- // Smart version of AutoExecConfig() that not depends on bugged valve functions
- //
- void AutoExecConfigSmart(bool autoCreate, const char[] name, const char[] folder = ".")
- {
- char sFile[PLATFORM_MAX_PATH];
- Format(sFile, sizeof(sFile), "cfg/sourcemod/%s/%s.cfg", folder, name);
- if (FileExists(sFile))
- CheckCfgAnomaly(0, sFile, true);
- else {
- if (StrEqual(folder, "."))
- AutoExecConfig(autoCreate, name);
- else
- AutoExecConfig(autoCreate, name, folder);
- }
- }
- public void ConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
- {
- GetCvars();
- }
- void GetCvars()
- {
- g_iLogPos = g_hCvarLogPos.IntValue;
- g_bAutoFix = g_hCvarAutoFix.BoolValue;
- g_iCheckAreas = g_hCvarAreas.IntValue;
- if (g_iCheckAreas == 0) // old version compatibility
- g_iCheckAreas = 4;
- SplitCvarToArrayList(g_hCvarCfgsInclude, "*", g_hArrayCfg);
- SplitCvarToArrayList(g_hCvarCfgsExclude, "*", g_hArrayCfgExclude);
- SplitCvarToArrayList(g_hCvarCVarExclude, "*", g_hArrayCvarExclude);
- }
- void SplitCvarToArrayList(ConVar cvar, char[] delim, ArrayList al)
- {
- char aItem[MAX_CVAR_ITEMS][PLATFORM_MAX_PATH];
- char sValue[MAX_CVAR_VALUE_LENGTH];
- int iCount;
- al.Clear();
- cvar.GetString(sValue, sizeof(sValue));
- if (strlen(sValue) != 0) {
- iCount = ExplodeString(sValue, delim, aItem, MAX_CVAR_ITEMS, PLATFORM_MAX_PATH);
- for (int i = 0; i < iCount; i++) {
- al.PushString(aItem[i]);
- }
- }
- }
- // apply ConVar fix before OnMapStart() forward, so another plugins will be able to precache objects with correct (fixed) names.
- //
- public void OnAutoConfigsBuffered()
- {
- if (g_bAutoFix)
- CompareConfigs2(0, true);
- }
- public Action CmdConfigsCompare2(int client, int args)
- {
- CompareConfigs2(client, false);
- return Plugin_Handled;
- }
- public Action CmdConfigsFix(int client, int args)
- {
- CompareConfigs2(client, true);
- return Plugin_Handled;
- }
- // Check anomaly in all cfg-files and optionally make in-game fix
- //
- bool CompareConfigs2(int client, bool doFix)
- {
- char sLine[100];
- OpenLog("a");
- FormatTime(sLine, sizeof(sLine), "%F, %X", GetTime());
- Format(sLine, sizeof(sLine), "\n\n\n%s - %s\n", sLine, doFix ? "FIX" : "show");
- StringToLog(sLine);
- char sDir[PLATFORM_MAX_PATH];
- strcopy(sDir, sizeof(sDir), "cfg/sourcemod/");
- DirectoryListing hDir = OpenDirectory(sDir);
- if( hDir == null )
- {
- PrintConsoles(client, "Could not open the directory \"cfg/sourcemod\".");
- return false;
- }
- char sFile[PLATFORM_MAX_PATH];
- FileType filetype;
- int pos, iFiles, iCvars = 0;
- while( hDir.GetNext(sFile, sizeof(sFile), filetype) )
- {
- if( filetype == FileType_File )
- {
- pos = FindCharInString(sFile, '.', true);
- if( pos != -1 && strcmp(sFile[pos], ".cfg", false) == 0 )
- {
- Format(sFile, sizeof(sFile), "cfg/sourcemod/%s", sFile);
- if (g_hArrayCfgExclude.FindString(sFile) == -1) {
- iFiles++;
- iCvars += CheckCfgAnomaly(client, sFile, doFix);
- }
- }
- }
- }
- // process additional cfgs
- for (int i = 0; i < g_hArrayCfg.Length; i++) {
- iFiles++;
- g_hArrayCfg.GetString(i, sFile, sizeof(sFile));
- iCvars += CheckCfgAnomaly(client, sFile, doFix);
- }
- PrintConsoles(client, "Total Files: %i (ConVars: %i)", iFiles, iCvars);
- g_hArrayCvarList.Clear();
- g_hArrayCvarValues.Clear();
- delete hDir;
- CloseLog();
- return true;
- }
- // Check anomaly by specified file and optionally make in-game fix
- //
- int CheckCfgAnomaly(int client, const char sFile[PLATFORM_MAX_PATH], bool doFix)
- {
- int iCount, iTotal;
- char sCvarName[MAX_CVAR_NAME_LENGTH];
- char sCfgValue[MAX_CVAR_VALUE_LENGTH];
- char sCvarValueOld[MAX_CVAR_VALUE_LENGTH];
- char sCvarValueCur[MAX_CVAR_VALUE_LENGTH];
- char sCvarValueDef[MAX_CVAR_VALUE_LENGTH];
- ConVar hCvar;
- float min, max, fCfgValue;
- bool hasMin, hasMax;
- g_hArrayCvarList.Clear();
- g_hArrayCvarValues.Clear();
- // parse cfg
- if (!ProcessConfigA(client, ".", sFile))
- return 0;
- // compare to actual
- for (int i = 0; i < g_hArrayCvarList.Length; i++) {
- iTotal++;
- g_hArrayCvarList.GetString(i, sCvarName, sizeof(sCvarName));
- if (g_hArrayCvarExclude.FindString(sCvarName) != -1)
- continue;
- if ((hCvar = FindConVar(sCvarName)) != null) {
- iCount++;
- g_hArrayCvarValues.GetString(i, sCfgValue, sizeof(sCfgValue));
- hCvar.GetString(sCvarValueCur, sizeof(sCvarValueCur));
- hCvar.GetDefault(sCvarValueDef, sizeof(sCvarValueDef));
- if (!doFix && (g_iCheckAreas == 4 || g_iCheckAreas == 3)) {
- // check for min/max value excess
- hasMin = hCvar.GetBounds(ConVarBound_Lower, min);
- hasMax = hCvar.GetBounds(ConVarBound_Upper, max);
- if (hasMin || hasMax) {
- if (!IsNumeric(sCfgValue)) {
- PrintConsoles(client, "Cfg value should be numeric. Cfg: %s, Cvar: %s, CfgValue: %s", sFile, sCvarName, sCfgValue);
- } else {
- fCfgValue = StringToFloat(sCfgValue);
- if (hasMin && fCfgValue < min)
- PrintConsoles(client, "Cfg value is too small. Cfg: %s, Cvar: %s, CfgValue: %s (min: %f)", sFile, sCvarName, sCfgValue, min);
- if (hasMax && fCfgValue > max)
- PrintConsoles(client, "Cfg value is too big. Cfg: %s, Cvar: %s, CfgValue: %s (max: %f)", sFile, sCvarName, sCfgValue, max);
- }
- }
- if (StrEqual(sCfgValue, "-2147483648")) {
- PrintConsoles(client, "Cfg value is too big. Cfg: %s, Cvar: %s, Value: %s, CfgValue: %s (max: 2147483647)", sFile, sCvarName, sCvarValueCur, sCfgValue);
- }
- }
- if ((g_iCheckAreas == 4 || g_iCheckAreas == 1) && !IsCvarValuesEqual(sCvarValueCur, sCfgValue)) {
- if (!doFix)
- PrintConsoles(client, "ConVar value is different. Cfg: %s, Cvar: %s, Value: %s (default: %s), should be: %s",
- sFile, sCvarName, sCvarValueCur, sCvarValueDef, sCfgValue);
- if (doFix) {
- strcopy(sCvarValueOld, sizeof(sCvarValueOld), sCvarValueCur);
- hCvar.SetString(sCfgValue, true, false);
- // check result again
- hCvar.GetString(sCvarValueCur, sizeof(sCvarValueCur));
- if (!StrEqual(sCvarValueCur, sCfgValue)) {
- PrintConsoles(client, "FAILURE. Unable to fix value of convar: %s (Value: %s, should be: %s)", sCvarName, sCvarValueCur, sCfgValue);
- } else {
- PrintConsoles(client, "Successfully changed convar: %s (Old value: %s, New value: %s)", sCvarName, sCvarValueOld, sCvarValueCur);
- }
- }
- }
- } else {
- if (!doFix && (g_iCheckAreas == 4 || g_iCheckAreas == 2) )
- PrintConsoles(client, "ConVar is not used: %s, cfg: %s", sCvarName, sFile);
- }
- }
- return iTotal;
- }
- // compare two string values by float rules
- //
- bool IsCvarValuesEqual(char[] sValue1, char[] sValue2)
- {
- bool err1, err2;
- float fValue1, fValue2;
- fValue1 = ParseFloat(sValue1, err1);
- fValue2 = ParseFloat(sValue2, err2);
- if (!err1 && !err2)
- if (AreFloatAlmostEqual(fValue1, fValue2))
- return (true);
- if (err1 ^ err2)
- return (false);
- return (StrEqual(sValue1, sValue2, false));
- }
- // compare two float numbers (thanks to @hmmmmm {alliedmods.net})
- //
- bool AreFloatAlmostEqual(float a, float b, float precision = 0.001)
- {
- return FloatAbs( a - b ) <= precision;
- }
- // convert string to float and return err == false on success.
- //
- float ParseFloat(char[] Str, bool &err)
- {
- if (IsNumeric(Str)) {
- err = false;
- return (StringToFloat(Str));
- } else {
- err = true;
- }
- return 0.0;
- }
- // parse cfg file and save Cvar names and values to ArrayLists: g_hArrayCvarList / g_hArrayCvarValues (thanks @SilverShot for initial parser work)
- //
- bool ProcessConfigA(int client, const char sFolder[PLATFORM_MAX_PATH], const char sFile[PLATFORM_MAX_PATH])
- {
- char sPath[PLATFORM_MAX_PATH];
- Format(sPath, sizeof(sPath), "%s/%s", sFolder, sFile);
- if (!FileExists(sPath))
- {
- PrintConsoles(client, "Can't process cfg file. Not exist: \"%s\".", sPath);
- return false;
- }
- File hFile = OpenFile(sPath, "r");
- if( hFile == null )
- {
- PrintConsoles(client, "Failed to open \"%s\".", sPath);
- return false;
- }
- char sValue[MAX_CVAR_NAME_LENGTH + MAX_CVAR_VALUE_LENGTH];
- char sLine[MAX_CVAR_NAME_LENGTH + MAX_CVAR_VALUE_LENGTH];
- int pos;
- while( !hFile.EndOfFile() && hFile.ReadLine(sLine, sizeof(sLine)) )
- {
- TrimString(sLine);
- if( sLine[0] != '\x0' && sLine[0] != '/' && sLine[1] != '/' )
- {
- if( strlen(sLine) > 5 )
- {
- pos = FindCharInString(sLine, ' ');
- if( pos != -1 )
- {
- strcopy(sValue, sizeof(sValue), sLine[pos + 1]);
- sLine[pos] = '\x0';
- if (StrEqual(sLine, "sm_cvar")) {
- strcopy(sLine, sizeof(sLine), sValue); // value => initial line
- pos = FindCharInString(sLine, ' '); // repeat same parsing
- if( pos == -1 ) {
- continue;
- } else {
- strcopy(sValue, sizeof(sValue), sLine[pos + 1]);
- sLine[pos] = '\x0';
- }
- }
- sValue = UnQuote(sValue);
- g_hArrayCvarList.PushString(sLine);
- g_hArrayCvarValues.PushString(sValue);
- }
- }
- }
- }
- delete hFile;
- return true;
- }
- // value for cvar can be quoted (CvarName "value") or not quoted (CvarName Value).
- //
- char[] UnQuote(char[] Str)
- {
- int pos;
- char EndChar;
- char buf[MAX_CVAR_VALUE_LENGTH];
- strcopy(buf, sizeof(buf), Str);
- TrimString(buf);
- if (buf[0] == '\"') {
- EndChar = '\"';
- strcopy(buf, sizeof(buf), buf[1]);
- } else {
- EndChar = ' ';
- }
- pos = FindCharInString(buf, EndChar);
- if( pos != -1 ) {
- buf[pos] = '\x0';
- }
- return buf;
- }
- // fix issue with trying to display in colsole cvar value with % specifier, or escape character
- //
- char[] EscapeString(char[] Str)
- {
- char buf[MAX_CVAR_VALUE_LENGTH];
- strcopy(buf, sizeof(buf), Str);
- ReplaceString(buf, sizeof(buf), "%", "%%");
- ReplaceString(buf, sizeof(buf), "\\", "\\\\");
- return buf;
- }
- // check if string is a valid number
- //
- bool IsNumeric(char[] Str)
- {
- static Regex regex;
- if (regex == null)
- regex = new Regex("^(((\\+|-)?\\d+(\\.\\d+)?)|((\\+|-)?\\.\\d+))(e(\\+|-)\\d+)?$", PCRE_CASELESS);
- if (strlen(Str) == 0) return (false);
- return (regex.Match(Str) > 0);
- }
- // print log to rcon/client consoles and file
- //
- void PrintConsoles(int client, const char[] format, any ...)
- {
- char buffer[500], buf2[500];
- VFormat(buffer, sizeof(buffer), format, 3);
- Format(buf2, sizeof(buf2), "[CVar_Anomaly]: %s", buffer);
- if ((g_iLogPos & 1) && client != 0 && IsClientInGame(client))
- PrintToConsole(client, EscapeString(buf2));
- if (g_iLogPos & 2)
- PrintToServer(EscapeString(buf2));
- if (g_iLogPos & 4)
- StringToLog(buf2);
- }
- void OpenLog(char[] access)
- {
- if (g_hLog != null) return;
- g_hLog = OpenFile(g_sLogPath, access);
- if( g_hLog == null )
- {
- PrintToServer("[CVar_Anomaly] Failed to open or create log file: %s (access: %s)", g_sLogPath, access);
- return;
- }
- }
- void CloseLog()
- {
- g_hLog.Close();
- g_hLog = null;
- }
- void StringToLog(char[] Str)
- {
- g_hLog.WriteLine(Str);
- FlushFile(g_hLog);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement