using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using EventsIO; using EventsIO.Interfaces; using ModHelper.Interfaces; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ModHelper")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("BFT")] [assembly: AssemblyProduct("ModHelper")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8d858f35-342c-4916-8e50-2b2cfd18640d")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace ModHelper { public class CommandArgsValidator { private readonly ILogger logger; public CommandArgsValidator(ILogger logger) { this.logger = logger; } public bool IsZeroArgs(IEnumerable args) { int num = args?.Count() ?? 0; if (num != 0) { logger?.LogWarning($"CommandArgsValidator wrong args count {num} (must be 0)"); return false; } return true; } public bool IsZeroArgs(IEnumerator args) { if (args == null || !args.MoveNext()) { return true; } logger?.LogWarning("CommandArgsValidator wrong args count (more than needed)"); return false; } private bool IsInvalidArgsCount(IEnumerator args) { if (args != null) { return !args.MoveNext(); } return true; } public bool IsValidInt(IEnumerator args, int minValue, int maxValue, bool isSkippable = false) { if (IsInvalidArgsCount(args)) { if (isSkippable) { return true; } logger?.LogWarning("CommandArgsValidator wrong args count"); return false; } try { int num = int.Parse(args.Current); if (num < minValue || num > maxValue) { string obj = $"CommandArgsValidator wrong integer value {num} (must be in range from {minValue} to {maxValue})"; logger?.LogWarning(obj); return false; } } catch (Exception arg) { logger?.LogWarning($"CommandArgsValidator can't parse integer value from '{args.Current}', {arg}"); return false; } return true; } public bool IsValidFloat(IEnumerator args, float minValue, float maxValue, bool isSkippable = false) { if (IsInvalidArgsCount(args)) { if (isSkippable) { return true; } logger?.LogWarning("CommandArgsValidator wrong args count"); return false; } try { float num = float.Parse(args.Current, CultureInfo.InvariantCulture.NumberFormat); if (num < minValue || num > maxValue) { string obj = $"CommandArgsValidator wrong float value {num} (must be in range from {minValue} to {maxValue})"; logger?.LogWarning(obj); return false; } } catch (Exception arg) { logger?.LogWarning($"CommandArgsValidator can't parse float value from '{args.Current}', {arg}"); } return true; } public bool IsValidString(IEnumerator args, Func validator, bool isSkippable = false) { if (IsInvalidArgsCount(args)) { if (isSkippable) { return true; } logger?.LogWarning("CommandArgsValidator wrong args count"); return false; } if (validator == null || validator(args.Current)) { return true; } logger?.LogWarning("CommandArgsValidator wrong string value " + args.Current); return false; } public bool IsValidBool(IEnumerator args, bool isSkippable = false) { if (IsInvalidArgsCount(args)) { if (isSkippable) { return true; } logger?.LogWarning("CommandArgsValidator wrong args count"); return false; } try { bool.Parse(args.Current.ToLower()); } catch (Exception arg) { logger?.LogWarning($"CommandArgsValidator can't parse bool value from '{args.Current}', {arg}"); return false; } return true; } } public class PeriodicTaskData { public string id; public float periodInSec; internal float periodCalculatedInSec; public int triggerTimes; public bool isInfiniteRepeates = true; public Action periodicAction; public Action finishAction; public override string ToString() { return "id=" + id + ", " + $"period={periodInSec}s, " + $"calc_period={periodCalculatedInSec}s, " + $"triggerTimes={triggerTimes}, " + $"isInfiniteRepeates={isInfiniteRepeates}"; } } public class TimerTaskData { public string id; public float durationInSec; public Action finishAction; public override string ToString() { return $"id={id}, duration={durationInSec}s"; } } public class BufferedLogger : HelperDebugLog, IDisposable { protected readonly Queue queue; protected readonly int bufferRecordsCount; private bool isDisposed; public BufferedLogger(string folderPath = null, string logFileName = null, bool isDebug = false, int bufferRecordsCount = 10) : base(folderPath, logFileName, isDebug) { this.bufferRecordsCount = bufferRecordsCount; queue = new Queue(); } public virtual void Flush(bool isNeedClearQueue = false) { if (queue.Count != 0) { WriteToFile(string.Join("\n", queue.ToArray())); if (isNeedClearQueue) { queue.Clear(); } } } public override void Log(object obj) { if (obj != null && isLogFile) { queue.Enqueue($"{LogTime} {obj}"); if (queue.Count >= bufferRecordsCount) { Flush(isNeedClearQueue: true); } } } protected virtual void Dispose(bool isDisposing) { if (!isDisposed) { isDisposed = true; if (queue.Count > 0) { Flush(); } } } public void Dispose() { Dispose(isDisposing: true); GC.SuppressFinalize(this); } ~BufferedLogger() { Dispose(isDisposing: false); } } public static class HelperGroups { public static Dictionary> GenerateNewEmptyGroupsDict() { return new Dictionary>(StringComparer.InvariantCultureIgnoreCase); } private static bool SafeApplyValidator(Func groupItemValidator, string itemName, ILogger logger = null) { try { return groupItemValidator?.Invoke(itemName) ?? true; } catch (Exception arg) { logger?.LogError($"RandomGroup.SafeApplyValidator unexpected error: {arg}"); } return false; } public static void InitGroupsFromFiles(string dataDir, Dictionary> groups, string groupsPrefix, Func groupItemValidator = null, IDebugLogger logger = null) { try { if (string.IsNullOrEmpty(groupsPrefix)) { logger?.LogError("RandomGroup error: wrong groupsPrefix " + groupsPrefix); return; } string[] files = Directory.GetFiles(dataDir); foreach (string path in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); if (fileNameWithoutExtension.StartsWith(groupsPrefix)) { groups[fileNameWithoutExtension] = GetItemsFromFile(path, logger); } } foreach (KeyValuePair> group in groups) { logger?.LogDebugInfo("RandomGroup validate list " + group.Key + ":"); List list = new List(); foreach (string item in group.Value) { if (!SafeApplyValidator(groupItemValidator, item, logger)) { logger?.LogWarning("invalid name " + item); list.Add(item); } } if (list.Count > 0) { foreach (string item2 in list) { group.Value.Remove(item2); } logger?.LogWarning("list " + group.Key + " is invalid"); } else { logger?.LogDebugInfo("list " + group.Key + " is valid"); } } } catch (Exception arg) { logger?.LogError($"RandomGroup.InitGroupsFromFiles unexpected error: {arg}"); } } public static Dictionary> GenerateGroupsFromFiles(string dataDir, string groupsPrefix, Func groupItemValidator = null, IDebugLogger logger = null) { Dictionary> dictionary = GenerateNewEmptyGroupsDict(); InitGroupsFromFiles(dataDir, dictionary, groupsPrefix, groupItemValidator, logger); return dictionary; } public static List GetItemsFromFile(string path, IDebugLogger logger = null) { try { List list = new HelperJson(logger).FromFile>(path); if (list == null) { list = new List(); } logger?.LogInfo($"Items from file {path}: found {list.Count}"); foreach (string item in list) { logger?.LogDebugInfo("value " + item); } return list; } catch (Exception arg) { logger?.LogError($"HelperGroups.GetItemsFromFile unexpected error: {arg}"); } return new List(); } public static bool IsValidGroupName(Dictionary> dict, string name) { return dict?.ContainsKey(name) ?? false; } public static void PrintGroups(Dictionary> dict, ILogger logger, string groupsName = "default_name") { if (logger == null) { return; } if (dict == null || dict.Count == 0) { logger?.LogWarning("No groups found for " + groupsName); return; } logger?.LogInfo("---------------Groups " + groupsName + " list---------------"); foreach (KeyValuePair> item in dict) { logger?.LogInfo(item.Key ?? ""); } logger?.LogInfo("---------------Groups " + groupsName + " list end---------------"); } } public class HelperJson : IJsonUtils { private class ContainerObjAndInt { public object valueObj; public int valueInt; } private class ContainerStringAndInt { public string valueString; public int valueInt; } private ILogger logger; private const char startClass = '{'; private const char endClass = '}'; private const char startArray = '['; private const char endArray = ']'; private const char isHasNextField = ','; private const char special = '\\'; private const char startString = '"'; private const char nameValueSeparator = ':'; private char[] ignoreChars = new char[9] { ' ', '\n', '\r', '\t', '\b', '\f', '\0', '{', '[' }; private string logMsgBaseError = "HelperJson error:"; private string logMsgBase = "HelperJson:"; private bool isVerboseDebugLog; private void VerboseDebugLog(string s) { if (isVerboseDebugLog) { logger?.LogInfo("[DEBUG] " + s); } } public HelperJson(IDebugLogger logger = null, bool isVerboseDebugLog = false) { this.logger = logger; this.isVerboseDebugLog = isVerboseDebugLog; } public T FromFile(string path) where T : class { string s; using (StreamReader streamReader = new StreamReader(path)) { s = streamReader.ReadToEnd(); } return FromString(s); } public T FromString(string s) where T : class { return GetFromString(typeof(T), s, 0).valueObj as T; } private char GetSpecialChar(char c) { if (c != '"') { return c; } return c; } private void SetFieldClass(T instance, string fieldName, R fieldValue) where T : class where R : class { Type type = instance.GetType(); VerboseDebugLog("type=" + type.Name + ", field=" + fieldName); type.GetField(fieldName).SetValue(instance, fieldValue); } private ContainerStringAndInt ParseNextStringValue(string s, int index, bool isSkipSep = true) { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; int i; for (i = index; i < s.Length; i++) { char c = s[i]; VerboseDebugLog($"{logMsgBase} ParseNextStringValue for process {c}"); if (!flag) { i = SkipIgnoredChars(s, i, isSkipHasNextField: false, isSkipSep); if (i >= s.Length) { logger?.Log(logMsgBaseError + " class field value parsing internal issue. Set default value."); break; } c = s[i]; if (c == '"') { flag = true; VerboseDebugLog(logMsgBase + " ParseNextStringValue is startString case"); continue; } if (c == ']' || c == '}') { break; } if (c == ',' || c == ':') { i = SkipIgnoredChars(s, i, isSkipHasNextField: true, isSkipSep); break; } } else if (c == '\\') { VerboseDebugLog(logMsgBase + " ParseNextStringValue is special case"); i++; if (i >= s.Length) { logger?.Log(logMsgBaseError + " class field value parsing internal issue with special symbols. Set default value."); break; } c = GetSpecialChar(s[i]); if (c != '"') { stringBuilder.Append('\\'); } } else if (c == '"') { VerboseDebugLog(logMsgBase + " ParseNextStringValue is startString end case"); i = SkipIgnoredChars(s, ++i, isSkipHasNextField: true, isSkipSep); break; } VerboseDebugLog($"{logMsgBase} ParseNextStringValue append {c}"); stringBuilder.Append(c); } string text = stringBuilder.ToString(); VerboseDebugLog($"{logMsgBase} ParseNextStringValue {text}, end at index {i}"); return new ContainerStringAndInt { valueString = text, valueInt = i }; } private ContainerStringAndInt GetNextFieldName(string s, int index) { VerboseDebugLog(logMsgBase + " GetNextFieldName"); return ParseNextStringValue(s, index, isSkipSep: false); } private ContainerObjAndInt GetNextValue(Type valueType, string s, int index, bool isSkipSep) { VerboseDebugLog(logMsgBase + " GetNextValue"); int num = SkipIgnoredChars(s, index, isSkipHasNextField: true, isSkipSep); if (num >= s.Length) { logger?.LogError(logMsgBaseError + " class parsing internal issue at value type " + valueType.Name + ". Set default value."); } if (valueType.IsPrimitive) { ContainerStringAndInt containerStringAndInt = ParseNextStringValue(s, num, isSkipSep); try { TypeCode typeCode = Type.GetTypeCode(valueType); object valueObj; if ((uint)(typeCode - 5) <= 10u) { if (double.TryParse(containerStringAndInt.valueString, NumberStyles.Any, CultureInfo.InvariantCulture, out var result)) { valueObj = Convert.ChangeType(result, valueType); } else { valueObj = Activator.CreateInstance(valueType); logger?.Log(logMsgBaseError + " incorrect convert, use default."); logger?.Log("Read value: " + containerStringAndInt.valueString + ", type: " + valueType.Name); } } else { valueObj = Convert.ChangeType(containerStringAndInt.valueString, valueType); } return new ContainerObjAndInt { valueObj = valueObj, valueInt = containerStringAndInt.valueInt }; } catch (Exception arg) { logger?.Log($"{logMsgBaseError} incorrect format, use default. Msg: {arg}"); logger?.Log("read value: " + containerStringAndInt.valueString + ", type: " + valueType.Name); return new ContainerObjAndInt { valueObj = Activator.CreateInstance(valueType), valueInt = containerStringAndInt.valueInt }; } } if (valueType.IsAssignableFrom(typeof(string))) { ContainerStringAndInt containerStringAndInt2 = ParseNextStringValue(s, num, isSkipSep); return new ContainerObjAndInt { valueObj = containerStringAndInt2.valueString, valueInt = containerStringAndInt2.valueInt }; } ContainerObjAndInt fromString = GetFromString(valueType, s, num); return new ContainerObjAndInt { valueObj = fromString.valueObj, valueInt = fromString.valueInt }; } private int SkipIgnoredChars(string s, int index, bool isSkipHasNextField = false, bool isSkipSep = false) { for (int i = index; i < s.Length; i++) { if (ignoreChars.Contains(s[i])) { VerboseDebugLog($"skip char '{s[i]}'"); continue; } if (isSkipHasNextField && s[i] == ',') { VerboseDebugLog($"skip char '{s[i]}'"); continue; } if (isSkipSep && s[i] == ':') { VerboseDebugLog($"skip char '{s[i]}'"); continue; } return i; } return s.Length; } private ContainerObjAndInt ParseIListByItemType(Type itemType, string s, int index) { if (!(Activator.CreateInstance(typeof(List<>).MakeGenericType(itemType)) is IList list)) { logger?.LogError(logMsgBaseError + " can't create IList for item type " + itemType.Name); return new ContainerObjAndInt { valueObj = null, valueInt = s.Length }; } do { ContainerObjAndInt nextValue = GetNextValue(itemType, s, index, isSkipSep: true); object valueObj = nextValue.valueObj; index = nextValue.valueInt; list.Add(valueObj); index = SkipIgnoredChars(s, index, isSkipHasNextField: true, isSkipSep: true); } while (index < s.Length && s[index] != ']' && s[index] != '}'); return new ContainerObjAndInt { valueObj = list, valueInt = index + 1 }; } private ContainerObjAndInt ParseIList(Type resultType, string s, int index) { Type itemType = (resultType.IsGenericType ? resultType.GetGenericArguments()[0] : resultType.GetElementType()); return ParseIListByItemType(itemType, s, index); } private ContainerObjAndInt ParseArray(Type resultType, string s, int index) { Type elementType = resultType.GetElementType(); ContainerObjAndInt containerObjAndInt = ParseIListByItemType(elementType, s, index); Array array; if (containerObjAndInt.valueObj is IList list) { array = Array.CreateInstance(elementType, list.Count); for (int i = 0; i < array.Length; i++) { array.SetValue(list[i], i); } } else { logger?.Log(logMsgBaseError + " create empty Array"); array = Array.CreateInstance(elementType, 0); } return new ContainerObjAndInt { valueObj = array, valueInt = containerObjAndInt.valueInt }; } private ContainerObjAndInt ParseIDictionary(Type dictType, string s, int index) { Type[] genericArguments = dictType.GetGenericArguments(); if (genericArguments.Length != 2) { logger?.Log(logMsgBaseError + " can't create IDictionary, invalid args"); } if (dictType.IsGenericType) { if (!dictType.IsGenericTypeDefinition) { dictType = dictType.GetGenericTypeDefinition(); } dictType = dictType.MakeGenericType(genericArguments); } if (!(Activator.CreateInstance(dictType) is IDictionary dictionary)) { string name = dictType.GetType().Name; logger?.Log(logMsgBaseError + " can't create IDictionary " + name + " for item type " + genericArguments[0].Name + ", " + genericArguments[1].Name); return new ContainerObjAndInt { valueObj = null, valueInt = s.Length }; } Type valueType = genericArguments[0]; Type valueType2 = genericArguments[1]; do { ContainerObjAndInt nextValue = GetNextValue(valueType, s, index, isSkipSep: false); index = nextValue.valueInt; ContainerObjAndInt nextValue2 = GetNextValue(valueType2, s, index, isSkipSep: true); index = nextValue2.valueInt; dictionary.Add(nextValue.valueObj, nextValue2.valueObj); VerboseDebugLog($"{logMsgBase} dict add key {nextValue.valueObj}"); index = SkipIgnoredChars(s, index, isSkipHasNextField: true, isSkipSep: true); } while (index < s.Length && s[index] != '}' && s[index] != ']'); return new ContainerObjAndInt { valueObj = dictionary, valueInt = index + 1 }; } private bool IsTypeOf(Type targetType, Type sourceType, int argsCount) { if (sourceType.IsAssignableFrom(targetType)) { VerboseDebugLog(logMsgBase + " is " + targetType.Name); return true; } Type[] types = sourceType.Assembly.GetTypes(); foreach (Type type in types) { if (!type.IsClass) { continue; } Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { if (type2.IsGenericType && type2.GetGenericTypeDefinition() == targetType && sourceType.GetGenericArguments().Length == argsCount) { VerboseDebugLog(logMsgBase + " is " + targetType.Name + " assembly interface"); return true; } } } return false; } private ContainerObjAndInt GetFromString(Type resultType, string s, int index) { try { VerboseDebugLog(logMsgBase + " GetFromString"); if (resultType.IsArray) { VerboseDebugLog(logMsgBase + " is array"); return ParseArray(resultType, s, index); } if (resultType.IsAssignableFrom(typeof(IList)) || IsTypeOf(typeof(IList<>), resultType, 1)) { return ParseIList(resultType, s, index); } if (resultType.IsAssignableFrom(typeof(IDictionary)) || IsTypeOf(typeof(IDictionary<, >), resultType, 2)) { return ParseIDictionary(resultType, s, index); } if (resultType.IsGenericType && !resultType.IsGenericTypeDefinition) { Type[] genericArguments = resultType.GetGenericArguments(); resultType = resultType.GetGenericTypeDefinition(); resultType = resultType.MakeGenericType(genericArguments); } object obj = Activator.CreateInstance(resultType); int num = index; while (num < s.Length && s[num] != ']' && s[num] != '}') { ContainerStringAndInt nextFieldName = GetNextFieldName(s, num); string valueString = nextFieldName.valueString; if (string.IsNullOrEmpty(valueString?.Trim())) { VerboseDebugLog(logMsgBase + " IsNullOrWhiteSpace field " + valueString + ", class type " + resultType.Name); num = GetNextValue(typeof(string), s, num, isSkipSep: true).valueInt; continue; } num = nextFieldName.valueInt; FieldInfo field = resultType.GetField(valueString); VerboseDebugLog(logMsgBase + " field " + valueString + ", class type " + resultType.Name); if (isVerboseDebugLog) { FieldInfo[] fields = resultType.GetFields(); VerboseDebugLog(logMsgBase + " fields in class: "); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { VerboseDebugLog(fieldInfo.Name); } } if (field == null) { logger?.LogWarning(logMsgBaseError + " can't find field " + valueString + " for type " + resultType.Name + ". Try to skip."); num = GetNextValue(typeof(string), s, num, isSkipSep: true).valueInt; } else { ContainerObjAndInt nextValue = GetNextValue(field.FieldType, s, num, isSkipSep: true); object valueObj = nextValue.valueObj; num = nextValue.valueInt; SetFieldClass(obj, valueString, valueObj); } } num = SkipIgnoredChars(s, num + 1, isSkipHasNextField: true, isSkipSep: true); return new ContainerObjAndInt { valueObj = obj, valueInt = num }; } catch (Exception arg) { logger?.LogError($"{logMsgBaseError} {arg}"); } return new ContainerObjAndInt { valueObj = null, valueInt = s.Length }; } } public class HelperDebugLog : HelperLog, IDebugLogger, ILoggerWithConsole, ILogger { protected bool isDebug; public HelperDebugLog(string folderPath = null, string logFileName = null, bool isDebug = false) : base(folderPath, logFileName) { this.isDebug = isDebug; } public virtual void LogDebugInfo(object obj) { if (isDebug) { LogInfo($"[DEBUG] {obj}"); } } public virtual void LogDebugWarning(object obj) { if (isDebug) { LogWarning($"[DEBUG] {obj}"); } } public virtual void LogDebugError(object obj) { if (isDebug) { LogError($"[DEBUG] {obj}"); } } } public class ScheduleManager { protected ILogger logger; protected Dictionary timerTasks = new Dictionary(); protected Dictionary periodicTasks = new Dictionary(); protected List timerTasksNoId = new List(); protected List periodicTasksNoId = new List(); protected int onUpdateErrorLimit = 10; public ScheduleManager(ILogger logger = null, int onUpdateErrorLimit = 0) { this.logger = logger; this.onUpdateErrorLimit = onUpdateErrorLimit; } public void NewTimerTask(TimerTaskData task, bool triggerFinishIfExists = false) { if (task == null) { logger?.LogInfo($"ScheduleManager new timer task null error. Details info: {task}"); return; } if (task.id == null) { NewTimerTaskWithOutId(task); return; } if (triggerFinishIfExists && timerTasks.ContainsKey(task.id)) { SafeRunAction(timerTasks[task.id].finishAction); } timerTasks[task.id] = task; logger?.LogInfo($"ScheduleManager new timer task {task}"); } public void NewPeriodicTask(PeriodicTaskData task, bool triggerFinishIfExists = false) { if (task == null) { logger?.LogInfo($"ScheduleManager new periodic task null error. Details info: {task}"); return; } task.periodCalculatedInSec = task.periodInSec; if (task.id == null) { NewPeriodicTaskWithOutId(task); return; } if (triggerFinishIfExists && periodicTasks.ContainsKey(task.id)) { SafeRunAction(periodicTasks[task.id].finishAction); } periodicTasks[task.id] = task; logger?.LogInfo($"ScheduleManager new periodic task {task}"); } public void AppendPeriodicTask(PeriodicTaskData task, bool isReplaceAction = false) { if (task.id == null || !periodicTasks.ContainsKey(task.id)) { NewPeriodicTask(task); return; } logger?.LogInfo($"ScheduleManager append periodic task {task}"); PeriodicTaskData periodicTaskData = periodicTasks[task.id]; periodicTaskData.triggerTimes += task.triggerTimes; if (isReplaceAction) { periodicTaskData.finishAction = task.finishAction; } } protected void NewTimerTaskWithOutId(TimerTaskData task) { if (task == null) { logger?.LogWarning($"ScheduleManager new timer no id task null error. Details info: {task}"); return; } timerTasksNoId.Add(task); logger?.LogInfo($"ScheduleManager new timer no id task {task}"); } protected void NewPeriodicTaskWithOutId(PeriodicTaskData task) { if (task == null) { logger?.LogWarning($"ScheduleManager new periodic no id task null error. Details info: {task}"); return; } periodicTasksNoId.Add(task); logger?.LogInfo($"ScheduleManager new periodic no id task {task}"); } public void AppendTimerTask(TimerTaskData task, bool isReplaceAction = true) { if (!timerTasks.ContainsKey(task.id)) { NewTimerTask(task); return; } logger?.LogInfo($"ScheduleManager append timer task {task}"); TimerTaskData timerTaskData = timerTasks[task.id]; timerTasks[task.id] = task; task.durationInSec += timerTaskData.durationInSec; if (!isReplaceAction) { task.finishAction = timerTaskData.finishAction; } } public bool IsTaskExists(string id) { if (!periodicTasks.ContainsKey(id)) { return timerTasks.ContainsKey(id); } return true; } public void RemoveTimerTask(string id) { if (timerTasks.Remove(id)) { logger?.LogInfo("ScheduleManager remove timer task " + id); } } public void RemovePeriodicTask(string id) { if (periodicTasks.Remove(id)) { logger?.LogInfo("ScheduleManager remove periodic task " + id); } } public void RemoveAllTasks(string id) { logger?.LogInfo("ScheduleManager RemoveAllTasks with id " + id); RemoveTimerTask(id); RemovePeriodicTask(id); } public void RemoveAllTasks(bool isSuppressLog = false) { timerTasks.Clear(); periodicTasks.Clear(); timerTasksNoId.Clear(); periodicTasksNoId.Clear(); if (!isSuppressLog && logger != null) { logger.LogInfo("ScheduleManager RemoveAllTasks"); } } protected List ProcessTimersList(ICollection list, float dtInSec) { List list2 = new List(); foreach (TimerTaskData item in list) { item.durationInSec -= dtInSec; if (item.durationInSec <= 0f) { SafeRunAction(item.finishAction); list2.Add(item); logger?.LogInfo("ScheduleManager triggered finish action timer task " + item.id); } } return list2; } protected List ProcessPeriodicList(ICollection list, float dtInSec) { List list2 = new List(); foreach (PeriodicTaskData item in list) { item.periodCalculatedInSec -= dtInSec; if (!(item.periodCalculatedInSec <= 0f)) { continue; } if (item.isInfiniteRepeates || item.triggerTimes > 0) { SafeRunAction(item.periodicAction); item.periodCalculatedInSec = item.periodInSec; if (item.triggerTimes == 1) { logger?.LogInfo("ScheduleManager triggered action periodic task " + item.id); } } if (!item.isInfiniteRepeates) { item.triggerTimes--; if (item.triggerTimes <= 0) { SafeRunAction(item.finishAction); list2.Add(item); logger?.LogInfo("ScheduleManager triggered finish action periodic task " + item.id); } } } return list2; } public void OnUpdate(float dtInSec) { try { foreach (TimerTaskData item in ProcessTimersList(timerTasks.Values, dtInSec)) { timerTasks.Remove(item.id); logger?.LogInfo($"ScheduleManager OnUpdate finished timer task {item}"); } foreach (PeriodicTaskData item2 in ProcessPeriodicList(periodicTasks.Values, dtInSec)) { periodicTasks.Remove(item2.id); logger?.LogInfo($"ScheduleManager OnUpdate finished periodic task {item2}"); } foreach (TimerTaskData item3 in ProcessTimersList(timerTasksNoId, dtInSec)) { timerTasksNoId.Remove(item3); logger?.LogInfo($"ScheduleManager OnUpdate finished timer no id task {item3}"); } foreach (PeriodicTaskData item4 in ProcessPeriodicList(periodicTasksNoId, dtInSec)) { periodicTasksNoId.Remove(item4); logger?.LogInfo($"ScheduleManager OnUpdate finished periodic no id task {item4}"); } } catch (Exception arg) { if (onUpdateErrorLimit > 0) { logger?.LogError($"ScheduleManager unexpected onUpdate error: {arg}"); onUpdateErrorLimit--; } } } public void FinishImmediatlyTimerTask(string id) { if (timerTasks.ContainsKey(id)) { SafeRunAction(timerTasks[id].finishAction); timerTasks.Remove(id); logger?.LogInfo("ScheduleManager finish and remove timer task " + id); } } public float GetTimerTaskDurationInSec(string id) { if (!timerTasks.ContainsKey(id)) { return -1f; } return timerTasks[id].durationInSec; } public void FinishImmediatlyPeriodicTask(string id) { if (periodicTasks.ContainsKey(id)) { SafeRunAction(periodicTasks[id].finishAction); periodicTasks.Remove(id); logger?.LogInfo("ScheduleManager finish and remove periodic task " + id); } } public void FinishAllImmediatly(string id) { FinishImmediatlyTimerTask(id); FinishImmediatlyPeriodicTask(id); } public void FinishAllImmediatly() { foreach (TimerTaskData value in timerTasks.Values) { SafeRunAction(value.finishAction); } foreach (PeriodicTaskData value2 in periodicTasks.Values) { SafeRunAction(value2.finishAction); } foreach (TimerTaskData item in timerTasksNoId) { SafeRunAction(item.finishAction); } foreach (PeriodicTaskData item2 in periodicTasksNoId) { SafeRunAction(item2.finishAction); } RemoveAllTasks(); } protected void SafeRunAction(Action action) { if (action == null) { return; } try { action(); } catch (Exception arg) { logger?.LogError($"SafeRunAction error: {arg}"); } } } public class CommandConsole { private readonly Dictionary consoleCommandsDict; private readonly IDebugLogger logger; private readonly List ignoreValidationCommands; public bool isLogCheckCommand = true; public bool isLogCheckCommandParameters = true; public int logCheckCommandParametersMaxLength = 40; public bool isLogProcessCommand = true; public bool isLogProcessCommandParameters = true; public int logProcessCommandParametersMaxLength = 40; public CommandConsole(IDebugLogger logger = null, List ignoreValidationCommands = null) { consoleCommandsDict = new Dictionary(); this.logger = logger; this.ignoreValidationCommands = ignoreValidationCommands; } public void RegisterCommand(IConsoleCommand command) { if (string.IsNullOrEmpty(command?.Id)) { logger?.LogWarning("UtilityConsole.RegisterCommand: invalid console command"); } else { consoleCommandsDict[command.Id] = command; } } public static string[] ParseCommandLine(string commandLine) { return commandLine.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); } public static string[] ParseArgs(string command) { return command.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); } private string GetCmdName(string cmd) { string[] array = cmd.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length != 0) { return array[0]; } return ""; } private void LogCommand(string funcPrefix, string cmd, bool isLog, bool isLogParams, int maxLen) { if (logger == null || !isLog) { return; } if (string.IsNullOrEmpty(cmd)) { logger.LogWarning(funcPrefix + " null or empty"); } else if (isLogParams) { if (cmd.Length > maxLen) { string text = cmd.Substring(0, maxLen); logger.Log(funcPrefix + " " + text + "..."); } else { logger.Log(funcPrefix + " " + cmd); } } else { logger.Log(funcPrefix + " " + GetCmdName(cmd)); } } public bool IsValidCommandLine(string commandLine) { try { if (string.IsNullOrEmpty(commandLine)) { logger?.LogDebugError("UtilityConsole.IsValidCommandLine: invalid command line"); return false; } string[] array = ParseCommandLine(commandLine); if (array == null || array.Length < 1) { logger?.LogDebugError("UtilityConsole.IsValidCommandLine: invalid commands count"); return false; } bool flag = true; string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text = array2[i]?.Trim(); LogCommand("UtilityConsole.IsValidCommandLine: check command", text, isLogCheckCommand, isLogCheckCommandParameters, logCheckCommandParametersMaxLength); string[] array3 = ParseArgs(text); if (array3 == null || array3.Length < 1) { logger?.LogDebugError("UtilityConsole.IsValidCommandLine: invalid commands count"); flag = false; continue; } string text2 = array3[0]; List list = ignoreValidationCommands; if (list != null && list.Contains(text2)) { logger?.LogDebugInfo("UtilityConsole.IsValidCommandLine: validation is ignored for this command"); } else if (consoleCommandsDict.ContainsKey(text2)) { bool flag2 = consoleCommandsDict[text2].IsValidCommandArgs(array3.Skip(1)); if (!flag2) { logger?.LogDebugError("UtilityConsole.IsValidCommandLine: command with id " + text2 + " is invalid"); } flag = flag && flag2; } else { logger?.LogDebugError("UtilityConsole.IsValidCommandLine: can't find custom command with id " + text2); flag = false; } } return flag; } catch (Exception arg) { logger?.LogDebugError(string.Format("{0} error: {1}", "UtilityConsole.IsValidCommandLine:", arg)); return false; } } public bool RunCommand(string commandLine, IEventsData data, Action onCustomCommandNotFound = null) { try { string[] array = ParseCommandLine(commandLine); foreach (string text in array) { LogCommand("UtilityConsole.RunCommand: process command", text, isLogProcessCommand, isLogProcessCommandParameters, logProcessCommandParametersMaxLength); string[] array2 = ParseArgs(text); string key = array2[0]; if (consoleCommandsDict.ContainsKey(key)) { try { if (consoleCommandsDict[key] is IConsoleCommandWithData consoleCommandWithData) { consoleCommandWithData.Execute(array2.Skip(1), data); } else { consoleCommandsDict[key].Execute(array2.Skip(1)); } } catch (Exception arg) { logger?.LogError(string.Format("{0} execute command error {1}", "UtilityConsole.RunCommand:", arg)); } } else if (onCustomCommandNotFound != null) { onCustomCommandNotFound(text); } else { logger?.LogError("UtilityConsole.RunCommand: execute command not found error"); } } } catch (Exception arg2) { logger?.LogError(string.Format("{0} error {1}", "UtilityConsole.RunCommand:", arg2)); return false; } return true; } } public class CommandArgsReader { private readonly ILogger logger; private readonly IEnumerator args; public CommandArgsReader(IEnumerable argsSource, ILogger logger) { args = argsSource.GetEnumerator(); this.logger = logger; } public int ReadInt(int defaultValue = 0) { if (args == null || !args.MoveNext()) { return defaultValue; } try { return int.Parse(args.Current); } catch (Exception arg) { logger?.LogWarning($"CommandArgsReader: {arg}"); return defaultValue; } } public float ReadFloat(float defaultValue = 0f) { if (args == null || !args.MoveNext()) { return defaultValue; } try { return float.Parse(args.Current, CultureInfo.InvariantCulture.NumberFormat); } catch (Exception arg) { logger?.LogWarning($"CommandArgsReader: {arg}"); return defaultValue; } } public string ReadString(string defaultValue = "") { if (args != null && args.MoveNext()) { return args.Current; } return defaultValue; } public bool ReadBool(bool defaultValue = false) { if (args == null || !args.MoveNext()) { return defaultValue; } try { return bool.Parse(args.Current.ToLower()); } catch (Exception arg) { logger?.LogWarning($"CommandArgsReader: {arg}"); return defaultValue; } } public string ReadSpacedString(string defaultValue = "") { return ParseItemPath(ReadString(defaultValue)); } public string ReadAllAsSingleString(string separator = " ", string defaultValue = "") { if (args == null) { return defaultValue; } StringBuilder stringBuilder = new StringBuilder(); bool flag = true; while (args.MoveNext()) { if (!flag && !string.IsNullOrEmpty(separator)) { stringBuilder.Append(separator); } flag = false; stringBuilder.Append(args.Current); } string text = stringBuilder.ToString(); if (!string.IsNullOrEmpty(text)) { return text; } return defaultValue; } public string[] ParseList(string s, string separator = "|") { if (string.IsNullOrEmpty(s)) { return null; } return (from item in s.Split(new string[1] { separator }, StringSplitOptions.None) select item.Trim() into item where !string.IsNullOrWhiteSpace(item) select item).ToArray(); } public static string ParseItemPath(string path) { return path?.Replace("_", " "); } } public static class RandomUtils { private static readonly Random rnd = new Random(Guid.NewGuid().GetHashCode()); public static T GetRandomItemFromList(IList list) { if (list != null && list.Count > 0) { return list[rnd.Next(list.Count)]; } return default(T); } public static List Shuffle(IEnumerable list) { if (list == null) { return new List(); } int num = list.Count(); if (num <= 1) { return new List(list); } List list2 = new List(list); for (int i = 0; i < num; i++) { T value = list2[i]; int index = rnd.Next(num); list2[i] = list2[index]; list2[index] = value; } return list2; } public static List ShuffleForceRandom(IEnumerable list) { if (list == null) { return new List(); } int num = list.Count(); if (num <= 1) { return new List(list); } T[] array = new T[num]; T[] array2 = list.ToArray(); List list2 = new List(Enumerable.Range(0, num)); for (int i = 0; i < num; i++) { List list3 = new List(list2); list3.Remove(i); if (list3.Count == 0) { break; } int num2 = list3[rnd.Next(list3.Count)]; array[i] = array2[num2]; list2.Remove(num2); } if (list2.Count == 1) { int num3 = num - 1; int num4 = rnd.Next(num3); array[num3] = array[num4]; array[num4] = array2[num3]; } return array.ToList(); } } public class HelperCommandManagerFormatted : ICommandManager { private Dictionary commandsDict; private readonly IDebugLogger logger; private readonly string errorString = string.Empty; public int logCommandMaxLength = 50; public bool isUseLogCommandLimit = true; public HelperCommandManagerFormatted(IDebugLogger logger) { this.logger = logger; } public virtual void InitFromFile(string path) { try { Dictionary dictionary = JsonUtils.Generate(logger).FromFile>(path); commandsDict = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); CheckLoadedDict(); } catch (Exception arg) { logger?.LogError($"HelperCommandManagerFormatted InitFromFile load: {arg}"); commandsDict = null; } } public virtual void InitFromString(string data) { try { Dictionary dictionary = JsonUtils.Generate(logger).FromString>(data); commandsDict = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); CheckLoadedDict(); } catch (Exception arg) { logger?.LogError($"HelperCommandManagerFormatted InitFromString load: {arg}"); commandsDict = null; } } protected virtual void CheckLoadedDict() { if (commandsDict == null) { logger?.LogWarning("HelperCommandManagerFormatted: commandsDict null"); return; } logger?.LogInfo($"HelperCommandManagerFormatted: found commands {commandsDict.Count}"); foreach (KeyValuePair item in commandsDict) { logger?.LogDebugInfo("command: key=" + item.Key + ", value=" + GetSubstringCmd(item.Value)); } } protected virtual string GetSubstringCmd(string cmd) { if (isUseLogCommandLimit && cmd != null) { if (cmd.Length > logCommandMaxLength) { return cmd.Substring(0, logCommandMaxLength) + "..."; } return cmd; } return cmd; } public virtual string GetCommandData(string eventKey, bool isLogError = false) { if (commandsDict == null) { logger?.LogWarning("HelperCommandManagerFormatted GetCommandData: commandsDict null"); return ""; } try { eventKey = eventKey.Trim(); string text = commandsDict[eventKey]; if (text != null) { return text; } throw new NullReferenceException(); } catch (Exception arg) { if (isLogError) { logger?.LogError($"HelperCommandManagerFormatted key {eventKey}, error: {arg}"); } return errorString; } } public virtual string[] GetCommands() { if (commandsDict == null) { logger?.LogWarning("HelperCommandManagerFormatted GetCommands: commandsDict null"); return new string[0]; } List list = new List(); foreach (KeyValuePair item in commandsDict) { list.Add(item.Value); } return list.ToArray(); } } public class HelperDictManager : IDictManager { private readonly ILogger logger; private readonly IEventsDataEncoder encoder; private readonly IEventsDataEncoderParams encoderParams; public HelperDictManager(ILogger logger = null) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown this.logger = logger; encoderParams = (IEventsDataEncoderParams)(object)(encoder = (IEventsDataEncoder)new EventsDataEncoder()); } public Dictionary ParseDict(string data) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); try { string[] array = data.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if (array == null) { logger?.LogError("HelperDictManager error, wrong args. Source: " + data); return dictionary; } string[] array2 = array; foreach (string text in array2) { string[] array3 = text.Split(new char[1] { encoderParams.ClientAppSepChar }, StringSplitOptions.RemoveEmptyEntries); if (array3.Length != 2) { logger?.LogWarning("HelperDictManager, invalid item: " + text); continue; } string text2 = array3[0]; string text3 = array3[1]; if (text3 == null || text2 == null) { logger?.LogWarning("HelperDictManager, invalid key/val: " + text2 + "/" + text3); continue; } string key = encoder.Decode(text2).Trim(); string value = encoder.Decode(text3).Trim(); dictionary.Add(key, value); } } catch (Exception arg) { logger?.LogError($"HelperDictManager error: {arg}"); } return dictionary; } } public class HelperCommandManager : ICommandManager { private readonly Dictionary commandsDict; private readonly IDebugLogger logger; private readonly string errorString = string.Empty; public HelperCommandManager(IDebugLogger logger, string commandsDir, string commandsFname = "commands.data") { this.logger = logger; try { string path = Path.Combine(commandsDir, commandsFname); IDictManager dictManager = new HelperDictManager(); using StreamReader streamReader = new StreamReader(path); string data = streamReader.ReadToEnd(); Dictionary dictionary = dictManager.ParseDict(data); commandsDict = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); logger?.LogInfo($"HelperCommandManager: found commands {commandsDict.Count}"); foreach (KeyValuePair item in commandsDict) { logger?.LogDebugInfo("command: key=" + item.Key + ", value=" + item.Value); } } catch (Exception arg) { logger?.LogError($"HelperCommandManager load: {arg}"); commandsDict = new Dictionary(StringComparer.OrdinalIgnoreCase); } } public string GetCommandData(string eventKey, bool isLogError = false) { try { eventKey = eventKey.Trim(); string text = commandsDict[eventKey]; if (text != null) { return text; } throw new NullReferenceException(); } catch (Exception arg) { if (isLogError) { logger?.LogError($"HelperCommandManager key {eventKey}: {arg}"); } return errorString; } } public string[] GetCommands() { List list = new List(); foreach (KeyValuePair item in commandsDict) { list.Add(item.Value); } return list.ToArray(); } public virtual void InitFromFile(string path) { throw new NotImplementedException(); } public virtual void InitFromString(string data) { throw new NotImplementedException(); } } public class HelperEventManager : IEventManager where CustomEvent : class, IEvent { private readonly Dictionary eventsDict; private readonly IDebugLogger logger; public HelperEventManager(IDebugLogger logger) { this.logger = logger; eventsDict = new Dictionary(StringComparer.OrdinalIgnoreCase); try { Type type = typeof(CustomEvent); IEnumerable enumerable = from p in AppDomain.CurrentDomain.GetAssemblies().SelectMany(delegate(Assembly s) { try { return s.GetTypes(); } catch (Exception) { return new Type[0]; } }) where p.IsClass && !p.IsAbstract && !p.IsInterface && type.IsAssignableFrom(p) select p; logger?.LogInfo($"HelperEventManager: found {enumerable.Count()} events"); foreach (Type item in enumerable) { CustomEvent val = Activator.CreateInstance(item) as CustomEvent; if (val != null) { eventsDict.Add(val.Id, val); logger?.LogDebugInfo("init event '" + val.Id + "' complete"); } else { logger?.LogWarning("init event '" + val.Id + "' failed"); } } } catch (Exception arg) { logger?.LogError($"HelperEventManager error: {arg}"); } } public HelperEventManager(IDebugLogger logger, IEventManager manager) { this.logger = logger; eventsDict = new Dictionary(StringComparer.OrdinalIgnoreCase); string name = typeof(CustomEvent).Name; try { foreach (IEvent item in manager.GetEventsCollection()) { if (item is CustomEvent val) { eventsDict.Add(val.Id, val); logger?.LogDebugInfo("init event '" + val.Id + "' as '" + name + "' complete"); } } } catch (Exception arg) { logger?.LogError($"HelperEventManager: {arg}"); } } public IEnumerable GetEventsCollection() { if (eventsDict == null) { return new List(); } return eventsDict.Values; } public CustomEvent GetEvent(string eventID, bool isLogError = false) { try { eventID = eventID.Trim(); CustomEvent val = eventsDict[eventID]; if (val != null) { return val; } throw new NullReferenceException(); } catch (Exception arg) { if (isLogError) { logger?.LogError($"HelperCommandManager key {eventID}: {arg}"); } return null; } } } public class HelperLanguagesFormatted : ITranslation { protected Dictionary transDict; protected readonly IDebugLogger logger; protected readonly string errorString = "trans_error"; public bool isLogOnlyEnItems; public bool isSuppressLogItems = true; public HelperLanguagesFormatted(IDebugLogger logger) { this.logger = logger; } public virtual void InitFromFile(string path) { try { Dictionary dictionary = JsonUtils.Generate(logger).FromFile>(path); transDict = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); CheckLoadedDict(); } catch (Exception arg) { logger?.LogError($"HelperLanguagesFormatted InitFromFile load: {arg}"); transDict = null; } } public virtual void InitFromString(string data) { try { Dictionary dictionary = JsonUtils.Generate(logger).FromString>(data); transDict = new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); CheckLoadedDict(); } catch (Exception arg) { logger?.LogError($"HelperLanguagesFormatted InitFromString load: {arg}"); transDict = null; } } protected virtual void CheckLoadedDict() { if (transDict == null) { logger?.LogWarning("HelperLanguagesFormatted: transDict null"); return; } logger?.LogInfo($"HelperLanguagesFormatted: found translates {transDict.Count}"); if (isSuppressLogItems || logger == null) { return; } foreach (KeyValuePair item in transDict) { if (isLogOnlyEnItems) { logger.LogDebugInfo("key=" + item.Key + ", en=" + item.Value.en); continue; } logger.LogDebugInfo("key=" + item.Key + ", ru=" + item.Value.ru + ", en=" + item.Value.en); } } public virtual string GetTrans(string key, Languages lang) { return GetTrans(key, lang.ToString()); } public virtual string GetTrans(string key, string lang) { if (transDict == null) { logger?.LogWarning("HelperLanguagesFormatted transDict is null"); return errorString; } try { key = key.Trim(); lang = lang.Trim(); return transDict[key].GetTrans(lang); } catch (Exception arg) { logger?.LogError($"HelperLanguagesFormatted key {key} lang {lang}, error: {arg}"); return errorString; } } } public class HelperLog : ILoggerWithConsole, ILogger { protected readonly string path; protected const string baseLogName = "interactive_mod_log.txt"; public bool isLogConsole = true; public bool isLogFile = true; public ConsoleColor infoColor = ConsoleColor.White; public ConsoleColor warningColor = ConsoleColor.Yellow; public ConsoleColor errorColor = ConsoleColor.Red; protected virtual string LogTime => DateTime.Now.ToString("h:mm:ss:fff"); public HelperLog(string logDir) : this(logDir, "interactive_mod_log.txt") { } public HelperLog(string logDir, string logFname) { if (logDir == "" || Directory.Exists(logDir)) { path = Path.Combine(logDir, logFname); } } protected virtual void WriteToFile(string s) { if (path != null) { using (StreamWriter streamWriter = new StreamWriter(path, append: true, Encoding.UTF8)) { streamWriter.WriteLine(s); } } } public virtual void Log(object obj) { if (isLogFile) { WriteToFile($"{LogTime} {obj}"); } } public virtual void LogConsole(string text, string prefix, ConsoleColor prefixColor) { Console.ForegroundColor = prefixColor; Console.Write(LogTime + " " + prefix + " "); Console.ResetColor(); Console.WriteLine(text); } protected virtual void LogWithConsole(object obj, string prefix, ConsoleColor color) { Log($"{prefix} {obj}"); if (isLogConsole) { LogConsole(obj.ToString(), prefix, color); } } public virtual void LogInfo(object obj) { LogWithConsole(obj, "[INFO]", infoColor); } public virtual void LogWarning(object obj) { LogWithConsole(obj, "[WARNING]", warningColor); } public virtual void LogError(object obj) { LogWithConsole(obj, "[ERROR]", errorColor); } public virtual void LogInfoConsoleOnly(object obj) { if (isLogConsole) { LogConsole(obj.ToString(), "[INFO]", infoColor); } } public virtual void LogWarningConsoleOnly(object obj) { if (isLogConsole) { LogConsole(obj.ToString(), "[WARNING]", warningColor); } } public virtual void LogErrorConsoleOnly(object obj) { if (isLogConsole) { LogConsole(obj.ToString(), "[ERROR]", errorColor); } } public virtual void LogInfoFileOnly(object obj) { Log($"[INFO] {obj}"); } public virtual void LogWarningFileOnly(object obj) { Log($"[WARNING] {obj}"); } public virtual void LogErrorFileOnly(object obj) { Log($"[ERROR] {obj}"); } public virtual void ClearLog(Action onError) { if (path == null) { return; } try { using (new StreamWriter(path)) { } } catch (Exception arg) { onError?.Invoke($"ClearLog error: {arg}"); } } public virtual void ClearLogBySize(int fileSizeInBytes, Action onError) { try { FileInfo fileInfo = new FileInfo(path); if (fileInfo.Exists && fileInfo.Length > fileSizeInBytes) { ClearLog(onError); } } catch (Exception arg) { onError?.Invoke($"ClearLogBySize error: {arg}"); } } } public class HelperParse : IParseManager { private readonly ILogger logger; public HelperParse(ILogger logger = null) { this.logger = logger; } public Type GetEnumUnderlyingType(Type type) { if (!type.IsEnum) { return type; } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (fields != null && fields.Length >= 1) { return fields[0].FieldType; } return type; } public bool TryParseEnum(string arg, out TEnum result, TEnum defaultValue = default(TEnum)) { Type typeFromHandle = typeof(TEnum); try { Type enumUnderlyingType = GetEnumUnderlyingType(typeFromHandle); if (enumUnderlyingType == typeof(int)) { if (int.TryParse(arg, out var result2) && Enum.IsDefined(typeFromHandle, result2)) { result = (TEnum)Enum.ToObject(typeFromHandle, result2); return true; } logger?.LogWarning("TryParseEnum can't parse int from " + arg + " for enum " + typeFromHandle.Name); } else if (enumUnderlyingType == typeof(sbyte)) { if (sbyte.TryParse(arg, out var result3) && Enum.IsDefined(typeFromHandle, result3)) { result = (TEnum)Enum.ToObject(typeFromHandle, result3); return true; } logger?.LogWarning("TryParseEnum can't parse sbyte from " + arg + " for enum " + typeFromHandle.Name); } else { foreach (TEnum value in Enum.GetValues(typeFromHandle)) { if (Enum.GetName(typeFromHandle, value).Equals(arg, StringComparison.OrdinalIgnoreCase)) { result = value; return true; } } logger?.LogWarning("TryParseEnum can't find item by direct name " + arg + " for enum " + typeFromHandle.Name); } } catch (Exception arg2) { logger?.LogError($"TryParseEnum error happened when process {arg} for enum {typeFromHandle.Name}: {arg2}"); } result = defaultValue; return false; } public bool TryParseBool(string arg, out bool result, bool defaultValue = false) { if (bool.TryParse(arg, out result)) { return true; } if (int.TryParse(arg, out var result2)) { result = result2 != 0; return true; } logger?.LogWarning($"TryParseBool error happened when process {arg}. Will be used default {defaultValue}"); result = defaultValue; return false; } } public class JsonUtils { private static Func customGenerator; public static IJsonUtils Generate(IDebugLogger logger) { if (customGenerator == null) { return new HelperJson(logger, File.Exists(".mod_json_debug")); } return customGenerator(); } public static void RegisterCustomGenerator(Func generator) { customGenerator = generator; } } public class SettingsUtils { public static string CommandUpdateSettingsID => "command_update_parameters"; public static string BaseFolder { get; set; } = "TokControl_BattleGolf_InteractiveModData"; public static string SettingsFileName => "settings.txt"; public static string SettingsPath => Path.Combine(BaseFolder, SettingsFileName); public static T LoadSettings(IDebugLogger logger) where T : class { string text = string.Empty; try { text = SettingsPath; T result = JsonUtils.Generate(logger).FromFile(text); logger?.LogInfo("settings loaded"); return result; } catch (FileNotFoundException) { logger?.LogWarning("can't load settings. File not found at " + text + ". Using standard."); } catch (Exception arg) { logger?.LogError($"can't load settings. Using standard. \n{arg}"); } T result2 = null; try { result2 = Activator.CreateInstance(); return result2; } catch (Exception arg2) { logger?.LogError($"can't create default data class. \n{arg2}"); } return result2; } } public enum Languages { ru, en } public class Text { public string ru; public string en; public Text(string ru, string en) { this.ru = ru; this.en = en; } public Text() { } public string GetTrans(Languages lang) { if (lang != Languages.en) { return ru; } return en; } public string GetTrans(string lang) { if (GetLangFromAppLang(lang) != Languages.en) { return ru; } return en; } private Languages GetLangFromAppLang(string lang) { lang = lang?.Trim()?.ToLower(); switch (lang) { default: return Languages.en; case "ru": case "russian": case "rus": case "русский": case "ру": case "рус": return Languages.ru; } } } public class HelperLanguages : ITranslation { private readonly Dictionary transDict; private readonly ILogger logger; private readonly string errorString = "trans_error"; public HelperLanguages(ILogger logger, string langsDir, string langsFname = "langs.data") { this.logger = logger; try { string path = Path.Combine(langsDir, langsFname); IDictManager dictManager = new HelperDictManager(logger); using (StreamReader streamReader = new StreamReader(path)) { string data = streamReader.ReadToEnd(); Dictionary source = dictManager.ParseDict(data); transDict = GetTransDict(source); } logger?.LogInfo($"HelperLanguages: found translates {transDict.Count}"); } catch (Exception arg) { logger?.LogError($"HelperLanguages load: {arg}"); transDict = new Dictionary(StringComparer.OrdinalIgnoreCase); } } protected Dictionary GetTransDict(Dictionary source) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item in source) { string[] array = item.Value.Split(new string[1] { "%lang%" }, StringSplitOptions.RemoveEmptyEntries); if (array == null || array.Length != 2) { logger?.LogError("HelperLanguages parse item: " + item.Key); continue; } string ru = array[0]; string en = array[1]; dictionary.Add(item.Key, new Text(ru, en)); } return dictionary; } public string GetTrans(string key, Languages lang) { return GetTrans(key, lang.ToString()); } public string GetTrans(string key, string lang) { try { key = key.Trim(); lang = lang.Trim(); return transDict[key].GetTrans(lang); } catch (Exception arg) { logger?.LogError($"HelperLanguages key {key} lang {lang}: {arg}"); return errorString; } } public virtual void InitFromFile(string path) { throw new NotImplementedException(); } public void InitFromString(string data) { throw new NotImplementedException(); } } } namespace ModHelper.Wrappers { public class LimitedLogWrapper : IDebugLogger, ILoggerWithConsole, ILogger { private readonly IDebugLogger logger; private int messagesLimit; public LimitedLogWrapper(int messagesLimit, IDebugLogger logger) { this.logger = logger; this.messagesLimit = messagesLimit; } public void Log(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.Log(obj); } } public void LogDebugInfo(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogDebugInfo(obj); } } public void LogDebugWarning(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogDebugWarning(obj); } } public void LogDebugError(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogDebugError(obj); } } public void LogInfo(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogInfo(obj); } } public void LogInfoConsoleOnly(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogInfoConsoleOnly(obj); } } public void LogInfoFileOnly(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogInfoFileOnly(obj); } } public void LogWarning(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogWarning(obj); } } public void LogWarningConsoleOnly(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogWarningConsoleOnly(obj); } } public void LogWarningFileOnly(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogWarningFileOnly(obj); } } public void LogError(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogError(obj); } } public void LogErrorConsoleOnly(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogErrorConsoleOnly(obj); } } public void LogErrorFileOnly(object obj) { if (messagesLimit > 0) { messagesLimit--; logger.LogErrorFileOnly(obj); } } public void UpdateLimit(int limit) { messagesLimit = limit; } public void ClearLog(Action onError) { logger.ClearLog(onError); } public void ClearLogBySize(int fileSizeInBytes, Action onError) { logger.ClearLogBySize(fileSizeInBytes, onError); } } } namespace ModHelper.Interfaces { public interface ICommandTargets { bool IsAllPlayers { get; } } public interface IConsoleCommandWithData : IConsoleCommand { bool Execute(IEnumerable args, IEventsData data); } public interface IConsoleCommand { string Id { get; } bool Execute(IEnumerable args); bool IsValidCommandArgs(IEnumerable args); } public interface IDebugLogger : ILoggerWithConsole, ILogger { void LogDebugInfo(object obj); void LogDebugWarning(object obj); void LogDebugError(object obj); } public interface ILoggerWithConsole : ILogger { void LogInfoConsoleOnly(object obj); void LogWarningConsoleOnly(object obj); void LogErrorConsoleOnly(object obj); void LogInfoFileOnly(object obj); void LogWarningFileOnly(object obj); void LogErrorFileOnly(object obj); } public interface ICommandNotify { string ProcessMsg(string msg); } public interface ICommand { string Id { get; } } public interface IDictionaryItem { string Key { get; } T Value { get; } } public interface IJsonUtils { T FromFile(string path) where T : class; T FromString(string s) where T : class; } public interface IDictManager { Dictionary ParseDict(string data); } public interface ICommandManager { string GetCommandData(string eventKey, bool isLogError = false); string[] GetCommands(); void InitFromString(string data); void InitFromFile(string path); } public interface IEventManager { CustomEvent GetEvent(string eventID, bool isLogError = false); IEnumerable GetEventsCollection(); } public interface IEventWithData : IEvent { bool Execute(IEventsData data); } public interface IEvent { string Id { get; } bool Execute(); } public interface ILogger { void Log(object obj); void LogInfo(object obj); void LogWarning(object obj); void LogError(object obj); void ClearLog(Action onError); void ClearLogBySize(int fileSizeInBytes, Action onError); } public interface ITranslation { string GetTrans(string key, Languages lang); string GetTrans(string key, string lang); void InitFromString(string data); void InitFromFile(string path); } public interface IParseManager { bool TryParseEnum(string arg, out TEnum result, TEnum defaultValue = default(TEnum)); bool TryParseBool(string arg, out bool result, bool defaultValue = false); } }