Decompiled source of SoloMapPins v0.2.0

plugins\SoloMapPins.dll

Decompiled 17 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
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("SoloMapPins")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SoloMapPins")]
[assembly: AssemblyTitle("SoloMapPins")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 SoloMapPins
{
	[BepInPlugin("SoloMapPins", "Solo Map Pins", "0.2.0")]
	public sealed class SoloMapPinsPlugin : BaseUnityPlugin
	{
		private struct DictionarySnapshotEntry
		{
			public object? Key;

			public object? Value;
		}

		private static class DiscoverLocationPatch
		{
			public static bool Prefix(object?[] __args)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				if (!_blockFarSharedDiscoveries.Value || !TryGetWorldPoint(__args, out var point))
				{
					if (TryGetWorldPoint(__args, out point))
					{
						UnlockLocation(point, null);
					}
					return true;
				}
				if (IsLocalPlayerNear(point, _fallbackDiscoveryRange.Value))
				{
					return true;
				}
				DebugLog($"Blocked shared location discovery at {point}; local player is not nearby.");
				return false;
			}
		}

		private static class LocationIconsPatch
		{
			public static void Postfix()
			{
				PruneDistantLocationPins();
			}
		}

		private static class LocationPinsUpdatePatch
		{
			public static void Postfix()
			{
				PruneDistantLocationPins();
			}
		}

		public const string PluginGuid = "SoloMapPins";

		public const string PluginName = "Solo Map Pins";

		public const string PluginVersion = "0.2.0";

		private static ConfigEntry<bool> _blockFarSharedDiscoveries;

		private static ConfigEntry<bool> _filterGlobalLocationIcons;

		private static ConfigEntry<float> _fallbackDiscoveryRange;

		private static ConfigEntry<float> _locationIconRevealRange;

		private static ConfigEntry<string> _alwaysKeepLocationNames;

		private static ConfigEntry<float> _duplicatePinRadius;

		private static ConfigEntry<bool> _debugLogging;

		private static SoloMapPinsPlugin _instance;

		private const string UnlockedLocationsKey = "SoloMapPins.UnlockedLocationIcons";

		private Harmony _harmony;

		private void Awake()
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			_instance = this;
			_blockFarSharedDiscoveries = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "BlockFarSharedDiscoveries", false, "Prevents direct location discovery pins from being accepted when the local player is not near the location. Disabled by default because mods like MWL Waystones intentionally reveal distant personal pins.");
			_filterGlobalLocationIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FilterGlobalLocationIcons", true, "Removes synced unique location icons unless the local player is close enough to that location.");
			_fallbackDiscoveryRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "FallbackDiscoveryRange", 100f, "Distance used when guarding direct location discovery responses.");
			_locationIconRevealRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LocationIconRevealRange", 250f, "Unique location icons within this many meters of the local player are allowed to stay on the map.");
			_alwaysKeepLocationNames = ((BaseUnityPlugin)this).Config.Bind<string>("General", "AlwaysKeepLocationNames", "StartTemple,BossStone", "Comma-separated location name fragments that are never removed from the synced location icon list.");
			_duplicatePinRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DuplicatePinRadius", 25f, "Existing location pins within this many meters are treated as the same discovery.");
			_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DebugLogging", false, "Logs blocked or locally-added location pins.");
			_harmony = new Harmony("SoloMapPins");
			PatchIfPresent(typeof(Minimap), "DiscoverLocation", typeof(DiscoverLocationPatch));
			PatchIfPresent(typeof(Game), "RPC_DiscoverLocationResponse", typeof(DiscoverLocationPatch));
			PatchIfPresent(typeof(ZoneSystem), "RPC_LocationIcons", typeof(LocationIconsPatch), postfix: true);
			PatchIfPresent(typeof(Minimap), "UpdateLocationPins", typeof(LocationPinsUpdatePatch), postfix: true);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Solo Map Pins 0.2.0 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void PatchIfPresent(Type targetType, string methodName, Type patchType, bool postfix = false)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			MethodInfo methodInfo = AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not find " + targetType.Name + "." + methodName + "; this Valheim version may have renamed it."));
			}
			else
			{
				HarmonyMethod val = (postfix ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchType, "Prefix", (Type[])null, (Type[])null)));
				HarmonyMethod val2 = ((!postfix) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.Method(patchType, "Postfix", (Type[])null, (Type[])null)));
				_harmony.Patch((MethodBase)methodInfo, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void DebugLog(string message)
		{
			if (_debugLogging.Value)
			{
				((BaseUnityPlugin)_instance).Logger.LogInfo((object)message);
			}
		}

		private static bool IsLocalPlayerNear(Vector3 point, float range)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				return Vector3.Distance(((Component)localPlayer).transform.position, point) <= range;
			}
			return false;
		}

		private static bool ShouldKeepLocationIcon(Vector3 point)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				return Vector3.Distance(((Component)localPlayer).transform.position, point) <= _locationIconRevealRange.Value;
			}
			return false;
		}

		private static string GetLocationKey(Vector3 point)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return $"{Mathf.RoundToInt(point.x)}:{Mathf.RoundToInt(point.z)}";
		}

		private static Dictionary<string, string> GetUnlockedLocations()
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return dictionary;
			}
			Dictionary<string, string> value = Traverse.Create((object)localPlayer).Field("m_customData").GetValue<Dictionary<string, string>>();
			if (value == null || !value.TryGetValue("SoloMapPins.UnlockedLocationIcons", out var value2) || string.IsNullOrWhiteSpace(value2))
			{
				return dictionary;
			}
			string[] array = value2.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Split(new char[1] { '=' }, 2);
				if (array2.Length == 2 && !dictionary.ContainsKey(array2[0]))
				{
					dictionary.Add(array2[0], array2[1]);
				}
			}
			return dictionary;
		}

		private static void SaveUnlockedLocations(Dictionary<string, string> unlocked)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Dictionary<string, string> value = Traverse.Create((object)localPlayer).Field("m_customData").GetValue<Dictionary<string, string>>();
			if (value != null)
			{
				value["SoloMapPins.UnlockedLocationIcons"] = string.Join("|", unlocked.Select<KeyValuePair<string, string>, string>((KeyValuePair<string, string> kvp) => kvp.Key + "=" + kvp.Value).ToArray());
			}
		}

		private static bool IsLocationUnlocked(Vector3 point)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return GetUnlockedLocations().ContainsKey(GetLocationKey(point));
		}

		private static void UnlockLocation(Vector3 point, string? locationName)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, string> unlockedLocations = GetUnlockedLocations();
			string locationKey = GetLocationKey(point);
			if (!unlockedLocations.ContainsKey(locationKey))
			{
				unlockedLocations[locationKey] = locationName ?? string.Empty;
				SaveUnlockedLocations(unlockedLocations);
				DebugLog($"Unlocked personal location icon '{locationName ?? locationKey}' at {point}.");
			}
		}

		private static bool ShouldAlwaysKeepLocationName(object? locationName)
		{
			if (!(locationName is string text) || string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			string[] array = _alwaysKeepLocationNames.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text2 in array)
			{
				if (text.IndexOf(text2.Trim(), StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static List<DictionarySnapshotEntry> GetDictionarySnapshot(object dictionary)
		{
			List<DictionarySnapshotEntry> list = new List<DictionarySnapshotEntry>();
			if (!(dictionary is IEnumerable enumerable))
			{
				return list;
			}
			foreach (object item in enumerable)
			{
				if (item is DictionaryEntry dictionaryEntry)
				{
					list.Add(new DictionarySnapshotEntry
					{
						Key = dictionaryEntry.Key,
						Value = dictionaryEntry.Value
					});
					continue;
				}
				Type type = item.GetType();
				PropertyInfo property = type.GetProperty("Key");
				PropertyInfo property2 = type.GetProperty("Value");
				if (!(property == null) && !(property2 == null))
				{
					list.Add(new DictionarySnapshotEntry
					{
						Key = property.GetValue(item),
						Value = property2.GetValue(item)
					});
				}
			}
			return list;
		}

		private static bool TryGetLocationIconName(Vector3 point, out string? locationName)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			locationName = null;
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			object value = Traverse.Create((object)instance).Field("m_locationIcons").GetValue();
			if (value == null)
			{
				return false;
			}
			foreach (DictionarySnapshotEntry item in GetDictionarySnapshot(value))
			{
				if (item.Key is Vector3 val && val == point)
				{
					locationName = item.Value as string;
					return true;
				}
			}
			return false;
		}

		private static bool ShouldKeepLocationIcon(Vector3 point, string? locationName)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!ShouldAlwaysKeepLocationName(locationName) && !IsLocationUnlocked(point))
			{
				return ShouldKeepLocationIcon(point);
			}
			return true;
		}

		private static bool IsSavedPin(object pin)
		{
			object value = Traverse.Create(pin).Field("m_save").GetValue();
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static void PruneDistantLocationPins()
		{
			//IL_0080: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			if (!_filterGlobalLocationIcons.Value || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			Minimap instance = Minimap.instance;
			if ((Object)(object)instance == (Object)null || !(Traverse.Create((object)instance).Field("m_locationPins").GetValue() is IDictionary dictionary))
			{
				return;
			}
			List<object> list = new List<object>();
			foreach (DictionarySnapshotEntry item in GetDictionarySnapshot(dictionary))
			{
				if (!(item.Key is Vector3 point) || !TryGetLocationIconName(point, out string locationName))
				{
					continue;
				}
				object value = item.Value;
				if (value != null && IsSavedPin(value))
				{
					continue;
				}
				if (ShouldAlwaysKeepLocationName(locationName) || ShouldKeepLocationIcon(point))
				{
					UnlockLocation(point, locationName);
				}
				else if (!IsLocationUnlocked(point))
				{
					if (value != null)
					{
						AccessTools.Method(typeof(Minimap), "RemovePin", new Type[1] { value.GetType() }, (Type[])null)?.Invoke(instance, new object[1] { value });
					}
					list.Add(item.Key);
				}
			}
			foreach (object item2 in list)
			{
				dictionary.Remove(item2);
			}
			if (list.Count > 0)
			{
				DebugLog($"Pruned {list.Count} distant synced location pins.");
			}
		}

		private static bool TryGetWorldPoint(object?[] args, out Vector3 point)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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)
			for (int i = 0; i < args.Length; i++)
			{
				if (args[i] is Vector3 val)
				{
					point = val;
					return true;
				}
			}
			point = default(Vector3);
			return false;
		}
	}
}