Please disclose if your mod was created primarily 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 SullysAutoPinner v1.4.0
BepInEx/plugins/SullysAutoPinner.dll
Decompiled 9 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using SimpleJSON; using Splatform; using SullysAutoPinner; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("SullysAutoPinner")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("SullysAutoPinner")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("53eb7460-cf9b-4412-b6f9-0c7191329f58")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.2.0.0")] public class LocalizationManager { private readonly Dictionary<string, string> _translations = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _missingKeys = new HashSet<string>(); private readonly ManualLogSource _logger; private readonly string _langCode; private readonly string _translationsFolder; private const string FallbackLang = "en"; private string _currentLanguage = ""; public LocalizationManager(ManualLogSource logger, string langCode) { _logger = logger; _langCode = (string.IsNullOrWhiteSpace(langCode) ? "en" : langCode.ToLowerInvariant()); _translationsFolder = Path.Combine(Paths.ConfigPath, "SullysAutoPinnerFiles", "Translations"); LoadTranslations("en"); if (_langCode != "en") { LoadTranslations(_langCode); } } public void Reload(string newLanguageCode) { if (!_currentLanguage.Equals(newLanguageCode, StringComparison.OrdinalIgnoreCase)) { _currentLanguage = newLanguageCode; LoadTranslations(newLanguageCode); } } private void LoadTranslations(string lang) { try { string text = Path.Combine(_translationsFolder, lang + ".json"); if (!File.Exists(text)) { _logger.LogWarning((object)("[LocalizationManager] Translation file not found: " + text)); return; } JSONNode jSONNode = JSON.Parse(File.ReadAllText(text)); if (jSONNode == null || !jSONNode.IsObject) { _logger.LogWarning((object)("[LocalizationManager] Failed to parse " + text)); return; } JSONNode.Enumerator enumerator = jSONNode.AsObject.GetEnumerator(); while (enumerator.MoveNext()) { KeyValuePair<string, JSONNode> current = enumerator.Current; _translations[current.Key.ToUpperInvariant()] = current.Value.Value; } _logger.LogWarning((object)$"[LocalizationManager] Loaded {jSONNode.Count} entries from {lang}.json"); } catch (Exception ex) { _logger.LogWarning((object)("[LocalizationManager] Error loading " + lang + ".json: " + ex.Message)); } } public string GetLabel(string key) { if (string.IsNullOrWhiteSpace(key)) { return key; } if (_translations.TryGetValue(key.ToUpperInvariant(), out var value)) { return value; } if (_missingKeys.Add(key)) { _logger.LogWarning((object)("[LocalizationManager] Missing translation for label: " + key)); } return key; } public static void EnsureBaseLanguageFiles(ManualLogSource logger) { string text = Path.Combine(Paths.ConfigPath, "SullysAutoPinnerFiles", "Translations"); Directory.CreateDirectory(text); foreach (KeyValuePair<string, Dictionary<string, string>> defaultTranslation in GetDefaultTranslations()) { string key = defaultTranslation.Key; string path = Path.Combine(text, key + ".json"); if (File.Exists(path)) { continue; } try { JSONObject jSONObject = new JSONObject(); foreach (KeyValuePair<string, string> item in defaultTranslation.Value) { jSONObject[item.Key] = item.Value; } File.WriteAllText(path, jSONObject.ToString(2)); logger.LogWarning((object)("[LocalizationManager] Created default translation file: " + key + ".json")); } catch (Exception ex) { logger.LogWarning((object)("[LocalizationManager] Failed to write " + key + ".json: " + ex.Message)); } } } private static Dictionary<string, Dictionary<string, string>> GetDefaultTranslations() { return new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase) { ["en"] = LocalizationTranslations.EN, ["ru"] = LocalizationTranslations.RU, ["fr"] = LocalizationTranslations.FR, ["de"] = LocalizationTranslations.DE, ["es"] = LocalizationTranslations.ES, ["pl"] = LocalizationTranslations.PL, ["zh"] = LocalizationTranslations.ZH, ["pt"] = LocalizationTranslations.PT }; } } public static class LocalizationTranslations { public static readonly Dictionary<string, string> EN = new Dictionary<string, string> { ["ABOMINATION"] = "Abomination", ["CRYPT"] = "Crypt", ["DRAUGRSPAWNER"] = "Draugr Spawner", ["DVERGERTHINGS"] = "Dvergr Remains", ["DWARFSPAWNER"] = "Greydwarf Nest", ["FIRE"] = "Campfire", ["FLAX"] = "Flax", ["IRON"] = "Iron", ["MUDPILE"] = "Mudpile", ["METEORITE"] = "Meteorite", ["MAGECAPS"] = "Magecaps", ["MARKER"] = "Marker", ["MUSHROOMS"] = "Mushrooms", ["OBSIDIAN"] = "Obsidian", ["SEEDS"] = "Seeds", ["SILVER"] = "Silver", ["SKELETON"] = "Skeleton", ["SMOKEPUFFS"] = "Smoke Puffs", ["TAR"] = "Tar Pit", ["THISTLE"] = "Thistle", ["TOTEM"] = "Fuling Totem", ["TROLLCAVE"] = "Troll Cave", ["TREASURE"] = "Buried Treasure", ["VOLTUREEGG"] = "Vulture Egg", ["YPCONES"] = "Yggdrasil Pine Cones", ["JOTUNNPUFFS"] = "Jotunn Puffs", ["RASPBERRIES"] = "Raspberries", ["BLUEBERRIES"] = "Blueberries", ["CLOUDBERRIES"] = "Cloudberries", ["BARLEY"] = "Barley", ["CLOTH"] = "Cloth", ["SHIPWRECKCHEST"] = "Shipwreck Chest", ["GIANTSWORD"] = "Giant Sword", ["GIANTRIBS"] = "Giant Ribs", ["GIANTSKULL"] = "Giant Skull", ["GIANTBRAIN"] = "Giant Brain", ["EXCAVATION"] = "Excavation Site", ["LANTERN"] = "Lantern", ["ASHLANDPOTGREEN"] = "Ashland Pot (Green)", ["ASHLANDPOTRED"] = "Ashland Pot (Red)", ["SEEKERBRUTE"] = "Seeker Brute", ["BONEMAWSERPENT"] = "Bonemaw Serpent", ["COPPER"] = "Copper", ["DRAGONEGG"] = "Dragon Egg" }; public static readonly Dictionary<string, string> RU = new Dictionary<string, string> { ["ABOMINATION"] = "Мерзость", ["CRYPT"] = "Крипта", ["DRAUGRSPAWNER"] = "Портал драугров", ["DVERGERTHINGS"] = "Останки двергров", ["DWARFSPAWNER"] = "Гнездо серого карлика", ["FIRE"] = "Костер", ["FLAX"] = "Лён", ["IRON"] = "Железо", ["MUDPILE"] = "Грязевая куча", ["METEORITE"] = "Метеорит", ["MAGECAPS"] = "Волшебные шляпки", ["MARKER"] = "Маркер", ["MUSHROOMS"] = "Грибы", ["OBSIDIAN"] = "Обсидиан", ["SEEDS"] = "Семена", ["SILVER"] = "Серебро", ["SKELETON"] = "Скелет", ["SMOKEPUFFS"] = "Клубы дыма", ["TAR"] = "Смоляная яма", ["THISTLE"] = "Чертополох", ["TOTEM"] = "Тотем фулингов", ["TROLLCAVE"] = "Пещера тролля", ["TREASURE"] = "Сокровище", ["VOLTUREEGG"] = "Яйцо стервятника", ["YPCONES"] = "Шишки Иггдрасиля", ["JOTUNNPUFFS"] = "Пух Йотуна", ["RASPBERRIES"] = "Малина", ["BLUEBERRIES"] = "Черника", ["CLOUDBERRIES"] = "Морошка", ["BARLEY"] = "Ячмень", ["CLOTH"] = "Ткань", ["SHIPWRECKCHEST"] = "Сундук кораблекрушения", ["GIANTSWORD"] = "Меч великана", ["GIANTRIBS"] = "Рёбра великана", ["GIANTSKULL"] = "Череп великана", ["GIANTBRAIN"] = "Мозг великана", ["EXCAVATION"] = "Место раскопок", ["LANTERN"] = "Фонарь", ["ASHLANDPOTGREEN"] = "Пот Зелёный", ["ASHLANDPOTRED"] = "Пот Красный", ["SEEKERBRUTE"] = "Ищущий Враг", ["BONEMAWSERPENT"] = "Костеглот", ["COPPER"] = "Медь", ["DRAGONEGG"] = "Яйцо дракона" }; public static readonly Dictionary<string, string> FR = new Dictionary<string, string> { ["ABOMINATION"] = "Abomination", ["CRYPT"] = "Crypte", ["DRAUGRSPAWNER"] = "Portail Draugr", ["DVERGERTHINGS"] = "Restes de Dvergr", ["DWARFSPAWNER"] = "Nid de nain gris", ["FIRE"] = "Feu de camp", ["FLAX"] = "Lin", ["IRON"] = "Fer", ["MUDPILE"] = "Tas de boue", ["METEORITE"] = "Météorite", ["MAGECAPS"] = "Chapeaux de mage", ["MARKER"] = "Marqueur", ["MUSHROOMS"] = "Champignons", ["OBSIDIAN"] = "Obsidienne", ["SEEDS"] = "Graines", ["SILVER"] = "Argent", ["SKELETON"] = "Squelette", ["SMOKEPUFFS"] = "Nuages de fumée", ["TAR"] = "Fosse à goudron", ["THISTLE"] = "Chardon", ["TOTEM"] = "Totem de Fuling", ["TROLLCAVE"] = "Grotte du troll", ["TREASURE"] = "Trésor", ["VOLTUREEGG"] = "Œuf de vautour", ["YPCONES"] = "Cônes d’Yggdrasil", ["JOTUNNPUFFS"] = "Poussières de Jotunn", ["RASPBERRIES"] = "Framboises", ["BLUEBERRIES"] = "Myrtilles", ["CLOUDBERRIES"] = "Plaquebières", ["BARLEY"] = "Orge", ["CLOTH"] = "Tissu", ["SHIPWRECKCHEST"] = "Coffre d'épave", ["GIANTSWORD"] = "Épée de géant", ["GIANTRIBS"] = "Côtes de géant", ["GIANTSKULL"] = "Crâne de géant", ["GIANTBRAIN"] = "Cerveau de géant", ["EXCAVATION"] = "Site d’excavation", ["LANTERN"] = "Lanterne", ["ASHLANDPOTGREEN"] = "Pot des cendres (vert)", ["ASHLANDPOTRED"] = "Pot des cendres (rouge)", ["SEEKERBRUTE"] = "Brute Chercheuse", ["BONEMAWSERPENT"] = "Serpent Mâchoires d’Os", ["COPPER"] = "Cuivre", ["DRAGONEGG"] = "Œuf de dragon" }; public static readonly Dictionary<string, string> DE = new Dictionary<string, string> { ["ABOMINATION"] = "Abscheulichkeit", ["CRYPT"] = "Gruft", ["DRAUGRSPAWNER"] = "Draugr-Portal", ["DVERGERTHINGS"] = "Dvergr-Überreste", ["DWARFSPAWNER"] = "Grauzwergen-Nest", ["FIRE"] = "Lagerfeuer", ["FLAX"] = "Flachs", ["IRON"] = "Eisen", ["MUDPILE"] = "Schlammhaufen", ["METEORITE"] = "Meteorit", ["MAGECAPS"] = "Magierkappen", ["MARKER"] = "Markierung", ["MUSHROOMS"] = "Pilze", ["OBSIDIAN"] = "Obsidian", ["SEEDS"] = "Samen", ["SILVER"] = "Silber", ["SKELETON"] = "Skelett", ["SMOKEPUFFS"] = "Rauchwolken", ["TAR"] = "Teergrube", ["THISTLE"] = "Distel", ["TOTEM"] = "Fuling-Totem", ["TROLLCAVE"] = "Trollhöhle", ["TREASURE"] = "Schatz", ["VOLTUREEGG"] = "Geier-Ei", ["YPCONES"] = "Yggdrasil-Zapfen", ["JOTUNNPUFFS"] = "Jotunnflusen", ["RASPBERRIES"] = "Himbeeren", ["BLUEBERRIES"] = "Blaubeeren", ["CLOUDBERRIES"] = "Moltebeeren", ["BARLEY"] = "Gerste", ["CLOTH"] = "Stoff", ["SHIPWRECKCHEST"] = "Schiffswracktruhe", ["GIANTSWORD"] = "Schwert des Riesen", ["GIANTRIBS"] = "Rippen des Riesen", ["GIANTSKULL"] = "Schädel des Riesen", ["GIANTBRAIN"] = "Gehirn des Riesen", ["EXCAVATION"] = "Ausgrabungsstätte", ["LANTERN"] = "Laterne", ["ASHLANDPOTGREEN"] = "Aschenkrug (Grün)", ["ASHLANDPOTRED"] = "Aschenkrug (Rot)", ["SEEKERBRUTE"] = "Sucher-Brutalo", ["BONEMAWSERPENT"] = "Knochenrachen-Schlange", ["COPPER"] = "Kupfer", ["DRAGONEGG"] = "Drachenei" }; public static readonly Dictionary<string, string> ES = new Dictionary<string, string> { ["ABOMINATION"] = "Abominación", ["CRYPT"] = "Cripta", ["DRAUGRSPAWNER"] = "Generador de Draugr", ["DVERGERTHINGS"] = "Restos de Dvergr", ["DWARFSPAWNER"] = "Nido de enano gris", ["FIRE"] = "Hoguera", ["FLAX"] = "Lino", ["IRON"] = "Hierro", ["MUDPILE"] = "Montón de barro", ["METEORITE"] = "Meteorito", ["MAGECAPS"] = "Sombreros de mago", ["MARKER"] = "Marcador", ["MUSHROOMS"] = "Hongos", ["OBSIDIAN"] = "Obsidiana", ["SEEDS"] = "Semillas", ["SILVER"] = "Plata", ["SKELETON"] = "Esqueleto", ["SMOKEPUFFS"] = "Nubes de humo", ["TAR"] = "Pozo de alquitrán", ["THISTLE"] = "Cardo", ["TOTEM"] = "Tótem de Fuling", ["TROLLCAVE"] = "Cueva del troll", ["TREASURE"] = "Tesoro enterrado", ["VOLTUREEGG"] = "Huevo de buitre", ["YPCONES"] = "Conos de Yggdrasil", ["JOTUNNPUFFS"] = "Pelusas de Jotunn", ["RASPBERRIES"] = "Frambuesas", ["BLUEBERRIES"] = "Arándanos", ["CLOUDBERRIES"] = "Moras árticas", ["BARLEY"] = "Cebada", ["CLOTH"] = "Tela", ["SHIPWRECKCHEST"] = "Cofre del naufragio", ["GIANTSWORD"] = "Espada gigante", ["GIANTRIBS"] = "Costillas de gigante", ["GIANTSKULL"] = "Cráneo de gigante", ["GIANTBRAIN"] = "Cerebro de gigante", ["EXCAVATION"] = "Sitio de excavación", ["LANTERN"] = "Linterna", ["ASHLANDPOTGREEN"] = "Tintero de ceniza (verde)", ["ASHLANDPOTRED"] = "Tintero de ceniza (rojo)", ["SEEKERBRUTE"] = "Bruto Buscador", ["BONEMAWSERPENT"] = "Serpiente Mandíbulas de Hueso", ["COPPER"] = "Cobre", ["DRAGONEGG"] = "Huevo de dragón" }; public static readonly Dictionary<string, string> PL = new Dictionary<string, string> { ["ABOMINATION"] = "Abominacja", ["CRYPT"] = "Krypta", ["DRAUGRSPAWNER"] = "Pojawiacz Draugrów", ["DVERGERTHINGS"] = "Szczątki Dvergrów", ["DWARFSPAWNER"] = "Gniazdo szarego krasnoluda", ["FIRE"] = "Ognisko", ["FLAX"] = "Len", ["IRON"] = "Żelazo", ["MUDPILE"] = "Stos błota", ["METEORITE"] = "Meteoryt", ["MAGECAPS"] = "Kapelusze maga", ["MARKER"] = "Znacznik", ["MUSHROOMS"] = "Grzyby", ["OBSIDIAN"] = "Obsydian", ["SEEDS"] = "Nasiona", ["SILVER"] = "Srebro", ["SKELETON"] = "Szkielet", ["SMOKEPUFFS"] = "Kłęby dymu", ["TAR"] = "Dół smoły", ["THISTLE"] = "Oset", ["TOTEM"] = "Totem Fulingów", ["TROLLCAVE"] = "Jaskinia trolla", ["TREASURE"] = "Zakopany skarb", ["VOLTUREEGG"] = "Jajo sępa", ["YPCONES"] = "Szyszki Yggdrasila", ["JOTUNNPUFFS"] = "Puch Jotunna", ["RASPBERRIES"] = "Maliny", ["BLUEBERRIES"] = "Jagody", ["CLOUDBERRIES"] = "Maliny moroszki", ["BARLEY"] = "Jęczmień", ["CLOTH"] = "Tkanina", ["SHIPWRECKCHEST"] = "Skrzynia z wraku statku", ["GIANTSWORD"] = "Miecz olbrzyma", ["GIANTRIBS"] = "Żebra olbrzyma", ["GIANTSKULL"] = "Czaszka olbrzyma", ["GIANTBRAIN"] = "Mózg olbrzyma", ["EXCAVATION"] = "Miejsce wykopalisk", ["LANTERN"] = "Latarnia", ["ASHLANDPOTGREEN"] = "Popielniczka (zielona)", ["ASHLANDPOTRED"] = "Popielniczka (czerwona)", ["SEEKERBRUTE"] = "Brutalny Poszukiwacz", ["BONEMAWSERPENT"] = "Wąż Kościany", ["COPPER"] = "Miedź", ["DRAGONEGG"] = "Jajo smoka" }; public static readonly Dictionary<string, string> ZH = new Dictionary<string, string> { ["ABOMINATION"] = "憎恶", ["CRYPT"] = "地穴", ["DRAUGRSPAWNER"] = "尸鬼刷怪点", ["DVERGERTHINGS"] = "矮人遗骸", ["DWARFSPAWNER"] = "灰矮人巢穴", ["FIRE"] = "篝火", ["FLAX"] = "亚麻", ["IRON"] = "铁", ["MUDPILE"] = "泥堆", ["METEORITE"] = "陨石", ["MAGECAPS"] = "法师帽", ["MARKER"] = "标记", ["MUSHROOMS"] = "蘑菇", ["OBSIDIAN"] = "黑曜石", ["SEEDS"] = "种子", ["SILVER"] = "银", ["SKELETON"] = "骷髅", ["SMOKEPUFFS"] = "烟雾", ["TAR"] = "焦油坑", ["THISTLE"] = "蓟", ["TOTEM"] = "伏林图腾", ["TROLLCAVE"] = "巨魔洞穴", ["TREASURE"] = "埋藏宝藏", ["VOLTUREEGG"] = "秃鹫蛋", ["YPCONES"] = "世界树松果", ["JOTUNNPUFFS"] = "尤顿之绒", ["RASPBERRIES"] = "覆盆子", ["BLUEBERRIES"] = "蓝莓", ["CLOUDBERRIES"] = "云莓", ["BARLEY"] = "大麦", ["CLOTH"] = "布料", ["SHIPWRECKCHEST"] = "沉船宝箱", ["GIANTSWORD"] = "巨人之剑", ["GIANTRIBS"] = "巨人肋骨", ["GIANTSKULL"] = "巨人头骨", ["GIANTBRAIN"] = "巨人脑", ["EXCAVATION"] = "挖掘现场", ["LANTERN"] = "灯笼", ["ASHLANDPOTGREEN"] = "灰烬罐(绿色)", ["ASHLANDPOTRED"] = "灰烬罐(红色)", ["SEEKERBRUTE"] = "搜寻者蛮兵", ["BONEMAWSERPENT"] = "骨颚巨蛇", ["COPPER"] = "铜", ["DRAGONEGG"] = "龙蛋" }; public static readonly Dictionary<string, string> PT = new Dictionary<string, string> { ["ABOMINATION"] = "Abominação", ["CRYPT"] = "Cripta", ["DRAUGRSPAWNER"] = "Gerador de Draugr", ["DVERGERTHINGS"] = "Restos de Dvergr", ["DWARFSPAWNER"] = "Ninho de Anão Cinzento", ["FIRE"] = "Fogueira", ["FLAX"] = "Linho", ["IRON"] = "Ferro", ["MUDPILE"] = "Monturo de lama", ["METEORITE"] = "Meteorito", ["MAGECAPS"] = "Chapéus de mago", ["MARKER"] = "Marcador", ["MUSHROOMS"] = "Cogumelos", ["OBSIDIAN"] = "Obsidiana", ["SEEDS"] = "Sementes", ["SILVER"] = "Prata", ["SKELETON"] = "Esqueleto", ["SMOKEPUFFS"] = "Fumaça", ["TAR"] = "Poça de piche", ["THISTLE"] = "Cardo", ["TOTEM"] = "Totem de Fuling", ["TROLLCAVE"] = "Caverna do troll", ["TREASURE"] = "Tesouro enterrado", ["VOLTUREEGG"] = "Ovo de abutre", ["YPCONES"] = "Pinhas de Yggdrasil", ["JOTUNNPUFFS"] = "Pufs de Jotunn", ["RASPBERRIES"] = "Framboesas", ["BLUEBERRIES"] = "Mirtilos", ["CLOUDBERRIES"] = "Amoras árticas", ["BARLEY"] = "Cevada", ["CLOTH"] = "Tecido", ["SHIPWRECKCHEST"] = "Baú de naufrágio", ["GIANTSWORD"] = "Espada gigante", ["GIANTRIBS"] = "Costelas gigantes", ["GIANTSKULL"] = "Crânio gigante", ["GIANTBRAIN"] = "Cérebro gigante", ["EXCAVATION"] = "Local de escavação", ["LANTERN"] = "Lanterna", ["ASHLANDPOTGREEN"] = "Pote das Cinzas (Verde)", ["ASHLANDPOTRED"] = "Pote das Cinzas (Vermelho)", ["SEEKERBRUTE"] = "Bruto Buscador", ["BONEMAWSERPENT"] = "Serpente Mandíbula Óssea", ["COPPER"] = "Cobre", ["DRAGONEGG"] = "Ovo de dragão" }; } public class PinManager { private const int PinFlushThreshold = 50; private readonly HashSet<string> _pinHashes = new HashSet<string>(); private readonly ModConfig _config; private readonly ManualLogSource _logger; private readonly LocalizationManager _localizationManager; private readonly List<Tuple<Vector3, string>> _currentPins = new List<Tuple<Vector3, string>>(); private readonly List<Tuple<Vector3, string>> _newPins = new List<Tuple<Vector3, string>>(); private float _lastSaveTime; private static readonly string SaveFolder = Path.Combine(Paths.ConfigPath, "SullysAutoPinnerFiles"); private static readonly string PinsFilePath = Path.Combine(SaveFolder, "Pins.txt"); public PinManager(ModConfig config, ManualLogSource logger, LocalizationManager localizationManager) { _config = config; _logger = logger; _localizationManager = localizationManager; Directory.CreateDirectory(Path.GetDirectoryName(PinsFilePath)); LoadPinsFromFile(); } public void ReloadConfig() { } public void TryAddPin(Vector3 pos, string label, PinType icon) { //IL_000f: 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) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null) { return; } Vector3 val = RoundTo0_1(pos); string text = label.ToUpperInvariant(); string pinHash = GetPinHash(val, text); if (_pinHashes.Contains(pinHash)) { return; } foreach (Tuple<Vector3, string> currentPin in _currentPins) { if (Vector3.Distance(currentPin.Item1, val) < _config.PinMergeDistance.Value && string.Equals(currentPin.Item2, text, StringComparison.OrdinalIgnoreCase)) { return; } } string label2 = _localizationManager.GetLabel(text); Minimap.instance.AddPin(val, icon, label2, true, false, 0L, default(PlatformUserID)); _newPins.Add(new Tuple<Vector3, string>(val, text)); _currentPins.Add(new Tuple<Vector3, string>(val, text)); _pinHashes.Add(pinHash); if (_config.EnablePinSaving.Value && _newPins.Count >= 50) { SavePinsToFile(); } } public void TryPeriodicSave() { if (_config.EnablePinSaving.Value && _newPins.Count != 0 && (_newPins.Count >= 50 || Time.time - _lastSaveTime > _config.SaveInterval.Value)) { SavePinsToFile(); } } public void SaveImmediate() { if (_config.EnablePinSaving.Value) { SavePinsToFile(); } } public void RemoveAllPins() { if ((Object)(object)Minimap.instance == (Object)null) { return; } _pinHashes.Clear(); try { File.WriteAllText(PinsFilePath, string.Empty); _logger.LogWarning((object)"SullysAutoPinner >>> All Map Pins Removed - Pin history file emptied."); } catch (Exception ex) { _logger.LogWarning((object)("SullysAutoPinner >>> Failed to clear Pins.txt: " + ex.Message)); return; } if (!(AccessTools.Field(typeof(Minimap), "m_pins").GetValue(Minimap.instance) is List<PinData> list)) { return; } foreach (PinData item in list.ToList()) { Minimap.instance.RemovePin(item); } _currentPins.Clear(); _newPins.Clear(); _pinHashes.Clear(); _logger.LogWarning((object)$"SullysAutoPinner >>> All Map Pins Removed: {list.Count} pins removed from the map."); } public void RemoveUnwantedPins(ModConfig config) { if ((Object)(object)Minimap.instance == (Object)null || !(AccessTools.Field(typeof(Minimap), "m_pins").GetValue(Minimap.instance) is List<PinData> list)) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, (string, PinType)> item2 in PinScanner.PinLabelMap.LabelToSettingInfo) { string item = item2.Value.Item1; FieldInfo field = typeof(ModConfig).GetField(item); if (!(field == null) && !(field.FieldType != typeof(ConfigEntry<bool>))) { ConfigEntry<bool> val = (ConfigEntry<bool>)field.GetValue(config); if (val != null && val.Value) { string label = _localizationManager.GetLabel(item.ToUpperInvariant()); hashSet.Add(label); } } } List<PinData> list2 = new List<PinData>(); foreach (PinData item3 in list) { if (!hashSet.Contains(item3.m_name)) { list2.Add(item3); } } foreach (PinData item4 in list2) { Minimap.instance.RemovePin(item4); } _logger.LogWarning((object)$"Removed {list2.Count} unwanted pins from the map."); } private bool IsLabelEnabled(string label, ModConfig config) { if (!PinScanner.PinLabelMap.LabelToSettingInfo.TryGetValue(label, out (string, PinType) value)) { return true; } FieldInfo field = typeof(ModConfig).GetField(value.Item1); if (field == null || field.FieldType != typeof(ConfigEntry<bool>)) { return true; } return ((ConfigEntry<bool>)field.GetValue(config)).Value; } private void SavePinsToFile() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) try { using StreamWriter streamWriter = File.AppendText(PinsFilePath); foreach (Tuple<Vector3, string> newPin in _newPins) { streamWriter.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:F1}|{1:F1}|{2:F1}|{3}", newPin.Item1.x, newPin.Item1.y, newPin.Item1.z, newPin.Item2)); } } catch (Exception ex) { _logger.LogWarning((object)("SullyAutoPinner >>> Error saving pins: " + ex.Message)); } _newPins.Clear(); _lastSaveTime = Time.time; } private void LoadPinsFromFile() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) try { if (!File.Exists(PinsFilePath)) { return; } string[] array = File.ReadAllLines(PinsFilePath); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '|' }); if (array2.Length == 4 && float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { string text = array2[3].Trim().ToUpperInvariant(); Vector3 val = RoundTo0_1(new Vector3(result, result2, result3)); string pinHash = GetPinHash(val, text); _pinHashes.Add(pinHash); _currentPins.Add(new Tuple<Vector3, string>(val, text)); } } } catch (Exception ex) { _logger.LogWarning((object)("SullyAutoPinner >>> Error loading pins: " + ex.Message)); } } private string GetPinHash(Vector3 pos, string label) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = RoundTo0_1(pos); return string.Format(CultureInfo.InvariantCulture, "{0}:{1:F1},{2:F1},{3:F1}", label.ToUpperInvariant(), val.x, val.y, val.z); } private Vector3 RoundTo0_1(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) return new Vector3(Mathf.Round(v.x * 10f) / 10f, Mathf.Round(v.y * 10f) / 10f, Mathf.Round(v.z * 10f) / 10f); } public void RemoveNearbyFallbackPins(Vector3 center, float radius) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null || !(AccessTools.Field(typeof(Minimap), "m_pins").GetValue(Minimap.instance) is List<PinData> list)) { return; } List<PinData> list2 = new List<PinData>(); foreach (PinData item in list) { if (!(Vector3.Distance(center, item.m_pos) > radius)) { string name = item.m_name; if (!PinScanner.PinLabelMap.LabelToSettingInfo.ContainsKey(name)) { list2.Add(item); } } } foreach (PinData item2 in list2) { Minimap.instance.RemovePin(item2); } _logger.LogWarning((object)$"Removed {list2.Count} fallback pins within {radius}m."); } } namespace SimpleJSON { public enum JSONNodeType { Array = 1, Object = 2, String = 3, Number = 4, NullValue = 5, Boolean = 6, None = 7, Custom = 255 } public enum JSONTextMode { Compact, Indent } public abstract class JSONNode { public struct Enumerator { private enum Type { None, Array, Object } private Type type; private Dictionary<string, JSONNode>.Enumerator m_Object; private List<JSONNode>.Enumerator m_Array; public bool IsValid => type != Type.None; public KeyValuePair<string, JSONNode> Current { get { if (type == Type.Array) { return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current); } if (type == Type.Object) { return m_Object.Current; } return new KeyValuePair<string, JSONNode>(string.Empty, null); } } public Enumerator(List<JSONNode>.Enumerator aArrayEnum) { type = Type.Array; m_Object = default(Dictionary<string, JSONNode>.Enumerator); m_Array = aArrayEnum; } public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) { type = Type.Object; m_Object = aDictEnum; m_Array = default(List<JSONNode>.Enumerator); } public bool MoveNext() { if (type == Type.Array) { return m_Array.MoveNext(); } if (type == Type.Object) { return m_Object.MoveNext(); } return false; } } public struct ValueEnumerator { private Enumerator m_Enumerator; public JSONNode Current => m_Enumerator.Current.Value; public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public ValueEnumerator GetEnumerator() { return this; } } public struct KeyEnumerator { private Enumerator m_Enumerator; public string Current => m_Enumerator.Current.Key; public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public KeyEnumerator GetEnumerator() { return this; } } public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IDisposable, IEnumerator, IEnumerable<KeyValuePair<string, JSONNode>>, IEnumerable { private JSONNode m_Node; private Enumerator m_Enumerator; public KeyValuePair<string, JSONNode> Current => m_Enumerator.Current; object IEnumerator.Current => m_Enumerator.Current; internal LinqEnumerator(JSONNode aNode) { m_Node = aNode; if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public void Dispose() { m_Node = null; m_Enumerator = default(Enumerator); } public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator() { return new LinqEnumerator(m_Node); } public void Reset() { if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } IEnumerator IEnumerable.GetEnumerator() { return new LinqEnumerator(m_Node); } } [CompilerGenerated] private sealed class <get_Children>d__43 : IEnumerable<JSONNode>, IEnumerable, IEnumerator<JSONNode>, IDisposable, IEnumerator { private int <>1__state; private JSONNode <>2__current; private int <>l__initialThreadId; JSONNode IEnumerator<JSONNode>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <get_Children>d__43(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { if (<>1__state != 0) { return false; } <>1__state = -1; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<JSONNode> IEnumerable<JSONNode>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <get_Children>d__43(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<JSONNode>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <get_DeepChildren>d__45 : IEnumerable<JSONNode>, IEnumerable, IEnumerator<JSONNode>, IDisposable, IEnumerator { private int <>1__state; private JSONNode <>2__current; private int <>l__initialThreadId; public JSONNode <>4__this; private IEnumerator<JSONNode> <>7__wrap1; private IEnumerator<JSONNode> <>7__wrap2; JSONNode IEnumerator<JSONNode>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <get_DeepChildren>d__45(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if ((uint)(num - -4) <= 1u || num == 1) { try { if (num == -4 || num == 1) { try { } finally { <>m__Finally2(); } } } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; JSONNode jSONNode = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -4; goto IL_008d; } <>1__state = -1; <>7__wrap1 = jSONNode.Children.GetEnumerator(); <>1__state = -3; goto IL_00a7; IL_008d: if (<>7__wrap2.MoveNext()) { JSONNode current = <>7__wrap2.Current; <>2__current = current; <>1__state = 1; return true; } <>m__Finally2(); <>7__wrap2 = null; goto IL_00a7; IL_00a7: if (<>7__wrap1.MoveNext()) { JSONNode current2 = <>7__wrap1.Current; <>7__wrap2 = current2.DeepChildren.GetEnumerator(); <>1__state = -4; goto IL_008d; } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } private void <>m__Finally2() { <>1__state = -3; if (<>7__wrap2 != null) { <>7__wrap2.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<JSONNode> IEnumerable<JSONNode>.GetEnumerator() { <get_DeepChildren>d__45 result; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; result = this; } else { result = new <get_DeepChildren>d__45(0) { <>4__this = <>4__this }; } return result; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<JSONNode>)this).GetEnumerator(); } } public static bool forceASCII = false; public static bool longAsString = false; public static bool allowLineComments = true; [ThreadStatic] private static StringBuilder m_EscapeBuilder; public abstract JSONNodeType Tag { get; } public virtual JSONNode this[int aIndex] { get { return null; } set { } } public virtual JSONNode this[string aKey] { get { return null; } set { } } public virtual string Value { get { return ""; } set { } } public virtual int Count => 0; public virtual bool IsNumber => false; public virtual bool IsString => false; public virtual bool IsBoolean => false; public virtual bool IsNull => false; public virtual bool IsArray => false; public virtual bool IsObject => false; public virtual bool Inline { get { return false; } set { } } public virtual IEnumerable<JSONNode> Children { [IteratorStateMachine(typeof(<get_Children>d__43))] get { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <get_Children>d__43(-2); } } public IEnumerable<JSONNode> DeepChildren { [IteratorStateMachine(typeof(<get_DeepChildren>d__45))] get { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <get_DeepChildren>d__45(-2) { <>4__this = this }; } } public IEnumerable<KeyValuePair<string, JSONNode>> Linq => new LinqEnumerator(this); public KeyEnumerator Keys => new KeyEnumerator(GetEnumerator()); public ValueEnumerator Values => new ValueEnumerator(GetEnumerator()); public virtual double AsDouble { get { double result = 0.0; if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { return result; } return 0.0; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual int AsInt { get { return (int)AsDouble; } set { AsDouble = value; } } public virtual float AsFloat { get { return (float)AsDouble; } set { AsDouble = value; } } public virtual bool AsBool { get { bool result = false; if (bool.TryParse(Value, out result)) { return result; } return !string.IsNullOrEmpty(Value); } set { Value = (value ? "true" : "false"); } } public virtual long AsLong { get { long result = 0L; if (long.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { return result; } return 0L; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual ulong AsULong { get { ulong result = 0uL; if (ulong.TryParse(Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) { return result; } return 0uL; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual JSONArray AsArray => this as JSONArray; public virtual JSONObject AsObject => this as JSONObject; internal static StringBuilder EscapeBuilder { get { if (m_EscapeBuilder == null) { m_EscapeBuilder = new StringBuilder(); } return m_EscapeBuilder; } } public virtual void Add(string aKey, JSONNode aItem) { } public virtual void Add(JSONNode aItem) { Add("", aItem); } public virtual JSONNode Remove(string aKey) { return null; } public virtual JSONNode Remove(int aIndex) { return null; } public virtual JSONNode Remove(JSONNode aNode) { return aNode; } public virtual void Clear() { } public virtual JSONNode Clone() { return null; } public virtual bool HasKey(string aKey) { return false; } public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { return aDefault; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, 0, JSONTextMode.Compact); return stringBuilder.ToString(); } public virtual string ToString(int aIndent) { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, aIndent, JSONTextMode.Indent); return stringBuilder.ToString(); } internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); public abstract Enumerator GetEnumerator(); public static implicit operator JSONNode(string s) { if (s != null) { return new JSONString(s); } return JSONNull.CreateOrGet(); } public static implicit operator string(JSONNode d) { if (!(d == null)) { return d.Value; } return null; } public static implicit operator JSONNode(double n) { return new JSONNumber(n); } public static implicit operator double(JSONNode d) { if (!(d == null)) { return d.AsDouble; } return 0.0; } public static implicit operator JSONNode(float n) { return new JSONNumber(n); } public static implicit operator float(JSONNode d) { if (!(d == null)) { return d.AsFloat; } return 0f; } public static implicit operator JSONNode(int n) { return new JSONNumber(n); } public static implicit operator int(JSONNode d) { if (!(d == null)) { return d.AsInt; } return 0; } public static implicit operator JSONNode(long n) { if (longAsString) { return new JSONString(n.ToString(CultureInfo.InvariantCulture)); } return new JSONNumber(n); } public static implicit operator long(JSONNode d) { if (!(d == null)) { return d.AsLong; } return 0L; } public static implicit operator JSONNode(ulong n) { if (longAsString) { return new JSONString(n.ToString(CultureInfo.InvariantCulture)); } return new JSONNumber(n); } public static implicit operator ulong(JSONNode d) { if (!(d == null)) { return d.AsULong; } return 0uL; } public static implicit operator JSONNode(bool b) { return new JSONBool(b); } public static implicit operator bool(JSONNode d) { if (!(d == null)) { return d.AsBool; } return false; } public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue) { return aKeyValue.Value; } public static bool operator ==(JSONNode a, object b) { if ((object)a == b) { return true; } bool flag = a is JSONNull || (object)a == null || a is JSONLazyCreator; bool flag2 = b is JSONNull || b == null || b is JSONLazyCreator; if (flag && flag2) { return true; } if (!flag) { return a.Equals(b); } return false; } public static bool operator !=(JSONNode a, object b) { return !(a == b); } public override bool Equals(object obj) { return (object)this == obj; } public override int GetHashCode() { return base.GetHashCode(); } internal static string Escape(string aText) { StringBuilder escapeBuilder = EscapeBuilder; escapeBuilder.Length = 0; if (escapeBuilder.Capacity < aText.Length + aText.Length / 10) { escapeBuilder.Capacity = aText.Length + aText.Length / 10; } foreach (char c in aText) { switch (c) { case '\\': escapeBuilder.Append("\\\\"); continue; case '"': escapeBuilder.Append("\\\""); continue; case '\n': escapeBuilder.Append("\\n"); continue; case '\r': escapeBuilder.Append("\\r"); continue; case '\t': escapeBuilder.Append("\\t"); continue; case '\b': escapeBuilder.Append("\\b"); continue; case '\f': escapeBuilder.Append("\\f"); continue; } if (c < ' ' || (forceASCII && c > '\u007f')) { ushort num = c; escapeBuilder.Append("\\u").Append(num.ToString("X4")); } else { escapeBuilder.Append(c); } } string result = escapeBuilder.ToString(); escapeBuilder.Length = 0; return result; } private static JSONNode ParseElement(string token, bool quoted) { if (quoted) { return token; } if (token.Length <= 5) { string text = token.ToLower(); switch (text) { case "false": case "true": return text == "true"; case "null": return JSONNull.CreateOrGet(); } } if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return token; } public static JSONNode Parse(string aJSON) { Stack<JSONNode> stack = new Stack<JSONNode>(); JSONNode jSONNode = null; int i = 0; StringBuilder stringBuilder = new StringBuilder(); string aKey = ""; bool flag = false; bool flag2 = false; bool flag3 = false; for (; i < aJSON.Length; i++) { switch (aJSON[i]) { case '{': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONObject()); if (jSONNode != null) { jSONNode.Add(aKey, stack.Peek()); } aKey = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); flag3 = false; break; case '[': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONArray()); if (jSONNode != null) { jSONNode.Add(aKey, stack.Peek()); } aKey = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); flag3 = false; break; case ']': case '}': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stack.Count == 0) { throw new Exception("JSON Parse: Too many closing brackets"); } stack.Pop(); if (stringBuilder.Length > 0 || flag2) { jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2)); } if (jSONNode != null) { jSONNode.Inline = !flag3; } flag2 = false; aKey = ""; stringBuilder.Length = 0; if (stack.Count > 0) { jSONNode = stack.Peek(); } break; case ':': if (flag) { stringBuilder.Append(aJSON[i]); break; } aKey = stringBuilder.ToString(); stringBuilder.Length = 0; flag2 = false; break; case '"': flag = !flag; flag2 = flag2 || flag; break; case ',': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stringBuilder.Length > 0 || flag2) { jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2)); } flag2 = false; aKey = ""; stringBuilder.Length = 0; flag2 = false; break; case '\n': case '\r': flag3 = true; break; case '\t': case ' ': if (flag) { stringBuilder.Append(aJSON[i]); } break; case '\\': i++; if (flag) { char c = aJSON[i]; switch (c) { case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'n': stringBuilder.Append('\n'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': { string s = aJSON.Substring(i + 1, 4); stringBuilder.Append((char)int.Parse(s, NumberStyles.AllowHexSpecifier)); i += 4; break; } default: stringBuilder.Append(c); break; } } break; case '/': if (allowLineComments && !flag && i + 1 < aJSON.Length && aJSON[i + 1] == '/') { while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') { } } else { stringBuilder.Append(aJSON[i]); } break; default: stringBuilder.Append(aJSON[i]); break; case '\ufeff': break; } } if (flag) { throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } if (jSONNode == null) { return ParseElement(stringBuilder.ToString(), flag2); } return jSONNode; } } public class JSONArray : JSONNode { [CompilerGenerated] private sealed class <get_Children>d__24 : IEnumerable<JSONNode>, IEnumerable, IEnumerator<JSONNode>, IDisposable, IEnumerator { private int <>1__state; private JSONNode <>2__current; private int <>l__initialThreadId; public JSONArray <>4__this; private List<JSONNode>.Enumerator <>7__wrap1; JSONNode IEnumerator<JSONNode>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <get_Children>d__24(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = default(List<JSONNode>.Enumerator); <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; JSONArray jSONArray = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = jSONArray.m_List.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } if (<>7__wrap1.MoveNext()) { JSONNode current = <>7__wrap1.Current; <>2__current = current; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = default(List<JSONNode>.Enumerator); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap1).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<JSONNode> IEnumerable<JSONNode>.GetEnumerator() { <get_Children>d__24 result; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; result = this; } else { result = new <get_Children>d__24(0) { <>4__this = <>4__this }; } return result; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<JSONNode>)this).GetEnumerator(); } } private List<JSONNode> m_List = new List<JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Array; public override bool IsArray => true; public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_List.Count) { return new JSONLazyCreator(this); } return m_List[aIndex]; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex < 0 || aIndex >= m_List.Count) { m_List.Add(value); } else { m_List[aIndex] = value; } } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this); } set { if (value == null) { value = JSONNull.CreateOrGet(); } m_List.Add(value); } } public override int Count => m_List.Count; public override IEnumerable<JSONNode> Children { [IteratorStateMachine(typeof(<get_Children>d__24))] get { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <get_Children>d__24(-2) { <>4__this = this }; } } public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } m_List.Add(aItem); } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_List.Count) { return null; } JSONNode result = m_List[aIndex]; m_List.RemoveAt(aIndex); return result; } public override JSONNode Remove(JSONNode aNode) { m_List.Remove(aNode); return aNode; } public override void Clear() { m_List.Clear(); } public override JSONNode Clone() { JSONArray jSONArray = new JSONArray(); jSONArray.m_List.Capacity = m_List.Capacity; foreach (JSONNode item in m_List) { if (item != null) { jSONArray.Add(item.Clone()); } else { jSONArray.Add(null); } } return jSONArray; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('['); int count = m_List.Count; if (inline) { aMode = JSONTextMode.Compact; } for (int i = 0; i < count; i++) { if (i > 0) { aSB.Append(','); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append(']'); } } public class JSONObject : JSONNode { [CompilerGenerated] private sealed class <get_Children>d__27 : IEnumerable<JSONNode>, IEnumerable, IEnumerator<JSONNode>, IDisposable, IEnumerator { private int <>1__state; private JSONNode <>2__current; private int <>l__initialThreadId; public JSONObject <>4__this; private Dictionary<string, JSONNode>.Enumerator <>7__wrap1; JSONNode IEnumerator<JSONNode>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <get_Children>d__27(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = default(Dictionary<string, JSONNode>.Enumerator); <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; JSONObject jSONObject = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = jSONObject.m_Dict.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } if (<>7__wrap1.MoveNext()) { <>2__current = <>7__wrap1.Current.Value; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = default(Dictionary<string, JSONNode>.Enumerator); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap1).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<JSONNode> IEnumerable<JSONNode>.GetEnumerator() { <get_Children>d__27 result; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; result = this; } else { result = new <get_Children>d__27(0) { <>4__this = <>4__this }; } return result; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<JSONNode>)this).GetEnumerator(); } } private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Object; public override bool IsObject => true; public override JSONNode this[string aKey] { get { if (m_Dict.TryGetValue(aKey, out var value)) { return value; } return new JSONLazyCreator(this, aKey); } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = value; } else { m_Dict.Add(aKey, value); } } } public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } return m_Dict.ElementAt(aIndex).Value; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex >= 0 && aIndex < m_Dict.Count) { string key = m_Dict.ElementAt(aIndex).Key; m_Dict[key] = value; } } } public override int Count => m_Dict.Count; public override IEnumerable<JSONNode> Children { [IteratorStateMachine(typeof(<get_Children>d__27))] get { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <get_Children>d__27(-2) { <>4__this = this }; } } public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } if (aKey != null) { if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = aItem; } else { m_Dict.Add(aKey, aItem); } } else { m_Dict.Add(Guid.NewGuid().ToString(), aItem); } } public override JSONNode Remove(string aKey) { if (!m_Dict.ContainsKey(aKey)) { return null; } JSONNode result = m_Dict[aKey]; m_Dict.Remove(aKey); return result; } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } KeyValuePair<string, JSONNode> keyValuePair = m_Dict.ElementAt(aIndex); m_Dict.Remove(keyValuePair.Key); return keyValuePair.Value; } public override JSONNode Remove(JSONNode aNode) { try { KeyValuePair<string, JSONNode> keyValuePair = m_Dict.Where((KeyValuePair<string, JSONNode> k) => k.Value == aNode).First(); m_Dict.Remove(keyValuePair.Key); return aNode; } catch { return null; } } public override void Clear() { m_Dict.Clear(); } public override JSONNode Clone() { JSONObject jSONObject = new JSONObject(); foreach (KeyValuePair<string, JSONNode> item in m_Dict) { jSONObject.Add(item.Key, item.Value.Clone()); } return jSONObject; } public override bool HasKey(string aKey) { return m_Dict.ContainsKey(aKey); } public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { if (m_Dict.TryGetValue(aKey, out var value)) { return value; } return aDefault; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('{'); bool flag = true; if (inline) { aMode = JSONTextMode.Compact; } foreach (KeyValuePair<string, JSONNode> item in m_Dict) { if (!flag) { aSB.Append(','); } flag = false; if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } aSB.Append('"').Append(JSONNode.Escape(item.Key)).Append('"'); if (aMode == JSONTextMode.Compact) { aSB.Append(':'); } else { aSB.Append(" : "); } item.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append('}'); } } public class JSONString : JSONNode { private string m_Data; public override JSONNodeType Tag => JSONNodeType.String; public override bool IsString => true; public override string Value { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONString(string aData) { m_Data = aData; } public override JSONNode Clone() { return new JSONString(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('"').Append(JSONNode.Escape(m_Data)).Append('"'); } public override bool Equals(object obj) { if (base.Equals(obj)) { return true; } if (obj is string text) { return m_Data == text; } JSONString jSONString = obj as JSONString; if (jSONString != null) { return m_Data == jSONString.m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = ""; } } public class JSONNumber : JSONNode { private double m_Data; public override JSONNodeType Tag => JSONNodeType.Number; public override bool IsNumber => true; public override string Value { get { return m_Data.ToString(CultureInfo.InvariantCulture); } set { if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { m_Data = result; } } } public override double AsDouble { get { return m_Data; } set { m_Data = value; } } public override long AsLong { get { return (long)m_Data; } set { m_Data = value; } } public override ulong AsULong { get { return (ulong)m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONNumber(double aData) { m_Data = aData; } public JSONNumber(string aData) { Value = aData; } public override JSONNode Clone() { return new JSONNumber(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(Value.ToString(CultureInfo.InvariantCulture)); } private static bool IsNumeric(object value) { if (!(value is int) && !(value is uint) && !(value is float) && !(value is double) && !(value is decimal) && !(value is long) && !(value is ulong) && !(value is short) && !(value is ushort) && !(value is sbyte)) { return value is byte; } return true; } public override bool Equals(object obj) { if (obj == null) { return false; } if (base.Equals(obj)) { return true; } JSONNumber jSONNumber = obj as JSONNumber; if (jSONNumber != null) { return m_Data == jSONNumber.m_Data; } if (IsNumeric(obj)) { return Convert.ToDouble(obj) == m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = 0.0; } } public class JSONBool : JSONNode { private bool m_Data; public override JSONNodeType Tag => JSONNodeType.Boolean; public override bool IsBoolean => true; public override string Value { get { return m_Data.ToString(); } set { if (bool.TryParse(value, out var result)) { m_Data = result; } } } public override bool AsBool { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONBool(bool aData) { m_Data = aData; } public JSONBool(string aData) { Value = aData; } public override JSONNode Clone() { return new JSONBool(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(m_Data ? "true" : "false"); } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is bool) { return m_Data == (bool)obj; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = false; } } public class JSONNull : JSONNode { private static JSONNull m_StaticInstance = new JSONNull(); public static bool reuseSameInstance = true; public override JSONNodeType Tag => JSONNodeType.NullValue; public override bool IsNull => true; public override string Value { get { return "null"; } set { } } public override bool AsBool { get { return false; } set { } } public static JSONNull CreateOrGet() { if (reuseSameInstance) { return m_StaticInstance; } return new JSONNull(); } private JSONNull() { } public override Enumerator GetEnumerator() { return default(Enumerator); } public override JSONNode Clone() { return CreateOrGet(); } public override bool Equals(object obj) { if ((object)this == obj) { return true; } return obj is JSONNull; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } } internal class JSONLazyCreator : JSONNode { private JSONNode m_Node; private string m_Key; public override JSONNodeType Tag => JSONNodeType.None; public override JSONNode this[int aIndex] { get { return new JSONLazyCreator(this); } set { Set(new JSONArray()).Add(value); } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this, aKey); } set { Set(new JSONObject()).Add(aKey, value); } } public override int AsInt { get { Set(new JSONNumber(0.0)); return 0; } set { Set(new JSONNumber(value)); } } public override float AsFloat { get { Set(new JSONNumber(0.0)); return 0f; } set { Set(new JSONNumber(value)); } } public override double AsDouble { get { Set(new JSONNumber(0.0)); return 0.0; } set { Set(new JSONNumber(value)); } } public override long AsLong { get { if (JSONNode.longAsString) { Set(new JSONString("0")); } else { Set(new JSONNumber(0.0)); } return 0L; } set { if (JSONNode.longAsString) { Set(new JSONString(value.ToString(CultureInfo.InvariantCulture))); } else { Set(new JSONNumber(value)); } } } public override ulong AsULong { get { if (JSONNode.longAsString) { Set(new JSONString("0")); } else { Set(new JSONNumber(0.0)); } return 0uL; } set { if (JSONNode.longAsString) { Set(new JSONString(value.ToString(CultureInfo.InvariantCulture))); } else { Set(new JSONNumber(value)); } } } public override bool AsBool { get { Set(new JSONBool(aData: false)); return false; } set { Set(new JSONBool(value)); } } public override JSONArray AsArray => Set(new JSONArray()); public override JSONObject AsObject => Set(new JSONObject()); public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONLazyCreator(JSONNode aNode) { m_Node = aNode; m_Key = null; } public JSONLazyCreator(JSONNode aNode, string aKey) { m_Node = aNode; m_Key = aKey; } private T Set<T>(T aVal) where T : JSONNode { if (m_Key == null) { m_Node.Add(aVal); } else { m_Node.Add(m_Key, aVal); } m_Node = null; return aVal; } public override void Add(JSONNode aItem) { Set(new JSONArray()).Add(aItem); } public override void Add(string aKey, JSONNode aItem) { Set(new JSONObject()).Add(aKey, aItem); } public static bool operator ==(JSONLazyCreator a, object b) { if (b == null) { return true; } return (object)a == b; } public static bool operator !=(JSONLazyCreator a, object b) { return !(a == b); } public override bool Equals(object obj) { if (obj == null) { return true; } return (object)this == obj; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } } public static class JSON { public static JSONNode Parse(string aJSON) { return JSONNode.Parse(aJSON); } } } namespace SullysAutoPinner { public class ConfigWatcher : IDisposable { private readonly string _filePath; private readonly ManualLogSource _logger; private readonly Action _onConfigChanged; private FileSystemWatcher _watcher; private Timer _debounceTimer; public ConfigWatcher(string filePath, Action onConfigChanged, ManualLogSource logger) { _filePath = filePath; _onConfigChanged = onConfigChanged; _logger = logger; if (!File.Exists(filePath)) { _logger.LogWarning((object)("[ConfigWatcher] File does not exist yet: " + filePath)); return; } string directoryName = Path.GetDirectoryName(filePath); string fileName = Path.GetFileName(filePath); _watcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.Size | NotifyFilters.LastWrite), EnableRaisingEvents = true }; _watcher.Changed += OnChanged; _watcher.Renamed += OnChanged; _logger.LogInfo((object)("[ConfigWatcher] Watching for changes: " + filePath)); } private void OnChanged(object sender, FileSystemEventArgs e) { _debounceTimer?.Dispose(); _debounceTimer = new Timer(delegate { try { _logger.LogInfo((object)("[ConfigWatcher] Detected config change: " + _filePath)); _onConfigChanged?.Invoke(); } catch (Exception ex) { _logger.LogWarning((object)("[ConfigWatcher] Reload failed: " + ex.Message)); } }, null, 300, -1); } public void Dispose() { _watcher?.Dispose(); _debounceTimer?.Dispose(); } } [BepInPlugin("sullys.autopinner", "Sullys Auto Pinner", "1.4.0")] public class SullysAutoPinner : BaseUnityPlugin { private PinManager _pinManager; private ModConfig _config; private PinScanner _scanner; private PrefabTracker _prefabTracker; private float _lastScanTime; private LocalizationManager _localizationManager; private ConfigWatcher _configWatcher; private void Awake() { _config = new ModConfig(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); string filePath = Path.Combine(Paths.ConfigPath, "sullys.autopinner.cfg"); _configWatcher = new ConfigWatcher(filePath, OnConfigReloaded, ((BaseUnityPlugin)this).Logger); LocalizationManager.EnsureBaseLanguageFiles(((BaseUnityPlugin)this).Logger); _localizationManager = new LocalizationManager(((BaseUnityPlugin)this).Logger, _config.PreferredLanguage.Value); _pinManager = new PinManager(_config, ((BaseUnityPlugin)this).Logger, _localizationManager); _prefabTracker = new PrefabTracker(((BaseUnityPlugin)this).Logger, _config); _scanner = new PinScanner(_config, _pinManager, _prefabTracker); ((BaseUnityPlugin)this).Logger.LogWarning((object)"SullysAutoPinner Initialized"); } private void OnConfigReloaded() { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[SullysAutoPinner] Reloading config from file..."); ((BaseUnityPlugin)this).Config.Reload(); _localizationManager.Reload(_config.PreferredLanguage.Value); _pinManager.ReloadConfig(); if (_config.ClearAllUnwantedPins.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[SullysAutoPinner] Remove Unwanted Pins Triggered"); _pinManager.RemoveUnwantedPins(_config); _config.ClearAllUnwantedPins.Value = false; } if (_config.ClearAllMapPinsAndHistory.Value) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"[SullysAutoPinner] Remove All Pins and History Triggered"); _pinManager.RemoveAllPins(); _config.ClearAllMapPinsAndHistory.Value = false; } } private void Update() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Player.m_localPlayer == (Object)null)) { if (Time.time - _lastScanTime > _config.ScanInterval.Value) { _lastScanTime = Time.time; _scanner.RunScan(((Component)Player.m_localPlayer).transform.position); } _pinManager.TryPeriodicSave(); _prefabTracker.TryPeriodicSave(); } } private void OnApplicationQuit() { _pinManager.SaveImmediate(); _prefabTracker.SaveImmediate(); } } public class PrefabTracker { private readonly HashSet<string> _seenPrefabs = new HashSet<string>(); private readonly ManualLogSource _logger; private readonly ModConfig _config; private float _lastPrefabDumpTime; private const float DumpInterval = 300f; private const int PrefabFlushThreshold = 1000; private const string PrefabsFilePath = "BepInEx/config/SullysAutoPinnerFiles/SeenPrefabs.txt"; private static readonly HashSet<string> IgnoredPrefabs = new HashSet<string> { "tree", "bush", "log", "stone", "branch" }; public PrefabTracker(ManualLogSource logger, ModConfig config) { _logger = logger; _config = config; Directory.CreateDirectory(Path.GetDirectoryName("BepInEx/config/SullysAutoPinnerFiles/SeenPrefabs.txt")); } public void LogPrefabHierarchy(GameObject rootGO) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown if (!_config.EnablePrefabLogging.Value || (Object)(object)rootGO == (Object)null) { return; } string text = ((Object)rootGO).name.ToLowerInvariant(); foreach (string ignoredPrefab in IgnoredPrefabs) { if (text.Contains(ignoredPrefab)) { return; } } List<string> list = new List<string>(); foreach (Transform item2 in rootGO.transform) { Transform val = item2; if ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); } } list.Sort(); string item = ((Object)rootGO).name + " | " + string.Join(",", list); if (_seenPrefabs.Add(item) && _seenPrefabs.Count >= 1000) { SaveImmediate(); } } public void TryPeriodicSave() { if (_config.EnablePrefabLogging.Value && Time.time - _lastPrefabDumpTime > 300f) { SaveImmediate(); } } public void SaveImmediate() { _lastPrefabDumpTime = Time.time; try { HashSet<string> hashSet = new HashSet<string>(); if (File.Exists("BepInEx/config/SullysAutoPinnerFiles/SeenPrefabs.txt")) { string[] array = File.ReadAllLines("BepInEx/config/SullysAutoPinnerFiles/SeenPrefabs.txt"); foreach (string item in array) { hashSet.Add(item); } } using StreamWriter streamWriter = File.AppendText("BepInEx/config/SullysAutoPinnerFiles/SeenPrefabs.txt"); foreach (string seenPrefab in _seenPrefabs) { if (!hashSet.Contains(seenPrefab)) { streamWriter.WriteLine(seenPrefab); } } } catch (Exception ex) { _logger.LogWarning((object)("SullyAutoPinner >>> Error saving prefab list: " + ex.Message)); } } } public class PinScanner { public static class PinLabelMap { public static readonly Dictionary<string, (string SettingKey, PinType Icon)> LabelToSettingInfo = new Dictionary<string, (string, PinType)>(StringComparer.OrdinalIgnoreCase) { { "abomination", ("Abomination", (PinType)3) }, { "crypt2", ("Crypt", (PinType)3) }, { "crypt3", ("Crypt", (PinType)3) }, { "crypt4", ("Crypt", (PinType)3) }, { "trollcave", ("TrollCave", (PinType)3) }, { "dwarfspawner", ("DwarfSpawner", (PinType)2) }, { "draugrspawner", ("DraugrSpawner", (PinType)2) }, { "totem", ("Totem", (PinType)2) }, { "skel", ("Skeleton", (PinType)3) }, { "seekerbrute", ("SeekerBrute", (PinType)3) }, { "bonemawserpent", ("BonemawSerpent", (PinType)3) }, { "rock4_copper", ("Copper", (PinType)3) }, { "iron_deposit", ("Iron", (PinType)3) }, { "mudpile", ("Mudpile", (PinType)3) }, { "obsidian", ("Obsidian", (PinType)3) }, { "rock3_silver", ("Silver", (PinType)3) }, { "silvervein", ("Silver", (PinType)3) }, { "meteor", ("Meteorite", (PinType)3) }, { "pickable_tar", ("Tar", (PinType)2) }, { "pickable_flax_wild", ("Flax", (PinType)3) }, { "flax", ("Flax", (PinType)3) }, { "barley", ("Barley", (PinType)3) }, { "blueberry", ("BlueBerries", (PinType)3) }, { "raspberry", ("RaspBerries", (PinType)3) }, { "shroom", ("Mushrooms", (PinType)3) }, { "cloud", ("CloudBerries", (PinType)3) }, { "thistle", ("Thistle", (PinType)3) }, { "carrot", ("Seeds", (PinType)3) }, { "onion", ("Seeds", (PinType)3) }, { "turnip", ("Seeds", (PinType)3) }, { "magecap", ("MageCaps", (PinType)3) }, { "y-pcone", ("YPCones", (PinType)3) }, { "j-puff", ("JotunPuffs", (PinType)3) }, { "v-egg", ("VoltureEgg", (PinType)3) }, { "dragonegg", ("DragonEgg", (PinType)3) }, { "pickable_smokepuff", ("SmokePuffs", (PinType)3) }, { "thing", ("DvergerThings", (PinType)3) }, { "giant_sword1", ("GiantSword", (PinType)3) }, { "giant_ribs", ("GiantRibs", (PinType)3) }, { "giant_skull", ("GiantSkull", (PinType)3) }, { "giant_brain", ("GiantBrain", (PinType)3) }, { "marker", ("Marker", (PinType)2) }, { "campfire", ("Fire", (PinType)0) }, { "firepit", ("Fire", (PinType)0) }, { "treasurechest", ("Treasure", (PinType)3) }, { "shipwreck_karve_chest", ("ShipwreckChest", (PinType)3) }, { "pickable_dvergrlantern", ("Lantern", (PinType)3) }, { "mistlands_excavation2", ("Excavation", (PinType)3) }, { "ashland_pot1_green", ("AshlandPotGreen", (PinType)3) }, { "ashland_pot2_red", ("AshlandPotRed", (PinType)3) }, { "ashland_pot3_red", ("AshlandPotRed", (PinType)3) } }; } private readonly ModConfig _config; private readonly PinManager _pinManager; private readonly PrefabTracker _prefabTracker; public PinScanner(ModConfig config, PinManager pinManager, PrefabTracker prefabTracker) { _config = config; _pinManager = pinManager; _prefabTracker = prefabTracker; } public void RunScan(Vector3 playerPosition) { //IL_003f: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) if (_config.ClearNearbyFallbackPinsOnNextScan.Value) { _pinManager.RemoveNearbyFallbackPins(playerPosition, _config.ScanRadius.Value); _config.ClearNearbyFallbackPinsOnNextScan.Value = false; } if (playerPosition.y > 1000f) { return; } Collider[] array = Physics.OverlapSphere(playerPosition, _config.ScanRadius.Value, -1, (QueryTriggerInteraction)2); List<Vector3> list = new List<Vector3>(); List<(Vector3, Transform)> list2 = new List<(Vector3, Transform)>(); Collider[] array2 = array; foreach (Collider obj in array2) { GameObject gameObject = ((Component)((Component)obj).transform.root).gameObject; if ((Object)(object)gameObject == (Object)null) { continue; } _prefabTracker.LogPrefabHierarchy(gameObject); string text = ((Object)gameObject).name.ToLowerInvariant(); Vector3 pinPos2 = gameObject.transform.position; string matchedLabel; PinType icon; if (text.Contains("locationproxy")) { list2.Add((pinPos2, gameObject.transform)); } else if (TryMatchPrefab(text, out matchedLabel, out icon)) { if (matchedLabel == "TROLLCAVE") { list.Add(pinPos2); } if (!(matchedLabel == "CRYPT") || !list.Any((Vector3 tc) => Vector3.Distance(tc, pinPos2) < 40f)) { _pinManager.TryAddPin(pinPos2, matchedLabel, icon); } } } foreach (var item in list2) { var (val, _) = item; foreach (Transform item2 in item.Item2) { string text2 = ((Object)item2).name.ToLowerInvariant(); if (!text2.Contains("runestone") && TryMatchPrefab(text2, out var matchedLabel2, out var icon2)) { if (matchedLabel2 == "TROLLCAVE") { list.Add(val); _pinManager.TryAddPin(val, matchedLabel2, icon2); } break; } } } foreach (var item3 in list2) { var (pinPos, _) = item3; foreach (Transform item4 in item3.Item2) { string text3 = ((Object)item4).name.ToLowerInvariant(); if (!text3.Contains("runestone") && TryMatchPrefab(text3, out var matchedLabel3, out var icon3)) { if (!(matchedLabel3 == "TROLLCAVE") && (!(matchedLabel3 == "CRYPT") || !list.Any((Vector3 tc) => Vector3.Distance(tc, pinPos) < 40f))) { _pinManager.TryAddPin(pinPos, matchedLabel3, icon3); } break; } } } } private string CleanLabel(string rawName) { return Regex.Replace(rawName.Replace("(Clone)", ""), "\\d", "").ToUpperInvariant(); } private bool TryMatchPrefab(string prefabName, out string matchedLabel, out PinType icon) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected I4, but got Unknown matchedLabel = null; icon = (PinType)3; foreach (KeyValuePair<string, (string, PinType)> item2 in PinLabelMap.LabelToSettingInfo) { if (prefabName.IndexOf(item2.Key, StringComparison.OrdinalIgnoreCase) < 0) { continue; } string item = item2.Value.Item1; FieldInfo field = typeof(ModConfig).GetField(item, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public); if (!(field == null) && !(field.FieldType != typeof(ConfigEntry<bool>))) { ConfigEntry<bool> val = (ConfigEntry<bool>)field.GetValue(_config); if (val != null && val.Value) { matchedLabel = item.ToUpperInvariant(); icon = (PinType)(int)item2.Value.Item2; return true; } } } if (_config.ShowUnmappedPrefabs.Value) { string[] array = new string[85] { "beech", "berry", "birch", "boar", "branch", "bush", "dandelion", "deer", "feathers", "fish", "firtree", "fur", "goblin", "goblin_archer", "goblin_banner", "goblin_bed", "goblin_fence", "goblin_pole", "goblin_roof", "goblin_roof_cap", "goblin_shaman", "goblin_stepladder", "greyling", "greydwarf", "mushroom", "neck", "oak1", "pickable_dolmentreasure", "pickable_forestcryptremains", "pickable_flint", "pickable_stone", "player", "pine", "piece_sharpstakes", "piece_workbench", "berries", "resin", "seagal", "shrub", "stone", "stone_wall", "stubbe", "vines", "wood", "zone", "pillar", "carrot", "turnip", "onion", "thistle", "minerock_tin", "crow", "odin", "pickable_tar", "tarliquid", "skeleton", "deathsquito", "forestcrypt", "swamptree", "wraith", "statue", "leech", "table", "draugr", "blob", "oak", "leather", "log", "meat", "seed", "bonefragments", "honey", "forge", "entrails", "bonepile", "castlekit", "trophy", "sunken", "root", "guck", "swamp", "grave", "piece", "rock_3", "rock_4" }; foreach (string value in array) { if (prefabName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return false; } } matchedLabel = prefabName.Replace("(clone)", ""); icon = (PinType)2; return true; } return false; } } public class ModConfig { private readonly ManualLogSource _logger; public ConfigEntry<string> PreferredLanguage; public ConfigEntry<float> ScanRadius; public ConfigEntry<float> ScanInterval; public ConfigEntry<float> SaveInterval; public ConfigEntry<float> PinMergeDistance; public ConfigEntry<bool> EnablePinSaving; public ConfigEntry<bool> EnablePrefabLogging; public ConfigEntry<bool> ClearAllMapPinsAndHistory; public ConfigEntry<bool> ClearAllUnwantedPins; public ConfigEntry<bool> ClearNearbyFallbackPinsOnNextScan; public ConfigEntry<bool> ShowUnmappedPrefabs; public ConfigEntry<bool> Abomination; public ConfigEntry<bool> AshlandPotGreen; public ConfigEntry<bool> AshlandPotRed; public ConfigEntry<bool> Barley; public ConfigEntry<bool> BlueBerries; public ConfigEntry<bool> BonemawSerpent; public ConfigEntry<bool> CloudBerries; public ConfigEntry<bool> Copper; public ConfigEntry<bool> Crypt; public ConfigEntry<bool> DragonEgg; public ConfigEntry<bool> DraugrSpawner; public ConfigEntry<bool> DvergerThings; public ConfigEntry<bool> DwarfSpawner; public ConfigEntry<bool> Excavation; public ConfigEntry<bool> Fire; public ConfigEntry<bool> Flax; public ConfigEntry<bool> GiantBrain; public ConfigEntry<bool> GiantRibs; public ConfigEntry<bool> GiantSkull; public ConfigEntry<bool> GiantSword; public ConfigEntry<bool> Iron; public ConfigEntry<bool> JotunPuffs; public ConfigEntry<bool> Lantern; public ConfigEntry<bool> MageCaps; public ConfigEntry<bool> Marker; public ConfigEntry<bool> Meteorite; public ConfigEntry<bool> MistlandsGiants; public ConfigEntry<bool> MistlandsSwords; public ConfigEntry<bool> Mudpile; public ConfigEntry<bool> Mushrooms; public ConfigEntry<bool> Obsidian; public ConfigEntry<bool> Onion; public ConfigEntry<bool> PickableSmokePuff; public ConfigEntry<bool> RaspBerries; public ConfigEntry<bool> Seeds; public ConfigEntry<bool> SeekerBrute; public ConfigEntry<bool> ShipwreckChest; public ConfigEntry<bool> Silver; public ConfigEntry<bool> Skeleton; public ConfigEntry<bool> SmokePuffs; public ConfigEntry<bool> Tar; public ConfigEntry<bool> Thistle; public ConfigEntry<bool> Tin; public ConfigEntry<bool> Totem; public ConfigEntry<bool> Treasure; public ConfigEntry<bool> TrollCave; public ConfigEntry<bool> Turnip; public ConfigEntry<bool> VoltureEgg; public ConfigEntry<bool> YPCones; public ModConfig(ConfigFile config, ManualLogSource logger) { _logger = logger; PreferredLanguage = config.Bind<string>("Localization", "LanguageCode", "en", "Language code for localization (en, fr, ru, etc)."); ScanRadius = config.Bind<float>("Scanning", "ScanRadius", 300f, "Radius (in meters) to scan for prefabs."); ScanInterval = config.Bind<float>("Scanning", "ScanInterval", 12f, "Time (in seconds) between environment scans."); SaveInterval = config.Bind<float>("Persistence", "SaveInterval", 120f, "How often to save pins to disk (in seconds)."); PinMergeDistance = config.Bind<float>("Pins", "PinMergeDistance", 50f, "Distance (in meters) at which duplicate pins are merged."); EnablePinSaving = config.Bind<bool>("Persistence", "EnablePinSaving", true, "Enable saving of detected pins."); EnablePrefabLogging = config.Bind<bool>("Debug", "EnablePrefabLogging", false, "Enable logging of prefab names and hierarchy for debugging."); ClearAllMapPinsAndHistory = config.Bind<bool>("Admin", "ClearAllMapPinsAndHistory", false, "Clear all pins and reset history file on next run."); ClearAllUnwantedPins = config.Bind<bool>("Admin", "ClearAllUnwantedPins", false, "Clear only pins that do not match active settings."); ClearNearbyFallbackPinsOnNextScan = config.Bind<bool>("Admin", "ClearNearbyFallbackPinsOnNextScan", false, "Remove fallback pins near the player on next scan."); ShowUnmappedPrefabs = config.Bind<bool>("Fallback", "ShowUnmappedPrefabs", false, "Create pins for unknown prefabs using prefab name."); Mushrooms = config.Bind<bool>("Pins", "Mushrooms", false, ""); RaspBerries = config.Bind<bool>("Pins", "RaspBerries", false, ""); BlueBerries = config.Bind<bool>("Pins", "BlueBerries", false, ""); Seeds = config.Bind<bool>("Pins", "Seeds", false, ""); Thistle = config.Bind<bool>("Pins", "Thistle", false, ""); CloudBerries = config.Bind<bool>("Pins", "CloudBerries", false, ""); Copper = config.Bind<bool>("Pins", "Copper", true, ""); Tin = config.Bind<bool>("Pins", "Tin", false, ""); Skeleton = config.Bind<bool>("Pins", "Skeleton", false, ""); DwarfSpawner = config.Bind<bool>("Pins", "DwarfSpawner", true, ""); TrollCave = config.Bind<bool>("Pins", "TrollCave", true, ""); Crypt = config.Bind<bool>("Pins", "Crypt", true, ""); Totem = config.Bind<bool>("Pins", "Totem", true, ""); Fire = config.Bind<bool>("Pins", "Fire", true, ""); DraugrSpawner = config.Bind<bool>("Pins", "DraugrSpawner", false, ""); Treasure = config.Bind<bool>("Pins", "Treasure", false, ""); Barley = config.Bind<bool>("Pins", "Barley", false, ""); Flax = config.Bind<bool>("Pins", "Flax", false, ""); Tar = config.Bind<bool>("Pins", "Tar", true, ""); DragonEgg = config.Bind<bool>("Pins", "DragonEgg", true, ""); VoltureEgg = config.Bind<bool>("Pins", "VoltureEgg", true, ""); SmokePuffs = config.Bind<bool>("Pins", "SmokePuffs", false, ""); MageCaps = config.Bind<bool>("Pins", "MageCaps", true, ""); YPCones = config.Bind<bool>("Pins", "YPCones", true, ""); JotunPuffs = config.Bind<bool>("Pins", "JotunPuffs", true, ""); Iron = config.Bind<bool>("Pins", "Iron", true, ""); Mudpile = config.Bind<bool>("Pins", "Mudpile", true, ""); Abomination = config.Bind<bool>("Pins", "Abomination", true, ""); Meteorite = config.Bind<bool>("Pins", "Meteorite", true, ""); Obsidian = config.Bind<bool>("Pins", "Obsidian", true, ""); Silver = config.Bind<bool>("Pins", "Silver", true, ""); MistlandsGiants = config.Bind<bool>("Pins", "MistlandsGiants", true, ""); MistlandsSwords = config.Bind<bool>("Pins", "MistlandsSwords", true, ""); DvergerThings = config.Bind<bool>("Pins", "DvergerThings", true, ""); BonemawSerpent = config.Bind<bool>("Pins", "BonemawSerpent", true, ""); GiantSword = config.Bind<bool>("Pins", "GiantSword", true, ""); GiantRibs = config.Bind<bool>("Pins", "GiantRibs", true, ""); GiantSkull = config.Bind<bool>("Pins", "GiantSkull", true, ""); GiantBrain = config.Bind<bool>("Pins", "GiantBrain", true, ""); Lantern = config.Bind<bool>("Pins", "Lantern", true, ""); Excavation = config.Bind<bool>("Pins", "Excavation", true, ""); AshlandPotGreen = config.Bind<bool>("Pins", "AshlandPotGreen", true, ""); AshlandPotRed = config.Bind<bool>("Pins", "AshlandPotRed", true, ""); ShipwreckChest = config.Bind<bool>("Pins", "ShipwreckChest", true, ""); SeekerBrute = config.Bind<bool>("Pins", "SeekerBrute", true, ""); Marker = config.Bind<bool>("Pins", "Marker", true, ""); } } }