Decompiled source of MinimumRested v0.5.0

MinimumRested.dll

Decompiled 8 hours ago
using System;
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.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.MinimumRested.Core;
using JG224.MinimumRested.Patches;
using JG224.ModCore.API;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("MinimumRested")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("MinimumRested")]
[assembly: AssemblyTitle("MinimumRested")]
[assembly: AssemblyVersion("0.5.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 JG224.MinimumRested
{
	internal sealed class ConfigurationManagerAttributes
	{
		public string DispName;
	}
	internal sealed class MinimumRestedConfig
	{
		private readonly ConfigFile _file;

		private readonly List<Tuple<ConfigEntryBase, ConfigScope>> _entries = new List<Tuple<ConfigEntryBase, ConfigScope>>();

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<bool> GrantOnLogin { get; }

		internal ConfigEntry<bool> MaintainWhileSitting { get; }

		internal ConfigEntry<bool> DebugLogging { get; }

		private MinimumRestedConfig(ConfigFile file)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			_file = file;
			Enabled = Add<bool>(file.Bind<bool>("Rested", "LevelOneRested", true, new ConfigDescription("Enable four-minute Rested with normal bonuses. Grant on login or after 10 continuous seconds sitting under cover. Longer Rested effects are never shortened. The legacy setting key is preserved.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					DispName = "Enable Minimum Rested (4 minutes)"
				}
			})), (ConfigScope)1);
			GrantOnLogin = Add<bool>(file.Bind<bool>("Rested", "GrantOnLogin", true, "Grant four minutes of normal Rested on world entry when the master switch is enabled."), (ConfigScope)1);
			MaintainWhileSitting = Add<bool>(file.Bind<bool>("Rested", "MaintainWhileSitting", true, "After 10 continuous seconds sitting under cover, maintain at least four minutes of Rested until you stand or leave cover."), (ConfigScope)1);
			DebugLogging = Add<bool>(file.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable verbose MinimumRested diagnostics."), (ConfigScope)5);
		}

		internal static MinimumRestedConfig Load(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			string text = LegacyMinimumRestedConfigRules.FindImportSource(Directory.GetFiles(configDirectory, "*.cfg", SearchOption.TopDirectoryOnly));
			MinimumRestedConfig minimumRestedConfig = new MinimumRestedConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.MinimumRested.cfg"), true, plugin.Info.Metadata)));
			if (text != null)
			{
				minimumRestedConfig.ImportLegacy(text, log);
			}
			return minimumRestedConfig;
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			foreach (Tuple<ConfigEntryBase, ConfigScope> pair in _entries)
			{
				yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func<string>)(() => Convert.ToString(pair.Item1.BoxedValue, CultureInfo.InvariantCulture)), 1));
			}
		}

		private void ImportLegacy(string path, ManualLogSource log)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			try
			{
				ConfigFile val = new ConfigFile(path, false);
				Enabled.Value = val.Bind<bool>("Rested", "LevelOneRested", Enabled.Value, (ConfigDescription)null).Value;
				_file.Save();
				if (log != null)
				{
					log.LogInfo((object)("Imported Minimum Rested settings from " + Path.GetFileName(path) + "."));
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.LogWarning((object)("Could not import GeneralTweaks Minimum Rested settings; using defaults. " + ex.Message));
				}
			}
		}

		private ConfigEntry<T> Add<T>(ConfigEntry<T> entry, ConfigScope scope)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_entries.Add(Tuple.Create<ConfigEntryBase, ConfigScope>((ConfigEntryBase)(object)entry, scope));
			return entry;
		}
	}
	[BepInPlugin("jg224.MinimumRested", "MinimumRested", "0.5.0")]
	[BepInProcess("valheim.exe")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.MinimumRested";

		public const string PluginName = "MinimumRested";

		public const string PluginVersion = "0.5.0";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const string GeneralTweaksGuid = "JG224.GeneralTweaks";

		public const int ProtocolVersion = 1;

		public static readonly ModuleId ModuleId = new ModuleId("minimumrested");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private bool _active;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static MinimumRestedConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = MinimumRestedConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before MinimumRested.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains Minimum Rested; MinimumRested is paused to prevent duplicate grants.";
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)3, text);
					((BaseUnityPlugin)this).Logger.LogWarning((object)(text + " Update GeneralTweaks to 3.0.0 or newer."));
					return;
				}
				_harmony = new Harmony("jg224.MinimumRested");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				_active = true;
				Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Client-local minimum Rested maintenance ready.");
				Game.isModded = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("MinimumRested 0.5.0 loaded; protocol " + 1 + "."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("MinimumRested failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void Update()
		{
			if (_active)
			{
				MinimumRestedRuntime.Tick(Time.timeAsDouble);
			}
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.0", ref val))
			{
				throw new InvalidOperationException("MinimumRested has invalid release version metadata.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.MinimumRested", "MinimumRested", val, 1, (ModuleSide)1, (ModuleRequirement)1, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				MinimumRestedRuntime.Reset();
			}, 0));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				MinimumRestedRuntime.Reset();
			}, 0));
		}

		private static bool TryGetLegacyOwner(out Version version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("JG224.GeneralTweaks", out var value))
			{
				return false;
			}
			version = value.Metadata.Version;
			if (version != null)
			{
				return version.CompareTo(new Version(3, 0, 0)) < 0;
			}
			return false;
		}

		private void Shutdown()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			_active = false;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			MinimumRestedRuntime.Reset();
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
		}

		internal static void Debug(string message)
		{
			MinimumRestedConfig settings = Settings;
			if (settings != null && settings.DebugLogging.Value)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)message);
				}
			}
		}
	}
}
namespace JG224.MinimumRested.Patches
{
	internal static class MinimumRestedRuntime
	{
		private const double CheckIntervalSeconds = 0.5;

