using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; 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.1")] public sealed class SoloMapPinsPlugin : BaseUnityPlugin { private struct PendingLocation { public Vector3 Point; public string Name; } 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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)) { UnlockLocation(point, null); return true; } DebugLog($"Blocked shared location discovery at {point}; local player is not nearby."); return false; } } private static class LocationIconsPatch { public static void Postfix() { PendingLocations.Clear(); _nextPendingLocationCheck = 0f; ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance != (Object)null) { Dictionary dictionary = new Dictionary(); instance.GetLocationIcons(dictionary); } if ((Object)(object)Minimap.instance != (Object)null) { Traverse.Create((object)Minimap.instance).Field("m_updateLocationsTimer").SetValue((object)0f); } } } private static class GetLocationIconsPatch { public static void Postfix(Dictionary __0) { FilterLocationIcons(__0); } } public const string PluginGuid = "SoloMapPins"; public const string PluginName = "Solo Map Pins"; public const string PluginVersion = "0.2.1"; private static ConfigEntry _blockFarSharedDiscoveries = null; private static ConfigEntry _filterGlobalLocationIcons = null; private static ConfigEntry _fallbackDiscoveryRange = null; private static ConfigEntry _locationIconRevealRange = null; private static ConfigEntry _alwaysKeepLocationNames = null; private static ConfigEntry _duplicatePinRadius = null; private static ConfigEntry _debugLogging = null; private static SoloMapPinsPlugin _instance = null; private const string UnlockedLocationsKey = "SoloMapPins.UnlockedLocationIcons"; private const float PendingLocationCheckInterval = 1f; private static readonly Dictionary PendingLocations = new Dictionary(); private static float _nextPendingLocationCheck; 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("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("General", "FilterGlobalLocationIcons", true, "Filters synced unique location icons before they are added unless the local player is close enough to that location."); _fallbackDiscoveryRange = ((BaseUnityPlugin)this).Config.Bind("General", "FallbackDiscoveryRange", 100f, "Distance used when guarding direct location discovery responses."); _locationIconRevealRange = ((BaseUnityPlugin)this).Config.Bind("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("General", "AlwaysKeepLocationNames", "StartTemple,BossStone", "Comma-separated location name fragments that are never removed from the synced location icon list."); _duplicatePinRadius = ((BaseUnityPlugin)this).Config.Bind("General", "DuplicatePinRadius", 25f, "Existing location pins within this many meters are treated as the same discovery."); _debugLogging = ((BaseUnityPlugin)this).Config.Bind("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(ZoneSystem), "GetLocationIcons", typeof(GetLocationIconsPatch), postfix: true); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Solo Map Pins 0.2.1 loaded."); } private void Update() { ReevaluatePendingLocations(); } private void OnDestroy() { PendingLocations.Clear(); 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; range = Mathf.Max(0f, range); if ((Object)(object)localPlayer != (Object)null) { return HorizontalDistanceSquared(((Component)localPlayer).transform.position, point) <= range * range; } return false; } private static bool ShouldKeepLocationIcon(Vector3 point) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; float num = Mathf.Max(0f, _locationIconRevealRange.Value); if ((Object)(object)localPlayer != (Object)null) { return HorizontalDistanceSquared(((Component)localPlayer).transform.position, point) <= num * num; } return false; } private static float HorizontalDistanceSquared(Vector3 first, Vector3 second) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) float num = first.x - second.x; float num2 = first.z - second.z; return num * num + num2 * num2; } private static string GetLegacyLocationKey(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 string GetLocationKey(Vector3 point, string? locationName) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(locationName)) { return GetLegacyLocationKey(point); } string s = locationName.Trim().ToLowerInvariant(); string arg = Convert.ToBase64String(Encoding.UTF8.GetBytes(s)).TrimEnd(new char[1] { '=' }).Replace('+', '-') .Replace('/', '_'); return $"v2:{Mathf.RoundToInt(point.x)}:{Mathf.RoundToInt(point.z)}:{arg}"; } private static Dictionary GetUnlockedLocations() { Dictionary dictionary = new Dictionary(); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return dictionary; } Dictionary value = Traverse.Create((object)localPlayer).Field("m_customData").GetValue>(); 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 unlocked) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } Dictionary value = Traverse.Create((object)localPlayer).Field("m_customData").GetValue>(); if (value != null) { value["SoloMapPins.UnlockedLocationIcons"] = string.Join("|", unlocked.Select, string>((KeyValuePair kvp) => kvp.Key + "=" + kvp.Value).ToArray()); } } private static bool IsLocationUnlocked(Dictionary unlocked, Vector3 point, string? locationName) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!unlocked.ContainsKey(GetLegacyLocationKey(point))) { return unlocked.ContainsKey(GetLocationKey(point, locationName)); } return true; } private static bool AddUnlockedLocation(Dictionary unlocked, Vector3 point, string? locationName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) string locationKey = GetLocationKey(point, locationName); if (unlocked.ContainsKey(locationKey)) { return false; } unlocked[locationKey] = locationName ?? string.Empty; DebugLog($"Unlocked personal location icon '{locationName ?? locationKey}' at {point}."); return true; } private static void UnlockLocation(Vector3 point, string? locationName) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) Dictionary unlockedLocations = GetUnlockedLocations(); if (AddUnlockedLocation(unlockedLocations, point, locationName)) { SaveUnlockedLocations(unlockedLocations); } } 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 void FilterLocationIcons(Dictionary icons) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (!_filterGlobalLocationIcons.Value || (Object)(object)Player.m_localPlayer == (Object)null) { PendingLocations.Clear(); return; } Dictionary unlockedLocations = GetUnlockedLocations(); bool flag = false; KeyValuePair[] array = icons.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; string locationKey = GetLocationKey(keyValuePair.Key, keyValuePair.Value); if (ShouldAlwaysKeepLocationName(keyValuePair.Value) || IsLocationUnlocked(unlockedLocations, keyValuePair.Key, keyValuePair.Value)) { PendingLocations.Remove(locationKey); continue; } if (ShouldKeepLocationIcon(keyValuePair.Key)) { flag |= AddUnlockedLocation(unlockedLocations, keyValuePair.Key, keyValuePair.Value); PendingLocations.Remove(locationKey); continue; } icons.Remove(keyValuePair.Key); PendingLocations[locationKey] = new PendingLocation { Point = keyValuePair.Key, Name = keyValuePair.Value }; } if (flag) { SaveUnlockedLocations(unlockedLocations); } } private static void ReevaluatePendingLocations() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (!_filterGlobalLocationIcons.Value || PendingLocations.Count == 0 || (Object)(object)Player.m_localPlayer == (Object)null || Time.unscaledTime < _nextPendingLocationCheck) { return; } _nextPendingLocationCheck = Time.unscaledTime + 1f; Dictionary unlockedLocations = GetUnlockedLocations(); bool flag = false; bool flag2 = false; KeyValuePair[] array = PendingLocations.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; PendingLocation value = keyValuePair.Value; if (ShouldAlwaysKeepLocationName(value.Name) || ShouldKeepLocationIcon(value.Point)) { if (!ShouldAlwaysKeepLocationName(value.Name)) { flag |= AddUnlockedLocation(unlockedLocations, value.Point, value.Name); } PendingLocations.Remove(keyValuePair.Key); flag2 = true; } } if (flag) { SaveUnlockedLocations(unlockedLocations); } if (flag2 && (Object)(object)Minimap.instance != (Object)null) { Traverse.Create((object)Minimap.instance).Field("m_updateLocationsTimer").SetValue((object)0f); DebugLog("Revealed pending synced location icons after the local player entered range."); } } 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; } } }