Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PinRemoval v1.0.0
PinRemoval.dll
Decompiled 3 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; 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("PinRemoval")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Remove or deduplicate Valheim map pins by name (e.g. AutoMapPins Twitcher spam).")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+fe4f25eca1e3ff1cbdbc77787ceebda1b37c6e40")] [assembly: AssemblyProduct("PinRemoval")] [assembly: AssemblyTitle("PinRemoval")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 PinRemoval { [HarmonyPatch(typeof(Chat), "InputText")] internal static class ChatSlashPatch { [HarmonyPrefix] private static void Prefix(Chat __instance) { Traverse val = Traverse.Create((object)__instance).Field("m_input"); if (val.GetValue() == null) { return; } string value = val.Property("text", (object[])null).GetValue<string>(); if (string.IsNullOrEmpty(value) || value[0] != '/') { return; } string text = value.Substring(1).TrimStart(Array.Empty<char>()); if (text.Length != 0 && text.StartsWith("pinremoval", StringComparison.OrdinalIgnoreCase)) { string text2 = "/" + text; if (text2 != value) { val.Property("text", (object[])null).SetValue((object)text2); } } } } internal static class ConsoleCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; public static ConsoleEvent <>9__1_1; public static ConsoleEvent <>9__1_2; public static ConsoleEvent <>9__1_3; internal void <Register>b__1_0(ConsoleEventArgs args) { Run(args, null); } internal void <Register>b__1_1(ConsoleEventArgs args) { Run(args, PinService.Mode.RemoveAll); } internal void <Register>b__1_2(ConsoleEventArgs args) { Run(args, PinService.Mode.Deduplicate); } internal void <Register>b__1_3(ConsoleEventArgs args) { Run(args, PinService.Mode.Deduplicate); } } private static bool _registered; internal static void Register() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0082: 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_008d: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown if (_registered) { return; } object obj = <>c.<>9__1_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { Run(args, null); }; <>c.<>9__1_0 = val; obj = (object)val; } RegisterCommand("pinremoval", "/pinremoval [remove|duplicate] [names] [radius] — clean map pins by name (defaults from config)", (ConsoleEvent)obj); object obj2 = <>c.<>9__1_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { Run(args, PinService.Mode.RemoveAll); }; <>c.<>9__1_1 = val2; obj2 = (object)val2; } RegisterCommand("pinremoval_remove", "/pinremoval_remove [names] — remove all matching pins", (ConsoleEvent)obj2); object obj3 = <>c.<>9__1_2; if (obj3 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { Run(args, PinService.Mode.Deduplicate); }; <>c.<>9__1_2 = val3; obj3 = (object)val3; } RegisterCommand("pinremoval_duplicate", "/pinremoval_duplicate [names] [radius] — keep one matching pin per radius (default 5m)", (ConsoleEvent)obj3); object obj4 = <>c.<>9__1_3; if (obj4 == null) { ConsoleEvent val4 = delegate(ConsoleEventArgs args) { Run(args, PinService.Mode.Deduplicate); }; <>c.<>9__1_3 = val4; obj4 = (object)val4; } RegisterCommand("pinremoval_dedupe", "/pinremoval_dedupe [names] [radius] — alias for pinremoval_duplicate", (ConsoleEvent)obj4); _registered = true; PinRemovalPlugin.Log.LogInfo((object)"Registered commands: /pinremoval_remove, /pinremoval_duplicate (aliases: pinremoval, pinremoval_dedupe)"); } private static void RegisterCommand(string name, string description, ConsoleEvent action) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand(name, description, action, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static void Run(ConsoleEventArgs args, PinService.Mode? preferredMode) { string[] array = args.Args.Skip(1).ToArray(); PinService.Mode mode = (PinService.Mode)(((int?)preferredMode) ?? ((!ModConfig.IsRemoveAllMode()) ? 1 : 0)); float num = ModConfig.DeduplicateRadiusMeters.Value; List<string> list = ModConfig.GetConfiguredNames().ToList(); int num2 = 0; if (!preferredMode.HasValue && array.Length != 0) { if (string.Equals(array[0], "remove", StringComparison.OrdinalIgnoreCase) || string.Equals(array[0], "removeall", StringComparison.OrdinalIgnoreCase)) { mode = PinService.Mode.RemoveAll; num2 = 1; } else if (IsDuplicateToken(array[0])) { mode = PinService.Mode.Deduplicate; num2 = 1; } } if (array.Length > num2) { if (mode == PinService.Mode.Deduplicate && float.TryParse(array[^1], out var result)) { num = result; string text = string.Join(" ", array.Skip(num2).Take(array.Length - num2 - 1)); if (!string.IsNullOrWhiteSpace(text)) { list = ModConfig.ParseNames(text).ToList(); } } else { string text2 = string.Join(" ", array.Skip(num2)); if (!string.IsNullOrWhiteSpace(text2)) { list = ModConfig.ParseNames(text2).ToList(); } } } if (list.Count == 0) { Tell(args, "PinRemoval: no pin names configured. Set General.PinNames or pass names on the command."); return; } Tell(args, string.Format("PinRemoval: {0} [{1}] radius={2:0.#}m", mode, string.Join(", ", list), num)); PinService.Result result2 = PinService.Process(list, mode, num); Tell(args, $"PinRemoval: matched={result2.Matched} removed={result2.Removed} kept={result2.Kept}"); } private static bool IsDuplicateToken(string token) { if (!string.Equals(token, "duplicate", StringComparison.OrdinalIgnoreCase) && !string.Equals(token, "dedupe", StringComparison.OrdinalIgnoreCase)) { return string.Equals(token, "deduplicate", StringComparison.OrdinalIgnoreCase); } return true; } private static void Tell(ConsoleEventArgs args, string message) { args.Context.AddString(message); MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)1, message, 0, (Sprite)null, false, true); } } } internal static class ModConfig { internal static ConfigEntry<string> PinNames; internal static ConfigEntry<float> DeduplicateRadiusMeters; internal static ConfigEntry<string> DefaultMode; internal static ConfigEntry<bool> MatchContains; internal static ConfigEntry<bool> CaseInsensitive; internal static void Bind(ConfigFile config) { PinNames = config.Bind<string>("General", "PinNames", "Twitcher", "Comma-separated pin name filters. Used by default when a console command omits the name."); DeduplicateRadiusMeters = config.Bind<float>("General", "DeduplicateRadiusMeters", 5f, "When using /pinremoval_duplicate, keep one pin per name within this many meters."); DefaultMode = config.Bind<string>("General", "DefaultMode", "Deduplicate", "Default mode when using pinremoval with no arguments: RemoveAll or Deduplicate."); MatchContains = config.Bind<bool>("General", "MatchContains", true, "If true, pin names that contain a filter match. If false, require an exact name match."); CaseInsensitive = config.Bind<bool>("General", "CaseInsensitive", true, "Ignore case when matching pin names."); } internal static IReadOnlyList<string> GetConfiguredNames() { return ParseNames(PinNames.Value); } internal static IReadOnlyList<string> ParseNames(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return Array.Empty<string>(); } return (from s in raw.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim() into s where s.Length > 0 select s).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList(); } internal static bool IsRemoveAllMode() { return string.Equals(DefaultMode.Value?.Trim(), "RemoveAll", StringComparison.OrdinalIgnoreCase); } } internal static class ModConstants { public const string ModGuid = "Hardwire99.PinRemoval"; public const string ModName = "PinRemoval"; public const string ModVersion = "1.0.0"; } [BepInPlugin("Hardwire99.PinRemoval", "PinRemoval", "1.0.0")] public class PinRemovalPlugin : BaseUnityPlugin { private Harmony _harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); _harmony = new Harmony("Hardwire99.PinRemoval"); _harmony.PatchAll(); try { ConsoleCommands.Register(); } catch (Exception arg) { Log.LogError((object)$"PinRemoval failed to register console commands — chat slash patch remains active. {arg}"); } Log.LogInfo((object)"PinRemoval 1.0.0 loaded. Chat: /pinremoval_remove, /pinremoval_duplicate"); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class PinService { internal enum Mode { RemoveAll, Deduplicate } internal readonly struct Result { public readonly int Matched; public readonly int Removed; public readonly int Kept; public Result(int matched, int removed, int kept) { Matched = matched; Removed = removed; Kept = kept; } } private static readonly FieldRef<Minimap, List<PinData>> PinsRef = AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins"); internal static bool TryGetMinimap(out Minimap minimap) { minimap = Minimap.instance; return (Object)(object)minimap != (Object)null; } internal static Result Process(IReadOnlyList<string> nameFilters, Mode mode, float radiusMeters) { if (!TryGetMinimap(out var minimap)) { PinRemovalPlugin.Log.LogWarning((object)"Minimap is not ready yet. Open the map once or wait until you are in-world."); return new Result(0, 0, 0); } if (nameFilters == null || nameFilters.Count == 0) { PinRemovalPlugin.Log.LogWarning((object)"No pin name filters provided."); return new Result(0, 0, 0); } List<PinData> list = PinsRef.Invoke(minimap); if (list == null || list.Count == 0) { PinRemovalPlugin.Log.LogInfo((object)"No map pins found."); return new Result(0, 0, 0); } List<PinData> list2 = list.Where((PinData p) => p != null && MatchesAny(p.m_name, nameFilters)).ToList(); if (list2.Count == 0) { PinRemovalPlugin.Log.LogInfo((object)("No pins matched filters: " + string.Join(", ", nameFilters))); return new Result(0, 0, 0); } if (mode == Mode.RemoveAll) { return RemoveAll(minimap, list2); } return Deduplicate(minimap, list2, Math.Max(0f, radiusMeters)); } private static Result RemoveAll(Minimap minimap, List<PinData> matched) { int num = 0; foreach (PinData item in matched) { try { minimap.RemovePin(item); num++; } catch (Exception ex) { PinRemovalPlugin.Log.LogWarning((object)("Failed to remove pin '" + item.m_name + "': " + ex.Message)); } } PinRemovalPlugin.Log.LogInfo((object)$"Removed {num} pin(s) (matched {matched.Count})."); return new Result(matched.Count, num, matched.Count - num); } private static Result Deduplicate(Minimap minimap, List<PinData> matched, float radiusMeters) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) float radiusSq = radiusMeters * radiusMeters; int num = 0; int num2 = 0; foreach (IGrouping<string, PinData> item in matched.GroupBy<PinData, string>((PinData p) => (p.m_name ?? string.Empty).Trim(), StringComparer.OrdinalIgnoreCase)) { List<Vector3> list = new List<Vector3>(); foreach (PinData pin in item) { if (list.Any(delegate(Vector3 k) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Vector3 val = k - pin.m_pos; return ((Vector3)(ref val)).sqrMagnitude <= radiusSq; })) { try { minimap.RemovePin(pin); num++; } catch (Exception ex) { PinRemovalPlugin.Log.LogWarning((object)("Failed to remove pin '" + pin.m_name + "': " + ex.Message)); } } else { list.Add(pin.m_pos); num2++; } } } PinRemovalPlugin.Log.LogInfo((object)$"Deduplicated within {radiusMeters:0.#}m: removed {num}, kept {num2} (matched {matched.Count})."); return new Result(matched.Count, num, num2); } private static bool MatchesAny(string pinName, IReadOnlyList<string> filters) { if (string.IsNullOrEmpty(pinName)) { return false; } StringComparison comparisonType = (ModConfig.CaseInsensitive.Value ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal); foreach (string filter in filters) { if (string.IsNullOrEmpty(filter)) { continue; } if (ModConfig.MatchContains.Value) { if (pinName.IndexOf(filter, comparisonType) >= 0) { return true; } } else if (string.Equals(pinName, filter, comparisonType)) { return true; } } return false; } } }