Decompiled source of SailRange v0.5.0

SailRange.dll

Decompiled a month ago
using System;
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.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.ModCore.API;
using Microsoft.CodeAnalysis;
using SailRange.Core;
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("SailRange")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0+e7226e273c5e1399ea5d29d9165606bea5783ccf")]
[assembly: AssemblyProduct("SailRange")]
[assembly: AssemblyTitle("SailRange")]
[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 SailRange
{
	[BepInPlugin("jg224.BoatRadiusGuard", "SailRange", "0.5.0")]
	[BepInProcess("valheim.exe")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.BoatRadiusGuard";

		public const string PluginName = "SailRange";

		public const string PluginVersion = "0.5.0";

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

		public const string ConfigFileName = "jg224.BoatRadiusGuard.cfg";

		public const int ProtocolVersion = 1;

		public const float MinimumRadius = 10f;

		public const float MaximumRadius = 10000f;

		internal const ModuleSide DeclaredSide = (ModuleSide)1;

		internal const ModuleRequirement DeclaredRequirement = (ModuleRequirement)1;

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

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

		private Harmony _harmony;

		private bool _moduleRegistered;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<float> BoatExploreRadius { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<string> ShipRadii { get; private set; }

		internal static ConfigEntry<float> MinimumTravelDistance { get; private set; }

		internal static ConfigEntry<int> WorkBudget { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before SailRange.");
				}
				Core = ModCoreApi.Services;
				AttachAndBindConfiguration();
				RegisterWithCore();
				_harmony = new Harmony("jg224.BoatRadiusGuard");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Client-local sailing exploration radius ready.");
				Game.isModded = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("SailRange 0.5.0 loaded; local boat exploration radius: " + BoatExploreRadius.Value.ToString("0.##", CultureInfo.InvariantCulture) + "m."));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("SailRange failed to initialize safely: " + ex));
				if (Core != null && _moduleRegistered)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void AttachAndBindConfiguration()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Expected O, but got Unknown
			if (!Directory.Exists(Paths.ConfigPath))
			{
				Directory.CreateDirectory(Paths.ConfigPath);
			}
			string text = Path.Combine(Paths.ConfigPath, "jg224.BoatRadiusGuard.cfg");
			ConfigFile obj = PluginConfigFiles.Attach((BaseUnityPlugin)(object)this, new ConfigFile(text, true, ((BaseUnityPlugin)this).Info.Metadata));
			BoatExploreRadius = obj.Bind<float>("General", "BoatExploreRadius", 150f, new ConfigDescription("Map exploration radius in meters while the local player is aboard a boat.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 10000f), Array.Empty<object>()));
			Enabled = obj.Bind<bool>("General", "Enabled", true, "Enable local sailing exploration changes.");
			ShipRadii = obj.Bind<string>("General", "ShipRadii", "", "Optional prefab=radius overrides in metres, separated by semicolons; e.g. Karve=200;VikingShip=250. Range 10-10000.");
			MinimumTravelDistance = obj.Bind<float>("General", "MinimumTravelDistance", 2f, new ConfigDescription("Skip completed-radius rescans until the boat has moved this many metres. Zero always rescans.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			WorkBudget = obj.Bind<int>("General", "WorkBudget", 16384, new ConfigDescription("Maximum candidate map pixels per frame. Large radii reveal progressively until complete; no radius is discarded.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1024, 262144), Array.Empty<object>()));
		}

		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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Expected O, but got Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: 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("Invalid plugin version.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.BoatRadiusGuard", "SailRange", val, 1, (ModuleSide)1, (ModuleRequirement)1, 0uL, 1, 1)));
			_moduleRegistered = true;
			_registrations.Add(Core.Configuration.Register(new ConfigSettingDescriptor(ModuleId, ((ConfigEntryBase)BoatExploreRadius).Definition.Section, ((ConfigEntryBase)BoatExploreRadius).Definition.Key, (ConfigScope)1, (Func<string>)(() => Convert.ToString(((ConfigEntryBase)BoatExploreRadius).BoxedValue, CultureInfo.InvariantCulture)), 1)));
			ConfigEntryBase[] array = (ConfigEntryBase[])(object)new ConfigEntryBase[4]
			{
				(ConfigEntryBase)Enabled,
				(ConfigEntryBase)ShipRadii,
				(ConfigEntryBase)MinimumTravelDistance,
				(ConfigEntryBase)WorkBudget
			};
			foreach (ConfigEntryBase entry in array)
			{
				_registrations.Add(Core.Configuration.Register(new ConfigSettingDescriptor(ModuleId, entry.Definition.Section, entry.Definition.Key, (ConfigScope)1, (Func<string>)(() => Convert.ToString(entry.BoxedValue, CultureInfo.InvariantCulture)), 1)));
			}
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)9, (Action<LifecycleEvent>)delegate
			{
				MinimapUpdateExplorePatch.Reset();
			}, 0));
			_registrations.Add(Core.Lifecycle.Subscribe(ModuleId, (LifecycleEventKind)3, (Action<LifecycleEvent>)delegate
			{
				MinimapUpdateExplorePatch.Reset();
			}, 0));
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void Shutdown()
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			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();
			_moduleRegistered = false;
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			BoatExploreRadius = null;
			Enabled = null;
			ShipRadii = null;
			MinimumTravelDistance = null;
			WorkBudget = null;
			MinimapUpdateExplorePatch.Reset();
			Log = null;
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdateExplore")]
	internal static class MinimapUpdateExplorePatch
	{
		internal sealed class Scope
		{
			internal RadiusLease Lease;

			internal Vector3 Origin;

			internal float Radius;
		}

		private static readonly FieldRef<Minimap, Texture2D> FogTexture = AccessTools.FieldRefAccess<Minimap, Texture2D>("m_fogTexture");

		private static Func<Minimap, int, int, bool> _explorePixel;

		private static Minimap _map;

		private static Ship _ship;

		private static DiskScan _pending;

		private static Vector3 _origin;

		private static Vector3 _completedOrigin;

		private static float _radius;

		private static float _completedRadius;

		private static bool _completed;

		private static string _raw;

		private static Dictionary<string, float> _radii = ExplorationRules.ParseOverrides("");

		internal static void Reset()
		{
			_pending = null;
			_map = null;
			_ship = null;
			_completed = false;
		}

		[HarmonyPrefix]
		private static bool Prefix(Minimap __instance, Player player, ref float ___m_exploreTimer, out Scope __state)
		{
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			Ship localShip = Ship.GetLocalShip();
			if (Object.op_Implicit((Object)(object)localShip) && Object.op_Implicit((Object)(object)player))
			{
				ConfigEntry<bool> enabled = Plugin.Enabled;
				if (enabled != null && enabled.Value)
				{
					if ((Object)(object)_map != (Object)(object)__instance || (Object)(object)_ship != (Object)(object)localShip)
					{
						Reset();
						_map = __instance;
						_ship = localShip;
					}
					string value = Plugin.ShipRadii.Value;
					if (_raw != value)
					{
						_raw = value;
						_radii = ExplorationRules.ParseOverrides(value);
					}
					string key = ((Object)localShip).name.Replace("(Clone)", "").Trim();
					float value2;
					float num = (_radii.TryGetValue(key, out value2) ? value2 : Plugin.BoatExploreRadius.Value);
					if (!ExplorationRules.ValidRadius(num) || !(__instance.m_pixelSize > 0f))
					{
						return true;
					}
					if (_pending != null && _radius != num)
					{
						_pending = null;
					}
					if (_pending != null)
					{
						Process(__instance);
						return false;
					}
					if (___m_exploreTimer + Time.deltaTime <= __instance.m_exploreInterval)
					{
						return true;
					}
					Vector3 position = ((Component)player).transform.position;
					Vector3 val = position - _completedOrigin;
					val.y = 0f;
					if (!ExplorationRules.NeedsExplore(_completed, ((Vector3)(ref val)).sqrMagnitude, Plugin.MinimumTravelDistance.Value, _completedRadius, num))
					{
						___m_exploreTimer = 0f;
						return false;
					}
					int num2 = Mathf.CeilToInt(num / __instance.m_pixelSize);
					if (ExplorationRules.FitsBudget(num2, Plugin.WorkBudget.Value))
					{
						__state = new Scope
						{
							Lease = new RadiusLease(__instance.m_exploreRadius, num),
							Origin = position,
							Radius = num
						};
						__instance.m_exploreRadius = num;
						return true;
					}
					if (!Object.op_Implicit((Object)(object)FogTexture.Invoke(__instance)))
					{
						return true;
					}
					_origin = position;
					_radius = num;
					int x = Mathf.RoundToInt(position.x / __instance.m_pixelSize + (float)(__instance.m_textureSize / 2));
					int y = Mathf.RoundToInt(position.z / __instance.m_pixelSize + (float)(__instance.m_textureSize / 2));
					_pending = new DiskScan(x, y, num2, __instance.m_textureSize);
					___m_exploreTimer = 0f;
					Process(__instance);
					return false;
				}
			}
			Reset();
			return true;
		}

		[HarmonyPostfix]
		private static void Postfix(Minimap __instance, Scope __state, bool __runOriginal)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (__state != null)
			{
				Restore(__instance, __state);
				if (__runOriginal)
				{
					_completed = true;
					_completedOrigin = __state.Origin;
					_completedRadius = __state.Radius;
				}
			}
		}

		[HarmonyFinalizer]
		private static void Finalizer(Minimap __instance, Scope __state)
		{
			Restore(__instance, __state);
		}

		private static void Restore(Minimap map, Scope scope)
		{
			if (scope != null && scope.Lease.TryRestore(map.m_exploreRadius, out var value))
			{
				map.m_exploreRadius = value;
			}
		}

		private static void Process(Minimap map)
		{
			//IL_00b2: 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)
			if (_explorePixel == null)
			{
				_explorePixel = AccessTools.MethodDelegate<Func<Minimap, int, int, bool>>(AccessTools.Method(typeof(Minimap), "Explore", new Type[2]
				{
					typeof(int),
					typeof(int)
				}, (Type[])null), (object)null, true);
			}
			bool flag = false;
			for (int i = 0; i < Plugin.WorkBudget.Value; i++)
			{
				if (!_pending.Next(out var x, out var y, out var inside))
				{
					break;
				}
				if (inside && _explorePixel(map, x, y))
				{
					flag = true;
				}
			}
			if (flag)
			{
				FogTexture.Invoke(map).Apply();
			}
			if (_pending.Complete)
			{
				_pending = null;
				_completed = true;
				_completedOrigin = _origin;
				_completedRadius = _radius;
			}
		}
	}
}
namespace SailRange.Core
{
	internal static class ExplorationRules
	{
		internal static bool ValidRadius(float radius)
		{
			if (!float.IsNaN(radius) && !float.IsInfinity(radius) && radius >= 10f)
			{
				return radius <= 10000f;
			}
			return false;
		}

