Please disclose if your mod was created primarily 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 OdinSaves v1.5.0
OdinSaves.dll
Decompiled a year agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("OdinSaves")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("OdinSaves")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("649fad21-6342-4434-8747-28db2fe311bc")] [assembly: AssemblyFileVersion("1.5.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.5.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace OdinSaves { public static class PluginConfig { public static ConfigFile CurrentConfig { get; private set; } public static ConfigEntry<bool> IsModEnabled { get; private set; } public static ConfigEntry<int> SavePlayerProfileInterval { get; private set; } public static ConfigEntry<bool> SetLogoutPointOnSave { get; private set; } public static ConfigEntry<bool> ShowMessageOnModSave { get; private set; } public static ConfigEntry<bool> EnableMapDataCompression { get; private set; } public static void BindConfig(ConfigFile config) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown CurrentConfig = config; IsModEnabled = config.Bind<bool>("Global", "isModEnabled", true, "Globally enable or disable this mod."); SavePlayerProfileInterval = config.Bind<int>("Global", "savePlayerProfileInterval", 300, new ConfigDescription("Interval (seconds) for how often to save the player profile. Game default (and maximum) is 1200s.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 1200), Array.Empty<object>())); SetLogoutPointOnSave = config.Bind<bool>("Global", "setLogoutPointOnSave", true, "Sets your logout point to your current position when the mod performs a save."); ShowMessageOnModSave = config.Bind<bool>("Global", "saveMessageOnModSave", true, "Show a message (in the middle of your screen) when the mod tries to save."); EnableMapDataCompression = config.Bind<bool>("MapData.Compression", "enableMapDataCompression", false, "Enables the MapData compression feature on the character select screen (restart required)."); } } public static class SaveManager { [CompilerGenerated] private sealed class <CompressProfileMapDataCoroutine>d__14 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public PlayerProfile profile; public FejdStartup fejdStartup; private Selectable[] <selectables>5__2; private long <count>5__3; private TMP_Text <buttonText>5__4; private Dictionary<long, WorldPlayerData>.ValueCollection.Enumerator <>7__wrap4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CompressProfileMapDataCoroutine>d__14(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <selectables>5__2 = null; <buttonText>5__4 = null; <>7__wrap4 = default(Dictionary<long, WorldPlayerData>.ValueCollection.Enumerator); <>1__state = -2; } private bool MoveNext() { try { Selectable[] array; switch (<>1__state) { default: return false; case 0: { <>1__state = -1; <selectables>5__2 = Object.FindObjectsOfType<Selectable>(); array = <selectables>5__2; for (int i = 0; i < array.Length; i++) { array[i].interactable = false; } <count>5__3 = 0L; <buttonText>5__4 = ((Component)_compressMapDataButton).GetComponentInChildren<TMP_Text>(); <>7__wrap4 = profile.m_worldData.Values.GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } if (<>7__wrap4.MoveNext()) { WorldPlayerData current = <>7__wrap4.Current; <buttonText>5__4.text = $"Compressing... {++<count>5__3}/{profile.m_worldData.Count}"; current.m_mapData = CompressMapData(ref current.m_mapData); <>2__current = null; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap4 = default(Dictionary<long, WorldPlayerData>.ValueCollection.Enumerator); array = <selectables>5__2; for (int i = 0; i < array.Length; i++) { array[i].interactable = true; } profile.SavePlayerToDisk(); fejdStartup.UpdateCharacterList(); _mapPackage.Clear(); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap4).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly ZPackage _mapPackage = new ZPackage(); private static GameObject _profileCompressionRoot; private static Button _compressMapDataButton; private static TMP_Text _profileCompressionText; private static byte[] CompressMapData(ref byte[] mapData) { if (mapData == null || IsCompressedMapData(mapData)) { return mapData; } _mapPackage.Clear(); _mapPackage.Write(Minimap.MAPVERSION); _mapPackage.Write(Utils.Compress(mapData)); return _mapPackage.GetArray(); } private static bool IsCompressedMapData(byte[] data) { if (data != null && data.Length >= 4) { return BitConverter.ToInt32(data, 0) >= 7; } return false; } private static bool HasUncompressedData(PlayerProfile profile) { return profile.m_worldData.Values.Any((WorldPlayerData value) => value.m_mapData != null && !IsCompressedMapData(value.m_mapData)); } public static void CreateProfileCompressionUI(FejdStartup fejdStartup) { _profileCompressionRoot = CreateCompressionRoot(fejdStartup.m_selectCharacterPanel.transform); _compressMapDataButton = CreateCompressMapDataButton(fejdStartup, _profileCompressionRoot.transform); _profileCompressionText = CreateProfileCompressionText(fejdStartup, _profileCompressionRoot.transform); } private static GameObject CreateCompressionRoot(Transform parent) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown GameObject val = new GameObject("CompressionRoot", new Type[2] { typeof(RectTransform), typeof(VerticalLayoutGroup) }); val.transform.SetParent(parent); RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0.5f, 0f); component.anchorMax = new Vector2(0.5f, 0f); component.pivot = new Vector2(0.5f, 0f); component.anchoredPosition = new Vector2(410f, 74f); VerticalLayoutGroup component2 = val.GetComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component2).childControlHeight = false; ((HorizontalOrVerticalLayoutGroup)component2).childControlWidth = false; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component2).childForceExpandWidth = false; ((LayoutGroup)component2).childAlignment = (TextAnchor)1; val.SetActive(true); return val; } private static Button CreateCompressMapDataButton(FejdStartup fejdStartup, Transform parent) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown Button obj = Object.Instantiate<Button>(fejdStartup.m_csNewButton, parent); ((UnityEventBase)obj.onClick).RemoveAllListeners(); obj.onClick = new ButtonClickedEvent(); ((Component)obj).GetComponent<RectTransform>().SetSizeWithCurrentAnchors((Axis)0, 200f); TMP_Text componentInChildren = ((Component)obj).GetComponentInChildren<TMP_Text>(); ((Component)componentInChildren).GetComponent<RectTransform>().SetSizeWithCurrentAnchors((Axis)0, 200f); componentInChildren.text = "Compression"; ((Object)((Component)obj).gameObject).name = "CompressMapData.Button"; ((Component)obj).gameObject.SetActive(false); return obj; } private static TMP_Text CreateProfileCompressionText(FejdStartup fejdStartup, Transform parent) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) TMP_Text obj = Object.Instantiate<TMP_Text>(fejdStartup.m_csName, parent); obj.fontSize = 20f; ((Object)((Component)obj).gameObject).name = "ProfileCompression.Text"; obj.text = "Profile Compression Text"; RectTransform component = ((Component)obj).GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.zero; component.pivot = Vector2.zero; component.anchoredPosition = new Vector2(-146f, -89f); component.ForceUpdateRectTransforms(); return obj; } public static void UpdateProfileCompressionUI(FejdStartup fejdStartup) { int profileIndex = fejdStartup.m_profileIndex; if (Object.op_Implicit((Object)(object)_profileCompressionRoot) && profileIndex >= 0 && profileIndex < fejdStartup.m_profiles.Count) { PlayerProfile profile = fejdStartup.m_profiles[profileIndex]; UpdateCompressMapDataButton(fejdStartup, profile); UpdateProfileCompressionText(profile); } } private static void UpdateCompressMapDataButton(FejdStartup fejdStartup, PlayerProfile profile) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown bool flag = HasUncompressedData(profile); ((Component)_compressMapDataButton).GetComponentInChildren<TMP_Text>().text = (flag ? "Compress MapData" : "Compressed!"); ((UnityEventBase)_compressMapDataButton.onClick).RemoveAllListeners(); ((UnityEvent)_compressMapDataButton.onClick).AddListener((UnityAction)delegate { ((MonoBehaviour)fejdStartup).StartCoroutine(CompressProfileMapDataCoroutine(fejdStartup, profile)); }); ((Selectable)_compressMapDataButton).interactable = flag; ((Component)_compressMapDataButton).gameObject.SetActive(true); } private static void UpdateProfileCompressionText(PlayerProfile profile) { float num = profile.m_worldData.Values.Select(delegate(WorldPlayerData value) { byte[] mapData = value.m_mapData; return (mapData != null) ? mapData.Length : 0; }).Sum(); int num2 = profile.m_worldData.Values.Select((WorldPlayerData value) => (value.m_mapData == null || IsCompressedMapData(value.m_mapData)) ? 1 : 0).Sum(); _profileCompressionText.text = string.Format("Worlds: <color={0}>{1}</color>/<color={0}>{2}</color> compressed MapData: <color={0}>{3}</color> KB", "orange", profile.m_worldData.Count, num2, (num / 1024f).ToString("N0")); } [IteratorStateMachine(typeof(<CompressProfileMapDataCoroutine>d__14))] private static IEnumerator CompressProfileMapDataCoroutine(FejdStartup fejdStartup, PlayerProfile profile) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CompressProfileMapDataCoroutine>d__14(0) { fejdStartup = fejdStartup, profile = profile }; } } [BepInPlugin("redseiko.valheim.odinsaves", "OdinSaves", "1.5.0")] public sealed class OdinSaves : BaseUnityPlugin { public const string PluginGuid = "redseiko.valheim.odinsaves"; public const string PluginName = "OdinSaves"; public const string PluginVersion = "1.5.0"; private static ManualLogSource _logger; private void Awake() { _logger = ((BaseUnityPlugin)this).Logger; PluginConfig.BindConfig(((BaseUnityPlugin)this).Config); Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "redseiko.valheim.odinsaves"); } public static void LogInfo(object obj) { _logger.LogInfo((object)$"[{DateTime.Now.ToString(DateTimeFormatInfo.InvariantInfo)}] {obj}"); } } [HarmonyPatch(typeof(FejdStartup))] internal static class FejdStartupPatch { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(FejdStartup __instance) { if (PluginConfig.IsModEnabled.Value && PluginConfig.EnableMapDataCompression.Value) { SaveManager.CreateProfileCompressionUI(__instance); } } [HarmonyPostfix] [HarmonyPatch("UpdateCharacterList")] private static void UpdateCharacterListPostfix(FejdStartup __instance) { if (PluginConfig.IsModEnabled.Value && PluginConfig.EnableMapDataCompression.Value) { SaveManager.UpdateProfileCompressionUI(__instance); } } } [HarmonyPatch(typeof(Game))] internal static class GamePatch { private static float _savePlayerProfileTimer; [HarmonyPostfix] [HarmonyPatch("SavePlayerProfile")] private static void SavePlayerProfilePostfix() { _savePlayerProfileTimer = 0f; } [HarmonyPrefix] [HarmonyPatch("UpdateSaving")] private static void UpdateSavingPrefix(float dt) { _savePlayerProfileTimer += dt; } [HarmonyPostfix] [HarmonyPatch("UpdateSaving")] private static void UpdateSavingPostfix(Game __instance) { if (PluginConfig.IsModEnabled.Value && !(_savePlayerProfileTimer <= 0f) && !(_savePlayerProfileTimer < (float)PluginConfig.SavePlayerProfileInterval.Value)) { if (PluginConfig.ShowMessageOnModSave.Value) { MessageHud.instance.ShowMessage((MessageType)2, "Saving player profile...", 0, (Sprite)null, false); } _savePlayerProfileTimer = 0f; __instance.SavePlayerProfile(PluginConfig.SetLogoutPointOnSave.Value); } } } [HarmonyPatch(typeof(Player))] internal static class PlayerPatch { [HarmonyPostfix] [HarmonyPatch("OnDeath")] private static void OnDeathPostfix(ref Player __instance) { Game.instance.m_playerProfile.ClearLoguoutPoint(); } } }