		private static double _nextCheck;

		private static Player _comfortOverridePlayer;

		private static readonly CoveredSittingTimer Sitting = new CoveredSittingTimer();

		private static readonly RestedSessionState Session = new RestedSessionState();

		private static readonly FieldRef<Player, bool> Loading = AccessTools.FieldRefAccess<Player, bool>("m_isLoading");

		internal static void Tick(double now)
		{
			MinimumRestedConfig settings = Plugin.Settings;
			if (settings == null || !settings.Enabled.Value)
			{
				ResetPlayer();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				ResetPlayer();
				return;
			}
			int instanceID = ((Object)localPlayer).GetInstanceID();
			if (Session.ObservePlayer(instanceID))
			{
				_nextCheck = 0.0;
				Sitting.Reset();
			}
			bool flag = !Loading.Invoke(localPlayer) && !((Character)localPlayer).IsDead() && ((Character)localPlayer).GetHealth() > 0f && !((Character)localPlayer).IsTeleporting();
			bool flag2 = Sitting.Observe(now, flag && Plugin.Settings.MaintainWhileSitting.Value && ((Character)localPlayer).IsSitting() && localPlayer.InShelter());
			if (!flag || now < _nextCheck)
			{
				return;
			}
			_nextCheck = now + 0.5;
			bool flag3 = Session.LoginPending(Plugin.Settings.GrantOnLogin.Value);
			if ((flag3 || flag2) && TryEnsureMinimum(localPlayer, out var changed))
			{
				if (flag3)
				{
					Session.ConsumeLogin();
				}
				if (changed)
				{
					Plugin.Debug("Applied the four-minute Rested minimum" + (flag3 ? " on world entry." : " while sitting under cover."));
				}
			}
		}

		internal static bool TryOverrideComfort(Player player, out int comfort)
		{
			comfort = 1;
			if (Object.op_Implicit((Object)(object)player))
			{
				return (Object)(object)player == (Object)(object)_comfortOverridePlayer;
			}
			return false;
		}

		internal static void Reset()
		{
			Session.Reset();
			ResetPlayer();
			_comfortOverridePlayer = null;
		}

		private static void ResetPlayer()
		{
			Session.ObservePlayer(0);
			_nextCheck = 0.0;
			Sitting.Reset();
		}

