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 UnifiedAutoSaveInterval v1.0.0
plugins\UnifiedAutoSaveInterval\UnifiedAutoSaveInterval.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using UnityEngine; using UnityEngine.SceneManagement; [assembly: AssemblyCompany("Daishi11")] [assembly: AssemblyTitle("UnifiedAutoSaveInterval")] [assembly: AssemblyDescription("Configurable combat-aware Valheim autosave interval for hosted worlds and local characters")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyProduct("UnifiedAutoSaveInterval")] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("1.0.0.0")] namespace Daishi11.UnifiedAutoSaveInterval; [BepInPlugin("daishi11.valheim.unifiedautosaveinterval", "Unified Auto Save Interval", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "daishi11.valheim.unifiedautosaveinterval"; public const string PluginName = "Unified Auto Save Interval"; public const string PluginVersion = "1.0.0"; private const float CombatScanIntervalSeconds = 0.25f; private static readonly FieldInfo BaseAIField = typeof(Character).GetField("m_baseAI", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private ConfigEntry<int> saveIntervalSeconds; private ConfigEntry<bool> avoidSavingDuringCombat; private ConfigEntry<int> maxCombatDelaySeconds; private ConfigEntry<float> combatClearSeconds; private ConfigEntry<bool> protectAllPlayersWhenHosting; private float combatScanCountdown; private float? lastCombatSeenAt; private bool lastCombatState; private int lastAppliedInterval = -1; private void Awake() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown saveIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("General", "SaveIntervalSeconds", 600, new ConfigDescription("Normal Valheim autosave interval in seconds. 600 = 10 minutes, 480 = 8 minutes, 300 = 5 minutes. On an in-game host this controls the hosted world and the host character. On joining clients it controls that client's local character.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(60, 3600), new object[0])); avoidSavingDuringCombat = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "AvoidSavingDuringCombat", true, "Temporarily postpone an autosave while relevant players are in combat."); maxCombatDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<int>("Combat", "MaxCombatDelaySeconds", 120, new ConfigDescription("Maximum extra seconds a combat can postpone an autosave. With a 600 second normal interval and 120 second delay, a save is forced no later than 720 seconds after the previous save.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 600), new object[0])); combatClearSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Combat", "CombatClearSeconds", 5f, new ConfigDescription("How many seconds combat must remain clear before the normal autosave interval is restored. This prevents target switching from making combat flicker on and off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), new object[0])); protectAllPlayersWhenHosting = ((BaseUnityPlugin)this).Config.Bind<bool>("Combat", "ProtectAllPlayersWhenHosting", true, "When this Valheim instance hosts a world via Start Server, postpone the world autosave if any connected player is being targeted by a creature. Joining clients only protect their own local character save."); saveIntervalSeconds.SettingChanged += OnSettingChanged; avoidSavingDuringCombat.SettingChanged += OnSettingChanged; maxCombatDelaySeconds.SettingChanged += OnSettingChanged; combatClearSeconds.SettingChanged += OnSettingChanged; protectAllPlayersWhenHosting.SettingChanged += OnSettingChanged; SceneManager.sceneLoaded += OnSceneLoaded; ResetCombatState(); RefreshInterval(forceLog: true); } private void Update() { combatScanCountdown -= Time.unscaledDeltaTime; if (!(combatScanCountdown > 0f)) { combatScanCountdown = 0.25f; RefreshInterval(forceLog: false); } } private void OnDestroy() { if (saveIntervalSeconds != null) { saveIntervalSeconds.SettingChanged -= OnSettingChanged; } if (avoidSavingDuringCombat != null) { avoidSavingDuringCombat.SettingChanged -= OnSettingChanged; } if (maxCombatDelaySeconds != null) { maxCombatDelaySeconds.SettingChanged -= OnSettingChanged; } if (combatClearSeconds != null) { combatClearSeconds.SettingChanged -= OnSettingChanged; } if (protectAllPlayersWhenHosting != null) { protectAllPlayersWhenHosting.SettingChanged -= OnSettingChanged; } SceneManager.sceneLoaded -= OnSceneLoaded; } private void OnSettingChanged(object sender, EventArgs args) { lastAppliedInterval = -1; RefreshInterval(forceLog: true); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ResetCombatState(); lastAppliedInterval = -1; RefreshInterval(forceLog: true); } private void ResetCombatState() { combatScanCountdown = 0f; lastCombatSeenAt = null; lastCombatState = false; } private void RefreshInterval(bool forceLog) { bool flag = avoidSavingDuringCombat.Value && maxCombatDelaySeconds.Value > 0 && IsRelevantCombatActive(); int value = saveIntervalSeconds.Value; int num = value; if (flag) { num += maxCombatDelaySeconds.Value; } bool flag2 = lastAppliedInterval != num || Math.Abs(Game.m_saveInterval - (float)num) > 0.01f; if (flag2) { Game.m_saveInterval = num; lastAppliedInterval = num; } if (flag != lastCombatState) { lastCombatState = flag; if (flag) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Combat detected. Autosave may be postponed by up to " + maxCombatDelaySeconds.Value + " seconds; effective maximum interval is " + num + " seconds.")); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Combat clear. Normal autosave interval restored to " + value + " seconds.")); } } else if (forceLog || flag2) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Valheim autosave interval set to " + num + " seconds (" + ((double)num / 60.0).ToString("0.##") + " minutes)" + (flag ? " while combat protection is active." : "."))); } } private bool IsRelevantCombatActive() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { lastCombatSeenAt = null; return false; } bool flag = protectAllPlayersWhenHosting.Value && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); List<Player> list = null; if (flag) { list = Player.GetAllPlayers(); } List<Character> allCharacters = Character.GetAllCharacters(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (allCharacters != null) { for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if ((Object)(object)val == (Object)null) { continue; } BaseAI baseAI = GetBaseAI(val); if ((Object)(object)baseAI == (Object)null) { continue; } Character targetCreature = baseAI.GetTargetCreature(); if ((Object)(object)targetCreature == (Object)null) { continue; } bool flag2 = false; if (flag && list != null) { for (int j = 0; j < list.Count; j++) { Player val2 = list[j]; if ((Object)(object)val2 != (Object)null && object.ReferenceEquals(targetCreature, val2)) { flag2 = true; break; } } } else { flag2 = object.ReferenceEquals(targetCreature, localPlayer); } if (flag2) { lastCombatSeenAt = realtimeSinceStartup; return true; } } } if (!lastCombatSeenAt.HasValue) { return false; } return realtimeSinceStartup - lastCombatSeenAt.Value < combatClearSeconds.Value; } private static BaseAI GetBaseAI(Character character) { if (BaseAIField == null) { return null; } try { object? value = BaseAIField.GetValue(character); return (BaseAI)((value is BaseAI) ? value : null); } catch { return null; } } }