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 _blockFarSharedDiscoveries; private static ConfigEntry _filterGlobalLocationIcons; private static ConfigEntry _fallbackDiscoveryRange; private static ConfigEntry _locationIconRevealRange; private static ConfigEntry _alwaysKeepLocationNames; private static ConfigEntry _duplicatePinRadius; private static ConfigEntry _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("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, "Removes synced unique location icons 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(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 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(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 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 GetDictionarySnapshot(object dictionary) { List list = new List(); 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 list = new List(); 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; } } }