		private static bool TryEnsureMinimum(Player player, out bool changed)
		{
			changed = false;
			if (!Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				return false;
			}
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan == null)
			{
				return false;
			}
			StatusEffect statusEffect = sEMan.GetStatusEffect(SEMan.s_statusEffectRested);
			if (Object.op_Implicit((Object)(object)statusEffect))
			{
				SE_Rested val = (SE_Rested)(object)((statusEffect is SE_Rested) ? statusEffect : null);
				if (!Object.op_Implicit((Object)(object)val))
				{
					return true;
				}
				float remaningTime = ((StatusEffect)val).GetRemaningTime();
				if (!MinimumRestedRules.NeedsTopUp(remaningTime, 240f))
				{
					return true;
				}
				((StatusEffect)val).m_ttl = MinimumRestedRules.ExtendTtl(((StatusEffect)val).m_ttl, remaningTime);
				changed = true;
				return true;
			}
			_comfortOverridePlayer = player;
			try
			{
				StatusEffect val2 = sEMan.AddStatusEffect(SEMan.s_statusEffectRested, false, 0, 0f, (short)(-1));
				if (Object.op_Implicit((Object)(object)val2))
				{
					val2.m_ttl = 240f;
				}
				changed = Object.op_Implicit((Object)(object)val2);
				return Object.op_Implicit((Object)(object)val2) || Object.op_Implicit((Object)(object)sEMan.GetStatusEffect(SEMan.s_statusEffectRested));
			}
			finally
			{
				_comfortOverridePlayer = null;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetComfortLevel")]
	internal static class MinimumRestedComfortPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Player __instance, ref int __result)
		{
			if (!MinimumRestedRuntime.TryOverrideComfort(__instance, out var comfort))
			{
				return true;
			}
			__result = comfort;
			return false;
		}
	}
}
namespace JG224.MinimumRested.Core
{
	internal static class MinimumRestedRules
	{
		internal const float DurationSeconds = 240f;

		internal const double SittingDelaySeconds = 10.0;

		internal static bool ShouldMaintain(bool worldEntryPending, bool isSitting, bool isUnderCover)
		{
			if (!worldEntryPending)
			{
				return isSitting && isUnderCover;
			}
			return true;
		}

		internal static bool NeedsTopUp(float remainingSeconds, float minimumSeconds)
		{
			if (!IsFinite(minimumSeconds) || minimumSeconds <= 0f)
			{
				return false;
			}
			if (IsFinite(remainingSeconds))
			{
				return remainingSeconds + 0.1f < minimumSeconds;
			}
			return true;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		internal static float ExtendTtl(float ttl, float remaining)
		{
			if (!IsFinite(ttl) || !IsFinite(remaining))
			{
				return 240f;
			}
			if (!(remaining >= 240f))
			{
				return ttl + 240f - remaining;
			}
			return ttl;
		}
	}
	internal sealed class CoveredSittingTimer
	{
		private double? _started;

		internal bool Observe(double now, bool eligible)
		{
			if (!eligible || double.IsNaN(now) || double.IsInfinity(now))
			{
				Reset();
				return false;
			}
			if (!_started.HasValue || now < _started.Value)
			{
				_started = now;
			}
			return now - _started.Value >= 10.0;
		}

		internal void Reset()
		{
			_started = null;
		}
	}
	internal sealed class RestedSessionState
	{
		private int _playerId;

		private bool _loginConsumed;

		internal bool ObservePlayer(int playerId)
		{
			if (_playerId == playerId)
			{
				return false;
			}
			_playerId = playerId;
			return true;
		}

		internal bool LoginPending(bool enabled)
		{
			if (!enabled)
			{
				_loginConsumed = true;
			}
			return !_loginConsumed;
		}

		internal void ConsumeLogin()
		{
			_loginConsumed = true;
		}

		internal void Reset()
		{
			_playerId = 0;
			_loginConsumed = false;
		}
	}
	internal static class LegacyMinimumRestedConfigRules
	{
		internal const string CurrentFileName = "jg224.MinimumRested.cfg";

		internal const string GeneralTweaksFileName = "jg224.GeneralTweaks.cfg";

		internal const string LegacyGeneralTweaksFileName = "JG224.GeneralTweaks.cfg";

		internal static string FindImportSource(IEnumerable<string> candidatePaths)
		{
			string[] paths = ((candidatePaths == null) ? Array.Empty<string>() : candidatePaths.ToArray());
			if (FindExact(paths, "jg224.MinimumRested.cfg") != null)
			{
				return null;
			}
			return FindExact(paths, "jg224.GeneralTweaks.cfg") ?? FindExact(paths, "JG224.GeneralTweaks.cfg");
		}

		private static string FindExact(IEnumerable<string> paths, string fileName)
		{
			return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
}