Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RageClanPappyHUDLocal v1.2.0
RageClanPappyHUDLocal.dll
Decompiled a week agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("RageClanPappyHUDLocal")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RageClanPappyHUDLocal")] [assembly: AssemblyTitle("RageClanPappyHUDLocal")] [assembly: AssemblyVersion("1.0.0.0")] namespace RageClanPappyHUDLocal; [BepInPlugin("rageclan.valheim.pappyhudlocal", "Pappy HUD Local Config", "1.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class RageClanPappyHUDLocalPlugin : BaseUnityPlugin { private sealed class PendingSettingChange { public ConfigEntryBase Entry; public float LastChangedAt; } public const string PluginGuid = "rageclan.valheim.pappyhudlocal"; public const string PluginName = "Pappy HUD Local Config"; public const string PluginVersion = "1.2.0"; public const string PappyGuid = "pappy.valheim.hudmaster"; private const long MaxDedicatedLogBytes = 1048576L; private const float ChangeLogDelaySeconds = 0.75f; private ConfigFile localConfig; private bool localFileExisted; private MethodInfo genericBindMethod; private string dedicatedLogPath; private bool dedicatedLogDisabled; private readonly object dedicatedLogLock = new object(); private readonly object pendingChangesLock = new object(); private readonly Dictionary<string, PendingSettingChange> pendingChanges = new Dictionary<string, PendingSettingChange>(); private void Awake() { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown try { InitializeDedicatedLog(); LogInfoBoth("Starting Pappy HUD Local Config v1.2.0."); if (!Chainloader.PluginInfos.TryGetValue("pappy.valheim.hudmaster", out var value) || value == null || (Object)(object)value.Instance == (Object)null) { LogErrorBoth("Pappy HUD Master is not loaded. The local HUD redirect cannot start."); return; } string text = Path.Combine(Application.persistentDataPath, "PappyHUDLocal"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, "hud.cfg"); bool flag = MigrateLegacyLocalConfig(text2); localFileExisted = File.Exists(text2); localConfig = new ConfigFile(text2, true, ((BaseUnityPlugin)this).Info.Metadata); localConfig.SettingChanged += OnLocalSettingChanged; genericBindMethod = FindGenericBindMethod(); if (genericBindMethod == null) { LogErrorBoth("Unable to locate BepInEx ConfigFile.Bind<T>. Local HUD redirect disabled."); return; } LogInfoBoth("Pappy detected: " + value.Metadata.Version?.ToString() + "."); int num = RebindConfigFields(value.Instance); localConfig.Save(); LogInfoBoth("Pappy HUD settings redirected to a per-player local file."); LogInfoBoth("Local HUD config: " + text2); LogInfoBoth("Dedicated log: " + dedicatedLogPath); LogInfoBoth("Redirected config entries: " + num + "."); if (flag) { LogInfoBoth("Legacy RageClan HUD config migrated to the generic PappyHUDLocal directory."); } else { LogInfoBoth(localFileExisted ? "Existing per-player HUD config loaded." : "First run: current Pappy HUD values were copied into the local HUD file."); } } catch (Exception ex) { LogErrorBoth("Failed to redirect Pappy HUD config: " + ex); } } private bool MigrateLegacyLocalConfig(string targetPath) { if (File.Exists(targetPath)) { return false; } string text = Path.Combine(Application.persistentDataPath, "RageClan", "PappyHUDMaster", "hud.cfg"); if (!File.Exists(text)) { return false; } try { File.Copy(text, targetPath, overwrite: false); return true; } catch (Exception ex) { LogWarningBoth("Legacy HUD config migration failed; the original file was left untouched: " + ex.Message); return false; } } private int RebindConfigFields(object pappyInstance) { int num = 0; Type type = pappyInstance.GetType(); BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; FieldInfo[] fields = type.GetFields(bindingAttr); foreach (FieldInfo fieldInfo in fields) { if (typeof(ConfigEntryBase).IsAssignableFrom(fieldInfo.FieldType)) { object? value = fieldInfo.GetValue(pappyInstance); ConfigEntryBase val = (ConfigEntryBase)((value is ConfigEntryBase) ? value : null); if (val != null) { ConfigEntryBase value2 = CloneEntryToLocalFile(val); fieldInfo.SetValue(pappyInstance, value2); num++; } } } FieldInfo field = type.GetField("elements", bindingAttr); IEnumerable enumerable = ((field == null) ? null : (field.GetValue(pappyInstance) as IEnumerable)); if (enumerable == null) { LogWarningBoth("Pappy HUD Master 'elements' collection was not found. Only top-level settings were redirected."); return num; } MethodInfo method = type.GetMethod("SyncEditStrings", BindingFlags.Static | BindingFlags.NonPublic); foreach (object item in enumerable) { if (item == null) { continue; } fields = item.GetType().GetFields(bindingAttr); foreach (FieldInfo fieldInfo2 in fields) { if (typeof(ConfigEntryBase).IsAssignableFrom(fieldInfo2.FieldType)) { object? value3 = fieldInfo2.GetValue(item); ConfigEntryBase val2 = (ConfigEntryBase)((value3 is ConfigEntryBase) ? value3 : null); if (val2 != null) { ConfigEntryBase value4 = CloneEntryToLocalFile(val2); fieldInfo2.SetValue(item, value4); num++; } } } if (method != null) { try { method.Invoke(null, new object[1] { item }); } catch (Exception ex) { LogWarningBoth("Could not refresh one Pappy HUD editor value: " + ex.Message); } } } return num; } private ConfigEntryBase CloneEntryToLocalFile(ConfigEntryBase oldEntry) { MethodInfo methodInfo = genericBindMethod.MakeGenericMethod(oldEntry.SettingType); string text = ((oldEntry.Description == null) ? string.Empty : oldEntry.Description.Description); object? obj = methodInfo.Invoke(localConfig, new object[4] { oldEntry.Definition.Section, oldEntry.Definition.Key, oldEntry.BoxedValue, text }); ConfigEntryBase val = (ConfigEntryBase)((obj is ConfigEntryBase) ? obj : null); if (val == null) { throw new InvalidOperationException("ConfigFile.Bind<T> did not return a ConfigEntryBase."); } LogDebugBoth("Redirected [" + oldEntry.Definition.Section + "] " + oldEntry.Definition.Key + " = " + FormatValue(val.BoxedValue)); return val; } private static MethodInfo FindGenericBindMethod() { MethodInfo[] methods = typeof(ConfigFile).GetMethods(BindingFlags.Instance | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "Bind") && methodInfo.IsGenericMethodDefinition) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 4 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(string) && parameters[3].ParameterType == typeof(string)) { return methodInfo; } } } return null; } private void OnLocalSettingChanged(object sender, SettingChangedEventArgs args) { ConfigEntryBase changedSetting = args.ChangedSetting; if (changedSetting == null) { return; } string key = changedSetting.Definition.Section + "\n" + changedSetting.Definition.Key; lock (pendingChangesLock) { if (!pendingChanges.TryGetValue(key, out var value)) { value = new PendingSettingChange(); pendingChanges[key] = value; } value.Entry = changedSetting; value.LastChangedAt = Time.realtimeSinceStartup; } } private void Update() { FlushPendingSettingLogs(force: false); } private void OnDestroy() { FlushPendingSettingLogs(force: true); if (localConfig != null) { try { localConfig.Save(); } catch (Exception ex) { LogErrorBoth("Final local HUD save failed: " + ex.Message); } } LogInfoBoth("Session ended."); } private void FlushPendingSettingLogs(bool force) { List<string> list = new List<string>(); List<ConfigEntryBase> list2 = new List<ConfigEntryBase>(); float realtimeSinceStartup = Time.realtimeSinceStartup; lock (pendingChangesLock) { foreach (KeyValuePair<string, PendingSettingChange> pendingChange in pendingChanges) { if (force || !(realtimeSinceStartup - pendingChange.Value.LastChangedAt < 0.75f)) { list.Add(pendingChange.Key); list2.Add(pendingChange.Value.Entry); } } foreach (string item in list) { pendingChanges.Remove(item); } } foreach (ConfigEntryBase item2 in list2) { if (item2 != null) { LogDebugBoth("HUD setting saved: [" + item2.Definition.Section + "] " + item2.Definition.Key + " = " + FormatValue(item2.BoxedValue)); } } } private static string FormatValue(object value) { if (value == null) { return "<null>"; } if (value is IFormattable formattable) { return formattable.ToString(null, CultureInfo.InvariantCulture); } return value.ToString(); } private void InitializeDedicatedLog() { try { string text = Path.Combine(Application.persistentDataPath, "PappyHUDLocal"); Directory.CreateDirectory(text); dedicatedLogPath = Path.Combine(text, "PappyHUDLocal.log"); string text2 = Path.Combine(text, "PappyHUDLocal.previous.log"); if (File.Exists(dedicatedLogPath) && new FileInfo(dedicatedLogPath).Length >= 1048576) { if (File.Exists(text2)) { File.Delete(text2); } File.Move(dedicatedLogPath, text2); } WriteDedicatedLogRaw("============================================================"); WriteDedicatedLogRaw("New session - " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } catch (Exception ex) { dedicatedLogDisabled = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Dedicated Pappy HUD local log could not be initialized: " + ex.Message)); } } private void LogInfoBoth(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); WriteDedicatedLog("INFO", message); } private void LogWarningBoth(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); WriteDedicatedLog("WARN", message); } private void LogErrorBoth(string message) { ((BaseUnityPlugin)this).Logger.LogError((object)message); WriteDedicatedLog("ERROR", message); } private void LogDebugBoth(string message) { ((BaseUnityPlugin)this).Logger.LogDebug((object)message); WriteDedicatedLog("DEBUG", message); } private void WriteDedicatedLog(string level, string message) { WriteDedicatedLogRaw(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " [" + level + "] " + message); } private void WriteDedicatedLogRaw(string message) { if (dedicatedLogDisabled || string.IsNullOrEmpty(dedicatedLogPath)) { return; } try { lock (dedicatedLogLock) { File.AppendAllText(dedicatedLogPath, message + Environment.NewLine); } } catch (Exception ex) { dedicatedLogDisabled = true; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Dedicated Pappy HUD local log disabled after write failure: " + ex.Message)); } } }