		internal static Dictionary<string, float> ParseOverrides(string raw)
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);
			string[] array = (raw ?? "").Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' });
				if (array2.Length == 2 && array2[0].Trim().Length > 0 && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && ValidRadius(result))
				{
					dictionary[array2[0].Trim()] = result;
				}
			}
			return dictionary;
		}

		internal static bool NeedsExplore(bool completed, float movedSquared, float threshold, float previousRadius, float radius)
		{
			if (completed && previousRadius == radius && !(threshold <= 0f))
			{
				return movedSquared >= threshold * threshold;
			}
			return true;
		}

		internal static bool FitsBudget(int radiusPixels, int budget)
		{
			if (radiusPixels >= 0)
			{
				return (2L * (long)radiusPixels + 1) * (2L * (long)radiusPixels + 1) <= budget;
			}
			return false;
		}
	}
	internal sealed class DiskScan
	{
		private readonly int _centerX;

		private readonly int _centerY;

		private readonly int _radius;

		private readonly int _minX;

		private readonly int _maxX;

		private readonly int _maxY;

		private int _x;

		private int _y;

		internal bool Complete
		{
			get
			{
				if (_y <= _maxY)
				{
					return _minX > _maxX;
				}
				return true;
			}
		}

		internal DiskScan(int x, int y, int radius, int textureSize)
		{
			_centerX = x;
			_centerY = y;
			_radius = Math.Max(0, radius);
			_minX = Math.Max(0, x - _radius);
			_maxX = Math.Min(textureSize - 1, x + _radius);
			_x = _minX;
			_y = Math.Max(0, y - _radius);
			_maxY = Math.Min(textureSize - 1, y + _radius);
		}

		internal bool Next(out int x, out int y, out bool inside)
		{
			x = _x;
			y = _y;
			inside = false;
			if (Complete)
			{
				return false;
			}
			long num = (long)x - (long)_centerX;
			long num2 = (long)y - (long)_centerY;
			inside = num * num + num2 * num2 <= (long)_radius * (long)_radius;
			if (++_x > _maxX)
			{
				_x = _minX;
				_y++;
			}
			return true;
		}
	}
	internal sealed class RadiusLease
	{
		private bool _restored;

		internal float Original { get; }

		internal float Applied { get; }

		internal RadiusLease(float original, float applied)
		{
			Original = original;
			Applied = applied;
		}

		internal bool TryRestore(float current, out float value)
		{
			value = current;
			if (_restored)
			{
				return false;
			}
			_restored = true;
			if (current != Applied)
			{
				return false;
			}
			value = Original;
			return true;
		}
	}
}