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 SeneaL Shared Pins v0.1.8
BepInEx/plugins/SeneaLSharedPins/SeneaLSharedPins.dll
Decompiled 18 minutes ago
The result has been truncated due to the large size, download it to view full contents!
using 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.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Splatform; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("SeneaLSharedPins")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.8.0")] [assembly: AssemblyInformationalVersion("0.1.8")] [assembly: AssemblyProduct("SeneaLSharedPins")] [assembly: AssemblyTitle("SeneaLSharedPins")] [assembly: AssemblyVersion("0.1.8.0")] namespace SeneaLSharedPins; [BepInPlugin("bananas4dragons.valheim.senealsharedpins", "SeneaL Shared Pins", "0.1.8")] [BepInDependency("seneaL.valheim.ui", "1.2.8")] [BepInDependency("eu.mydayyy.plugins.serversidemap", "1.3.14")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class SharedPinsPlugin : BaseUnityPlugin { private sealed class View { internal PinData Pin; internal PinRecord Desired; internal PinRecord InFlight; internal PinRecord Displayed; internal bool Sending; internal bool Removing; } private sealed class DialogState { internal PinData Pin; internal string Icon; internal int Tint; internal bool Compass; } private enum Message : byte { Hello, SnapshotStart, SnapshotEnd, Upsert, Delete, Changes, Reject, CheckOre, OreAbsent } private const string Galleon = "ShipGalleon_DO"; private const string NavigationId = "navigation_mark_ship_id"; private const string NavigationActive = "navigation_mark_active"; private const string NavigationIcon = "PinNavigationMark_DO"; private const string WreckIcon = "PinShipwreckShip_DO"; private const string CrateIcon = "PinShipwreckCrate_DO"; private FieldInfo artisanNativeNavigation; private FieldInfo artisanShipId; private FieldInfo artisanWaterPin; private FieldInfo artisanWaterIcon; private FieldInfo artisanSprites; private MethodInfo artisanNavigationSprite; private readonly Dictionary<string, (Component Source, PinData Pin, PinRecord Record)> artisanWater = new Dictionary<string, (Component, PinData, PinRecord)>(); private readonly List<ZDO> galleons = new List<ZDO>(); private int galleonSector; private float nextGalleonScan; private readonly Dictionary<string, View> views = new Dictionary<string, View>(); private readonly Dictionary<PinData, string> ownedPins = new Dictionary<PinData, string>(); private readonly Dictionary<long, PinRecord> manualStyle = new Dictionary<long, PinRecord>(); private readonly Dictionary<PinData, PinRecord> newManual = new Dictionary<PinData, PinRecord>(); private readonly Dictionary<string, (int Percent, float Next)> observed = new Dictionary<string, (int, float)>(); private readonly HashSet<string> absentNativeOre = new HashSet<string>(StringComparer.Ordinal); private readonly Dictionary<Vector2s, List<PinRecord>> sharedOreByZone = new Dictionary<Vector2s, List<PinRecord>>(); private bool oreIndexDirty; private readonly HashSet<string> hiddenPins = new HashSet<string>(StringComparer.Ordinal); private readonly Dictionary<string, PinData> nativeLive = new Dictionary<string, PinData>(); private Minimap clientMap; private float nextDiscoveryScan; private int internalMapChange; private int loadingMap; private int oreRefresh; private DialogState activeDialog; public const string Id = "bananas4dragons.valheim.senealsharedpins"; private const string RpcName = "B4D_SeneaLSharedPins_2"; private const int FileVersion = 2; private const int BatchSize = 48; private static SharedPinsPlugin Instance; private Harmony harmony; private SeneaLBindings ui; private ConfigEntry<int> oreThreshold; private ConfigEntry<bool> removeDepletedOre; private ConfigEntry<bool> liveMarkers; private ConfigEntry<bool> artisanMarkers; private readonly PinStore store = new PinStore(); private readonly Dictionary<ZRpc, ZNetPeer> registered = new Dictionary<ZRpc, ZNetPeer>(); private readonly HashSet<ZRpc> subscribers = new HashSet<ZRpc>(); private readonly Dictionary<string, PinRecord> serverChanges = new Dictionary<string, PinRecord>(); private readonly Dictionary<string, float> missingObjects = new Dictionary<string, float>(); private readonly Dictionary<string, PinRecord> clientRecords = new Dictionary<string, PinRecord>(); private ZNet currentNet; private ZRpc serverRpc; private long worldId; private string savePath; private bool serverActive; private bool saveDirty; private bool backupLegacySave; private bool clientReady; private bool snapshotStarted; private bool serverHidesDepletedOre; private float nextTick; private float nextHello; private float nextWorldScan; private float nextSave; private float nextFailureLog; private int SharedOreThreshold { get { if (oreThreshold.Value >= 0) { return oreThreshold.Value; } return ui.OreThreshold; } } private bool ClientActive { get { if (clientReady && serverRpc != null) { return (Object)(object)Minimap.instance != (Object)null; } return false; } } private void BindArtisan() { Type type = SeneaLBindings.FindType("Artisan_Mastery.NavigationMarkTracker"); Type type2 = SeneaLBindings.FindType("Artisan_Mastery.WaterContainerSpawner"); if (type == null || type2 == null) { return; } try { artisanNativeNavigation = SeneaLBindings.Field(type, "_pin"); artisanShipId = SeneaLBindings.Field(type, "_trackedShipId"); artisanNavigationSprite = SeneaLBindings.Method(type, "GetPinSprite"); artisanSprites = SeneaLBindings.Field(type2, "PinSprites"); Type type3 = type2.GetNestedType("WaterContainerPinComponent", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new TypeLoadException("WaterContainerPinComponent"); artisanWaterPin = SeneaLBindings.Field(type3, "Pin"); artisanWaterIcon = SeneaLBindings.Field(type3, "PinSprite"); Hook(type3, "TryCreatePin", null, "ArtisanWaterCreated"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Artisan Mastery marker integration resolved."); } catch (Exception ex) { artisanWaterPin = null; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Artisan Mastery marker hooks could not be resolved: " + ex.Message)); } } private static void ArtisanWaterCreated(Component __instance) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null || instance.artisanWaterPin == null || !instance.artisanMarkers.Value) { return; } object? value = instance.artisanWaterPin.GetValue(__instance); PinData val = (PinData)((value is PinData) ? value : null); if (val == null) { return; } ZNetView componentInParent = __instance.GetComponentInParent<ZNetView>(); if (Object.op_Implicit((Object)(object)componentInParent) && componentInParent.IsValid()) { string text = instance.artisanWaterIcon.GetValue(__instance) as string; if (!(text != "PinShipwreckShip_DO") || !(text != "PinShipwreckCrate_DO")) { PinRecord pinRecord = new PinRecord { Kind = ((text == "PinShipwreckShip_DO") ? PinKind.ArtisanWreck : PinKind.ArtisanCrate), Name = (val.m_name ?? ""), Icon = text }; SetObject(pinRecord, componentInParent.GetZDO().m_uid); SetPosition(pinRecord, val.m_pos); instance.artisanWater[pinRecord.Id] = (__instance, val, pinRecord); } } } private void ScanArtisan(float now) { if (!artisanMarkers.Value) { return; } KeyValuePair<string, (Component, PinData, PinRecord)>[] array = artisanWater.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<string, (Component, PinData, PinRecord)> keyValuePair = array[i]; if (!Object.op_Implicit((Object)(object)keyValuePair.Value.Item1) || artisanWaterPin.GetValue(keyValuePair.Value.Item1) != keyValuePair.Value.Item2) { artisanWater.Remove(keyValuePair.Key); continue; } nativeLive[keyValuePair.Key] = keyValuePair.Value.Item2; if (!clientRecords.ContainsKey(keyValuePair.Key)) { Report(keyValuePair.Value.Item3, now); } } object? obj = artisanNativeNavigation?.GetValue(null); PinData val = (PinData)((obj is PinData) ? obj : null); if (val == null) { return; } string text = (string)artisanShipId.GetValue(null); if (string.IsNullOrEmpty(text)) { return; } foreach (PinRecord value in clientRecords.Values) { if (value.Kind == PinKind.ArtisanNavigation && value.Icon == text) { nativeLive[value.Id] = val; } } } private Sprite ArtisanSprite(PinKind kind) { object obj; switch (kind) { case PinKind.ArtisanNavigation: { object? obj2 = artisanNavigationSprite?.Invoke(null, new object[1] { "PinNavigationMark_DO" }); return (Sprite)(((obj2 is Sprite) ? obj2 : null) ?? ui.Sprite("ship")); } default: obj = "PinShipwreckCrate_DO"; break; case PinKind.ArtisanWreck: obj = "PinShipwreckShip_DO"; break; } string key = (string)obj; if (artisanSprites?.GetValue(null) is IDictionary<string, Sprite> dictionary && dictionary.TryGetValue(key, out var value)) { return value; } return ui.Sprite((kind == PinKind.ArtisanWreck) ? "ship" : "bag"); } private static bool ValidateArtisan(PinRecord pin, ZDO zdo, GameObject prefab) { string name = ((Object)prefab).name; switch (pin.Kind) { case PinKind.ArtisanNavigation: if (name != "ShipGalleon_DO" || !zdo.GetBool("navigation_mark_active", false)) { return false; } pin.Icon = zdo.GetString("navigation_mark_ship_id", ""); pin.Name = ""; return pin.Icon.Length > 0; case PinKind.ArtisanWreck: if (!(name == "ShipwreckFloatingShipFull_DO") && !(name == "ShipwreckFloatingShipFront_DO")) { return name == "ShipwreckFloatingShipBack_DO"; } return true; case PinKind.ArtisanCrate: if (!(name == "ShipwreckFloatingCrateLarge_DO") && !(name == "ShipwreckFloatingCrateMedium_DO")) { return name == "ShipwreckFloatingCrateSmall_DO"; } return true; default: return false; } } private void ScanGalleonMarks(float now) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; ZNetScene instance2 = ZNetScene.instance; if (!artisanMarkers.Value || instance == null || !Object.op_Implicit((Object)(object)instance2) || !Object.op_Implicit((Object)(object)instance2.GetPrefab("ShipGalleon_DO")) || now < nextGalleonScan || !instance.GetAllZDOsWithPrefabIterative("ShipGalleon_DO", galleons, ref galleonSector)) { return; } GameObject prefab = instance2.GetPrefab("ShipGalleon_DO"); foreach (ZDO galleon in galleons) { if (galleon != null && galleon.IsValid()) { PinRecord pinRecord = new PinRecord { Kind = PinKind.ArtisanNavigation }; SetObject(pinRecord, galleon.m_uid); if (PrepareLive(pinRecord, galleon, prefab) && store.Upsert(pinRecord, SharedOreThreshold, out var current2)) { RecordChange(pinRecord.Id, current2); } } } galleons.Clear(); galleonSector = 0; nextGalleonScan = now + 2f; } private void ResetArtisanClient() { artisanWater.Clear(); } private void ReceiveClient(Message message, BinaryReader reader) { switch (message) { case Message.SnapshotStart: if (!clientReady) { snapshotStarted = true; clientRecords.Clear(); oreIndexDirty = true; } break; case Message.SnapshotEnd: if (snapshotStarted && !clientReady) { serverHidesDepletedOre = reader.BaseStream.Position < reader.BaseStream.Length && reader.ReadBoolean(); snapshotStarted = false; clientReady = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Shared pin snapshot received. " + clientRecords.Count + " records.")); } break; case Message.Changes: if (snapshotStarted || clientReady) { int num = reader.ReadInt32(); if (num < 0 || num > 48) { throw new InvalidDataException("Invalid pin batch."); } for (int i = 0; i < num; i++) { ReadChange(reader, rejected: false); } } break; case Message.OreAbsent: { string text = reader.ReadString(); if (clientReady && reader.BaseStream.Position == reader.BaseStream.Length && observed.ContainsKey(text)) { absentNativeOre.Add(text); } break; } case Message.Reject: if (clientReady) { ReadChange(reader, rejected: true); } break; case Message.Upsert: case Message.Delete: case Message.CheckOre: break; } } private void ReadChange(BinaryReader reader, bool rejected) { if (!reader.ReadBoolean()) { string text = reader.ReadString(); if (clientRecords.TryGetValue(text, out var value) && value.Kind == PinKind.Ore) { oreIndexDirty = true; } clientRecords.Remove(text); RemoveView(text); return; } PinRecord pinRecord = PinRecord.Read(reader); clientRecords[pinRecord.Id] = pinRecord; if (pinRecord.Kind == PinKind.Ore) { oreIndexDirty = true; } if (pinRecord.Kind == PinKind.Manual && views.TryGetValue(pinRecord.Id, out var value2) && value2.Sending) { if (rejected || value2.Desired == null || value2.InFlight == null || value2.InFlight.SameDisplay(value2.Desired)) { value2.Desired = null; } value2.Sending = (value2.Removing = false); value2.InFlight = null; } } private void TickClient(float now) { Minimap instance = Minimap.instance; if ((Object)(object)instance != (Object)(object)clientMap) { ClearViews(); clientMap = instance; } HashSet<PinData> currentPins = new HashSet<PinData>(ui.MapPins(instance)); CaptureNewManual(instance, currentPins); SendManualChanges(currentPins); if (now >= nextDiscoveryScan) { nextDiscoveryScan = now + 1f; ScanDiscoveries(now); ReconcileNativeOreMarkers(instance); ScanLive(now); } foreach (PinRecord value2 in clientRecords.Values) { if (value2.Kind == PinKind.Dungeon) { object entry = GetEntry(value2); if (entry != null && ui.EntryPin(entry) != null) { PinData pin = ui.EntryPin(entry); ui.ClearEntryPin(entry); DropPin(instance, pin); } } if (!ShouldDisplay(value2, currentPins)) { RemoveView(value2.Id); continue; } if (!views.TryGetValue(value2.Id, out var value)) { value = new View(); views.Add(value2.Id, value); } if (!value.Removing) { PinRecord pinRecord = value.Desired ?? value2; if (value2.IsLive && (int)value2.Kind < 6 && ui.IsLiveHidden(value2)) { pinRecord = value2.Copy(); pinRecord.Checked = true; } if (value.Pin == null) { MakePin(instance, value, pinRecord); } else if (value.Displayed == null || !value.Displayed.SameDisplay(pinRecord)) { ApplyPin(instance, value, pinRecord); } } } } private bool ShouldDisplay(PinRecord record, HashSet<PinData> currentPins) { if (hiddenPins.Contains(record.Id) || record.IsHiddenByOrePolicy(serverHidesDepletedOre)) { return false; } if (record.IsDiscovery) { return ui.CanShowDiscoveries; } if (!record.IsLive) { return true; } if (nativeLive.TryGetValue(record.Id, out var value) && currentPins.Contains(value)) { return false; } if ((int)record.Kind < 6) { return ui.ShowLive(record); } return artisanMarkers.Value; } private object GetEntry(PinRecord record) { if (!ui.OwnPlayerLoaded) { return null; } return ui.Known["d" + Mathf.RoundToInt(record.X) + "_" + Mathf.RoundToInt(record.Z)]; } private void ReconcileNativeOreMarkers(Minimap map) { if (!ui.OwnPlayerLoaded) { return; } bool flag = false; foreach (DictionaryEntry item in ui.Known) { object value = item.Value; if (ui.EntryDungeon(value)) { continue; } if (absentNativeOre.Count != 0 && !ui.EntryOff(value)) { PinRecord pinRecord = NativeOreRecord(value); if (absentNativeOre.Contains(pinRecord.LegacyOreQueryKey) && ReadNativeOre(pinRecord, (Component)/*isinst with value type is only supported in some contexts*/) == null) { ui.DisableEntry(value); flag = true; } } PinData val = ui.EntryPin(value); if (val != null && HasSharedOreMarker(value)) { ui.ClearEntryPin(value); DropPin(map, val); } } if (flag) { ui.SaveOreDiscoveries(Player.m_localPlayer); } } private bool HasSharedOreMarker(object entry) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) PinRecord pinRecord = NativeOreRecord(entry); object? obj = ui.Rocks[ui.EntryId(entry)]; PinRecord pinRecord2 = ReadNativeOre(pinRecord, (Component)((obj is Component) ? obj : null)); if (pinRecord2 != null) { return clientRecords.ContainsKey(pinRecord2.Id); } if (absentNativeOre.Contains(pinRecord.LegacyOreQueryKey)) { return true; } if (oreIndexDirty) { sharedOreByZone.Clear(); foreach (PinRecord value3 in clientRecords.Values) { if (value3.Kind == PinKind.Ore) { Vector2s zone = ZoneSystem.GetZone(SeneaLBindings.Position(value3)); if (!sharedOreByZone.TryGetValue(zone, out var value)) { value = (sharedOreByZone[zone] = new List<PinRecord>()); } value.Add(value3); } } oreIndexDirty = false; } Vector3 val = SeneaLBindings.Position(pinRecord); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(2f, 0f, 2f); Vector2s zone2 = ZoneSystem.GetZone(val - val2); Vector2s zone3 = ZoneSystem.GetZone(val + val2); PinRecord match = null; int bestRank = 0; bool ambiguous = false; for (int i = zone2.x; i <= zone3.x; i++) { for (int j = zone2.y; j <= zone3.y; j++) { if (!sharedOreByZone.TryGetValue(new Vector2s((short)i, (short)j), out var value2)) { continue; } foreach (PinRecord item in value2) { if (!(pinRecord.DistanceSquared(item.X, item.Y, item.Z) > 4f)) { int rank = pinRecord.LegacyOreMatchRank(item.X, item.Y, item.Z, item.Name); PinRecord.ConsiderOreMatch(item, rank, ref match, ref bestRank, ref ambiguous); } } } } if (match != null) { return !ambiguous; } return false; } private PinRecord NativeOreRecord(object entry) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) PinRecord obj = new PinRecord { Kind = PinKind.Ore, Name = ui.EntryName(entry) }; SetPosition(obj, ui.EntryPos(entry)); SetObject(obj, ui.EntryZdo(entry)); return obj; } private PinRecord ReadNativeOre(PinRecord legacy, Component rock) { //IL_002c: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (Object.op_Implicit((Object)(object)rock) ? rock.GetComponent<ZNetView>() : null); if (!Object.op_Implicit((Object)(object)val) || !val.IsValid()) { return null; } ZDO zDO = val.GetZDO(); Vector3 position = zDO.GetPosition(); if (legacy.DistanceSquared(position.x, position.y, position.z) > 1f / 64f) { return null; } PinRecord pinRecord = legacy.Copy(); pinRecord.OrePrefab = zDO.GetPrefab(); SetPosition(pinRecord, position); SetObject(pinRecord, zDO.m_uid); return pinRecord; } private void CaptureNewManual(Minimap map, HashSet<PinData> currentPins) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) KeyValuePair<PinData, PinRecord>[] array = newManual.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<PinData, PinRecord> keyValuePair = array[i]; PinData key = keyValuePair.Key; if (key == ui.NamePin(map)) { continue; } newManual.Remove(key); if (currentPins.Contains(key)) { PinRecord value = keyValuePair.Value; value.Id = ManualId(key); value.Kind = PinKind.Manual; ReadManual(key, value); if (views.ContainsKey(value.Id) || clientRecords.ContainsKey(value.Id)) { DropPin(map, key); continue; } View value2 = new View { Pin = key, Desired = value, Displayed = value }; views.Add(value.Id, value2); ownedPins.Add(key, value.Id); manualStyle[SeneaLBindings.PositionKey(key.m_pos)] = value; } } } private static string ManualId(PinData pin) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_0029: 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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 PinType type = pin.m_type; if ((int)type == 9 || type - 14 <= 3) { string s = string.Format(CultureInfo.InvariantCulture, "{0}:{1:R}:{2:R}:{3:R}", pin.m_type, pin.m_pos.x, pin.m_pos.y, pin.m_pos.z); using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(s)); Array.Resize(ref array, 16); return new Guid(array).ToString("N"); } return Guid.NewGuid().ToString("N"); } private void SendManualChanges(HashSet<PinData> currentPins) { KeyValuePair<string, View>[] array = views.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<string, View> pair = array[i]; View value = pair.Value; if (value.Pin == null) { continue; } clientRecords.TryGetValue(pair.Key, out var accepted); PinRecord pinRecord = value.Desired ?? accepted; if (pinRecord == null || pinRecord.Kind != PinKind.Manual) { continue; } if (!currentPins.Contains(value.Pin)) { if (value.Sending) { continue; } if (accepted == null) { RemoveView(pair.Key); continue; } value.Sending = (value.Removing = true); Send(serverRpc, Message.Delete, delegate(BinaryWriter writer) { writer.Write(pair.Key); writer.Write(accepted.Revision); }); } else { if (!value.Sending && value.Desired == null && value.Displayed != null && !value.Displayed.SameDisplay(pinRecord)) { continue; } PinRecord pinRecord2 = pinRecord.Copy(); ReadManual(value.Pin, pinRecord2); if (value.Desired == null && value.Displayed != null && !value.Displayed.SameDisplay(pinRecord2)) { value.Desired = pinRecord2; } else if (value.Desired != null) { value.Desired = pinRecord2; } if (value.Sending || value.Desired == null) { continue; } if (accepted != null && accepted.SameDisplay(value.Desired)) { value.Desired = null; continue; } pinRecord2 = value.Desired.Copy(); pinRecord2.Revision = accepted?.Revision ?? 0; if (pinRecord2.IsValid()) { value.Sending = true; value.InFlight = pinRecord2; Send(serverRpc, Message.Upsert, pinRecord2.Write); } } } } private static void ReadManual(PinData pin, PinRecord record) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) string text = pin.m_name ?? ""; record.Name = text.Substring(0, Math.Min(text.Length, 200)); if (Array.IndexOf(SeneaLBindings.CustomIcons, record.Icon) <= 0) { record.Icon = (Enum.IsDefined(typeof(PinType), pin.m_type) ? ((object)Unsafe.As<PinType, PinType>(ref pin.m_type)/*cast due to .constrained prefix*/).ToString() : "Icon3"); } record.Checked = pin.m_checked; SetPosition(record, pin.m_pos); } private void ScanDiscoveries(float now) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) if (!ui.OwnPlayerLoaded || !ui.CanReportDiscoveries) { return; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; if (ui.WantsDungeons) { ScanDungeonEntrances(position, now); } foreach (DictionaryEntry item in ui.Known) { object value = item.Value; Vector3 val = ui.EntryPos(value); if (Utils.DistanceXZ(position, val) > 120f) { continue; } PinRecord pinRecord = new PinRecord { Name = ui.EntryName(value) }; SetPosition(pinRecord, val); if (ui.EntryDungeon(value)) { if (!ui.EntryOff(value)) { pinRecord.Kind = PinKind.Dungeon; pinRecord.Id = pinRecord.CanonicalId; if (!clientRecords.ContainsKey(pinRecord.Id)) { Report(pinRecord, now); } } continue; } pinRecord.Kind = PinKind.Ore; SetObject(pinRecord, ui.EntryZdo(value)); object? obj = ui.Rocks[ui.EntryId(value)]; Component rock = (Component)((obj is Component) ? obj : null); PinRecord pinRecord2 = ReadNativeOre(pinRecord, rock); if (pinRecord2 == null) { ReportLegacyOre(pinRecord, position, now); continue; } pinRecord2.Percent = ui.ReadPercent(rock); if (!clientRecords.TryGetValue(pinRecord2.Id, out var value2) || value2.IsMissingOre || (pinRecord2.Percent >= 0 && (value2.Percent < 0 || pinRecord2.Percent < value2.Percent))) { Report(pinRecord2, now); } } foreach (PinRecord value3 in clientRecords.Values) { if (value3.Kind != PinKind.Ore || value3.OrePrefab == 0 || value3.IsMissingOre || value3.Percent == 0 || DistanceXZ(value3, position) > 120f || !IsSameOre(value3, FindObject(value3))) { continue; } object? obj2 = ui.Rocks[value3.ObjectKey]; Component val2 = (Component)((obj2 is Component) ? obj2 : null); if (Object.op_Implicit((Object)(object)val2)) { int num = ui.ReadPercent(val2); if (num >= 0 && (value3.Percent < 0 || num < value3.Percent)) { PinRecord pinRecord3 = value3.Copy(); pinRecord3.Percent = num; Report(pinRecord3, now); } } } } private void ReportLegacyOre(PinRecord ore, Vector3 playerPosition, float now) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!(DistanceXZ(ore, playerPosition) > 45f) && ore.IsValid()) { string legacyOreQueryKey = ore.LegacyOreQueryKey; if (!absentNativeOre.Contains(legacyOreQueryKey) && (!observed.TryGetValue(legacyOreQueryKey, out (int, float) value) || !(now < value.Item2))) { observed[legacyOreQueryKey] = (-1, now + 8f); Send(serverRpc, Message.CheckOre, ore.Write); } } } private void ScanDungeonEntrances(Vector3 playerPosition, float now) { //IL_003a: 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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) float detectionRadius = ui.DetectionRadius; foreach (Location dungeonPlace in ui.DungeonPlaces) { if (!Object.op_Implicit((Object)(object)dungeonPlace)) { continue; } Vector3 position = ((Component)dungeonPlace).transform.position; if (position.y > 3000f || Mathf.Abs(position.y - playerPosition.y) > 45f || Utils.DistanceXZ(position, playerPosition) > detectionRadius + dungeonPlace.m_exteriorRadius) { continue; } string text = PinRecord.DungeonKey(position.x, position.z); clientRecords.TryGetValue(text, out var value); int num = ((value != null) ? DungeonNames.Priority(value.Icon, value.Name) : 0); if (num == 3) { continue; } PinRecord pinRecord = new PinRecord { Id = text, Kind = PinKind.Dungeon }; SetPosition(pinRecord, position); if (TryDungeonLabel(dungeonPlace, out var label, out var source)) { if (DungeonNames.Priority(source, label) <= num) { continue; } pinRecord.Name = label; pinRecord.Icon = source; } else { object entry = GetEntry(pinRecord); if (value != null || entry == null) { continue; } pinRecord.Name = ui.EntryName(entry); } Report(pinRecord, now); } } private static bool TryDungeonLabel(Location location, out string label, out string source) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) label = (source = null); if (!Object.op_Implicit((Object)(object)location)) { return false; } Vector3 position = ((Component)location).transform.position; float num = Mathf.Max(20f, location.m_exteriorRadius) + 8f; float num2 = num * num; Teleport[] componentsInChildren = ((Component)location).GetComponentsInChildren<Teleport>(true); foreach (Teleport val in componentsInChildren) { if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val.m_targetPoint)) { continue; } Vector3 position2 = ((Component)val).transform.position; Vector3 position3 = ((Component)val.m_targetPoint).transform.position; Vector3 val2 = position2 - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude >= num2) && (!(position2.y > 3000f) || !(position3.y < position2.y))) { string text = UsefulDungeonLabel(val.m_enterText); if (text != null) { num2 = sqrMagnitude; label = text; } } } if (label != null) { source = "dungeon:entrance"; return true; } label = UsefulDungeonLabel(location.m_discoverLabel); if (label != null) { source = "dungeon:discovery"; return true; } Transform val3 = ((Component)location).transform; while (Object.op_Implicit((Object)(object)val3)) { label = DungeonNames.ForPrefab(((Object)val3).name); if (label != null) { source = ((Object)val3).name; return true; } val3 = val3.parent; } return false; } private static string UsefulDungeonLabel(string text) { string text2 = DungeonNames.SpecificLabel(text); if (text2 == null || Localization.instance == null) { return text2; } if (DungeonNames.SpecificLabel(Localization.instance.Localize(text2)) == null) { return null; } return text2; } private static void SetObject(PinRecord record, ZDOID id) { record.ObjectUser = ((ZDOID)(ref id)).UserID; record.ObjectId = ((ZDOID)(ref id)).ID; record.Id = record.CanonicalId; } private void Report(PinRecord record, float now) { if (record.IsValid() && (!observed.TryGetValue(record.Id, out (int, float) value) || record.Percent != value.Item1 || !(now < value.Item2))) { observed[record.Id] = (record.Percent, now + 8f); Send(serverRpc, Message.Upsert, record.Write); } } private void ScanLive(float now) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) nativeLive.Clear(); foreach (DictionaryEntry track in ui.Tracks) { object value = track.Value; PinData val = ui.TrackPin(value); if (val != null) { PinRecord pinRecord = new PinRecord { Kind = ui.TrackKind(val.m_type), Name = (val.m_name ?? "") }; SetPosition(pinRecord, val.m_pos); SetObject(pinRecord, ui.TrackId(value)); nativeLive[pinRecord.Id] = val; if (!clientRecords.ContainsKey(pinRecord.Id) && !ui.TrackHidden(value)) { Report(pinRecord, now); } } } ScanArtisan(now); } private void MakePin(Minimap map, View view, PinRecord record) { //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_0014: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) PinType val = ui.TypeFor(record); ui.Extend(map, val); internalMapChange++; try { view.Pin = map.AddPin(SeneaLBindings.Position(record), val, Label(record), record.Kind == PinKind.Manual, record.Checked, 0L, default(PlatformUserID)); ownedPins[view.Pin] = record.Id; } finally { internalMapChange--; } ApplyPin(map, view, record); } private void ApplyPin(Minimap map, View view, PinRecord record) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown PinData pin = view.Pin; bool flag = view.Displayed == null || view.Displayed.Name != record.Name || view.Displayed.Icon != record.Icon || view.Displayed.Color != record.Color || view.Displayed.Percent != record.Percent || view.Displayed.Checked != record.Checked; if (view.Displayed != null && view.Displayed.Kind == PinKind.Manual) { ForgetManualStyle(view.Displayed); } pin.m_pos = SeneaLBindings.Position(record); pin.m_checked = record.Checked; string text = Label(record); if (pin.m_name != text) { pin.m_name = text; if (text.Length == 0 && pin.m_NamePinData != null) { if (Object.op_Implicit((Object)(object)pin.m_NamePinData.PinNameGameObject)) { Object.Destroy((Object)(object)pin.m_NamePinData.PinNameGameObject); } pin.m_NamePinData = null; } else if (text.Length > 0) { if (pin.m_NamePinData == null) { pin.m_NamePinData = new PinNameData(pin); } if (Object.op_Implicit((Object)(object)pin.m_NamePinData.PinNameText)) { pin.m_NamePinData.PinNameText.text = Localization.instance.Localize(text); } } } if (record.Kind == PinKind.Manual) { manualStyle[SeneaLBindings.PositionKey(pin.m_pos)] = record; } PinType type = (pin.m_type = ui.TypeFor(record)); if (flag) { Sprite sprite = GetSprite(record, type); if (Object.op_Implicit((Object)(object)sprite)) { pin.m_icon = sprite; } } view.Displayed = record; ui.Refresh(map, pin, flag); } private Sprite GetSprite(PinRecord record, PinType type) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) switch (record.Kind) { case PinKind.Ore: return ui.Sprite("ore"); case PinKind.Dungeon: return ui.Sprite("dungeon"); case PinKind.Ship: return ui.Sprite("ship"); case PinKind.Cart: return ui.Sprite("cart"); case PinKind.Portal: return ui.Sprite("portal"); case PinKind.ArtisanNavigation: case PinKind.ArtisanWreck: case PinKind.ArtisanCrate: return ArtisanSprite(record.Kind); default: { string key = ((record.Icon == "chest") ? "bag" : ((record.Icon == "danger") ? "alert" : record.Icon)); if (Array.IndexOf(SeneaLBindings.CustomIcons, record.Icon) > 0) { return ui.Sprite(key); } return ui.VanillaSprite(Minimap.instance, type); } } } private static string Label(PinRecord pin) { if (pin.Kind != PinKind.Ore || pin.Percent < 0) { return pin.Name; } return pin.Name + " " + pin.Percent + "%"; } private void ForgetManualStyle(PinRecord record) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) long key = SeneaLBindings.PositionKey(SeneaLBindings.Position(record)); if (manualStyle.TryGetValue(key, out var value) && value.Id == record.Id) { manualStyle.Remove(key); } } private void RemoveView(string id) { if (views.TryGetValue(id, out var value)) { views.Remove(id); PinRecord displayed = value.Displayed; if (displayed != null && displayed.Kind == PinKind.Manual) { ForgetManualStyle(value.Displayed); } if (value.Pin != null) { ownedPins.Remove(value.Pin); DropPin(clientMap, value.Pin); } } } private void DropPin(Minimap map, PinData pin) { if (!Object.op_Implicit((Object)(object)map) || pin == null) { return; } internalMapChange++; try { pin.m_save = false; map.RemovePin(pin); } finally { internalMapChange--; } } private void ClearViews() { string[] array = views.Keys.ToArray(); foreach (string id in array) { RemoveView(id); } views.Clear(); ownedPins.Clear(); manualStyle.Clear(); newManual.Clear(); } private void ResetClient() { clientReady = (snapshotStarted = (serverHidesDepletedOre = false)); serverRpc = null; if (ui != null) { ClearViews(); } clientRecords.Clear(); observed.Clear(); absentNativeOre.Clear(); sharedOreByZone.Clear(); oreIndexDirty = false; nativeLive.Clear(); hiddenPins.Clear(); ResetArtisanClient(); clientMap = null; } private void InstallHooks() { //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Expected O, but got Unknown Hook(typeof(Minimap), "AddPin", null, "PinAdded"); Hook(typeof(Minimap), "SetMapData", "BeginMapLoad", null, "EndMapLoad"); Hook(typeof(Minimap), "GetMapData", "BeginMapExport", null, "EndMapExport"); Hook(typeof(Minimap), "GetSharedMapData", "BeginMapExport", null, "EndMapExport"); Hook(typeof(Minimap), "RemovePin", "BeforeRemovePin", null, null, new Type[1] { typeof(PinData) }); Hook(typeof(ZNet), "Shutdown", "BeforeShutdown"); Hook(ui.PinDialog, "Save", "BeginDialog", null, "EndDialog"); Hook(ui.Pins, "CustomOf", null, "CustomStyle"); Hook(ui.Pins, "ColorOf", null, "ColorStyle"); Hook(ui.Pins, "IsMarked", null, "MarkedStyle"); Hook(ui.Pins, "IsHidden", null, "HiddenStyle"); Hook(ui.Pins, "SetCustom", "BeforeSetStyle"); Hook(ui.Pins, "SetColor", "BeforeSetStyle"); Hook(ui.Pins, "SetOnCompass", "BeforeSetStyle"); Hook(ui.Pins, "RemoveCustom", "BeforeRemoveStyle"); HarmonyMethod val = new HarmonyMethod(SeneaLBindings.Method(typeof(SharedPinsPlugin), "FillUnwrittenRockHealth")) { priority = 0 }; harmony.Patch((MethodBase)ui.ReadTargetHealth, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Hook(ui.OreScout, "Refresh", "BeginOreRefresh", null, "EndOreRefresh"); Hook(ui.OreScout, "Place", "BeforePlaceDiscovery"); Hook(ui.OreScout, "Forget", "BeforeForgetDiscovery"); Hook(ui.OreScout, "DungeonName", null, "NameDungeon"); Hook(ui.OreScout, "NearKnown", "BeforeNearKnown"); Hook(ui.LiveMarks, "Toggle", "BeforeToggleLive"); InstallServerSideMapHooks(); BindArtisan(); } private static void FillUnwrittenRockHealth(Component __0, ref int __1, ref int __2, ref bool __result) { SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null) { return; } MineRock5 val = (MineRock5)(object)((__0 is MineRock5) ? __0 : null); if (val != null && Object.op_Implicit((Object)(object)val) && (!__result || (__1 == 1 && __2 == 1))) { ZNetView component = ((Component)val).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && string.IsNullOrEmpty(component.GetZDO().GetString(ZDOVars.s_health, "")) && instance.ui.TryReadInitializedRock(val, out var health, out var maximum)) { __1 = health; __2 = maximum; __result = true; } } } private void InstallServerSideMapHooks() { Type type = SeneaLBindings.RequireType("ServerSideMap.Store"); Hook(type, "IsSharingPin", "DisableServerSideMapPinSharing"); Type type2 = SeneaLBindings.RequireType("ServerSideMap.InitialPinSync"); Hook(type2, "OnReceiveInitialDataPin", "SkipServerSideMapPinTransfer"); Hook(type2, "SendPinsToServer", "SkipServerSideMapPinTransfer"); Type type3 = SeneaLBindings.RequireType("ServerSideMap.UtilityPin"); Hook(type3, "UploadAllPins", "SkipServerSideMapPinTransfer"); Hook(type3, "DownloadPins", "SkipServerSideMapPinTransfer"); } private static bool DisableServerSideMapPinSharing(ref bool __result) { __result = false; return false; } private static bool SkipServerSideMapPinTransfer() { return false; } private void Hook(Type type, string method, string prefix = null, string postfix = null, string finalizer = null, Type[] parameters = null) { harmony.Patch((MethodBase)SeneaLBindings.Method(type, method, parameters), Patch(prefix), Patch(postfix), (HarmonyMethod)null, Patch(finalizer), (HarmonyMethod)null); static HarmonyMethod Patch(string name) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if (name != null) { return new HarmonyMethod(SeneaLBindings.Method(typeof(SharedPinsPlugin), name)); } return null; } } private static void PinAdded(PinData __result) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected I4, but got Unknown SharedPinsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.ClientActive && __result != null && instance.loadingMap == 0 && instance.internalMapChange == 0 && __result.m_save && __result.m_ownerID == 0L) { PinType type = __result.m_type; switch (type - 5) { case 0: case 2: case 5: case 6: case 7: case 8: return; } DialogState dialogState = instance.activeDialog; instance.newManual[__result] = ((dialogState == null) ? new PinRecord { Compass = true } : new PinRecord { Icon = dialogState.Icon, Color = dialogState.Tint, Compass = dialogState.Compass }); } } private static void BeginMapLoad() { if ((Object)(object)Instance != (Object)null) { Instance.loadingMap++; } } private static Exception EndMapLoad(Exception __exception) { SharedPinsPlugin instance = Instance; if ((Object)(object)instance != (Object)null && --instance.loadingMap == 0 && instance.ClientActive) { instance.ClearViews(); } return __exception; } private static void BeginMapExport(out PinData[] __state) { SharedPinsPlugin instance = Instance; __state = (((Object)(object)instance == (Object)null) ? Array.Empty<PinData>() : instance.ownedPins.Keys.Where((PinData pin) => pin.m_save).ToArray()); PinData[] array = __state; for (int num = 0; num < array.Length; num++) { array[num].m_save = false; } } private static Exception EndMapExport(PinData[] __state, Exception __exception) { if (__state != null) { for (int i = 0; i < __state.Length; i++) { __state[i].m_save = true; } } return __exception; } private static bool BeforeRemovePin(PinData __0) { SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null || __0 == null || !instance.ClientActive || instance.internalMapChange != 0 || instance.loadingMap != 0 || !instance.ownedPins.TryGetValue(__0, out var value)) { return true; } if (instance.oreRefresh != 0) { return false; } if (instance.clientRecords.TryGetValue(value, out var value2) && value2.Kind != PinKind.Manual) { if (value2.IsLive && (int)value2.Kind < 6) { instance.ui.ToggleLiveHidden(value2, hideOnly: true); } else { instance.hiddenPins.Add(value); } } return true; } private static void BeforeShutdown() { Instance?.SaveWorld(); } private static void BeginDialog(object __instance, out DialogState __state) { __state = null; SharedPinsPlugin instance = Instance; if (!((Object)(object)instance == (Object)null) && instance.ClientActive && instance.ui.DialogOpen(__instance)) { PinData val = instance.ui.Editing(__instance); if (val == null || instance.ownedPins.ContainsKey(val)) { instance.ui.ReadDialog(__instance, out var icon, out var tint, out var onCompass); __state = new DialogState { Pin = val, Icon = icon, Tint = tint, Compass = onCompass }; instance.activeDialog = __state; } } } private static Exception EndDialog(DialogState __state, Exception __exception) { SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null || __state == null) { return __exception; } instance.activeDialog = null; if (__exception != null || __state.Pin == null || !instance.ownedPins.TryGetValue(__state.Pin, out var value) || !instance.views.TryGetValue(value, out var value2)) { return __exception; } PinRecord pinRecord = (value2.Desired ?? value2.Displayed).Copy(); pinRecord.Icon = __state.Icon; pinRecord.Color = __state.Tint; pinRecord.Compass = __state.Compass; ReadManual(__state.Pin, pinRecord); value2.Desired = pinRecord; return __exception; } private static bool BeforeSetStyle() { return Instance?.activeDialog == null; } private static bool BeforeRemoveStyle(Vector3 __1) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) PinRecord record; return !TryStyle(__1, out record); } private static bool TryStyle(Vector3 position, out PinRecord record) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) SharedPinsPlugin instance = Instance; record = null; if ((Object)(object)instance != (Object)null) { return instance.manualStyle.TryGetValue(SeneaLBindings.PositionKey(position), out record); } return false; } private static void CustomStyle(Vector3 __0, ref int __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (TryStyle(__0, out var record)) { __result = Math.Max(0, Array.IndexOf(SeneaLBindings.CustomIcons, record.Icon)); } } private static void ColorStyle(Vector3 __0, ref int __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (TryStyle(__0, out var record)) { __result = record.Color; } } private static void MarkedStyle(Vector3 __0, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (TryStyle(__0, out var record)) { __result = record.Compass; } } private static void HiddenStyle(Vector3 __0, ref bool __result) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (TryStyle(__0, out var record)) { __result = !record.Compass; } } private static void BeginOreRefresh() { if ((Object)(object)Instance != (Object)null) { Instance.oreRefresh++; } } private static Exception EndOreRefresh(Exception __exception) { if ((Object)(object)Instance != (Object)null) { Instance.oreRefresh--; } return __exception; } private static bool BeforePlaceDiscovery(object __1) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null || !instance.ClientActive) { return true; } if (instance.ui.EntryDungeon(__1)) { Vector3 val = instance.ui.EntryPos(__1); return !instance.clientRecords.ContainsKey(PinRecord.DungeonKey(val.x, val.z)); } return !instance.HasSharedOreMarker(__1); } private static bool BeforeForgetDiscovery(PinData __0) { SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null || __0 == null || !instance.ClientActive || !instance.ownedPins.TryGetValue(__0, out var value) || !instance.clientRecords.TryGetValue(value, out var value2) || !value2.IsDiscovery) { return true; } instance.hiddenPins.Add(value); instance.RemoveView(value); return false; } private static void NameDungeon(Location __0, ref string __result) { SharedPinsPlugin instance = Instance; if (instance != null && instance.ClientActive && TryDungeonLabel(__0, out var label, out var _)) { __result = label; } } private static bool BeforeNearKnown(ref bool __result) { SharedPinsPlugin instance = Instance; if (instance == null || !instance.ClientActive) { return true; } __result = false; return false; } private static bool BeforeToggleLive(PinData __1) { SharedPinsPlugin instance = Instance; if ((Object)(object)instance == (Object)null || !instance.ClientActive || __1 == null || !instance.ownedPins.TryGetValue(__1, out var value) || !instance.clientRecords.TryGetValue(value, out var value2) || !value2.IsLive) { return true; } if ((int)value2.Kind < 6) { instance.ui.ToggleLiveHidden(value2, hideOnly: false); } else { instance.hiddenPins.Add(value); } return false; } private void AcceptOre(ZNetPeer peer, PinRecord pin) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (pin.OrePrefab == 0 || DistanceXZ(pin, peer.m_refPos) > 150f) { return; } ZDO zdo = FindObject(pin); if (!IsSameOre(pin, zdo)) { return; } GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(pin.OrePrefab) : null); if (Object.op_Implicit((Object)(object)OreComponent(val))) { int percent; bool flag = TryReadOrePercent(zdo, val, out percent); if (flag) { pin.Percent = percent; } else if (pin.Percent == 0 || Object.op_Implicit((Object)(object)val.GetComponent<MineRock5>())) { pin.Percent = -1; } pin.Name = CleanOreName(pin.Name); pin.Icon = ""; PublishOre(pin, flag); } } private void CheckLegacyOre(ZNetPeer peer, PinRecord report) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) ZDOMan instance = ZDOMan.instance; if (report.Kind != PinKind.Ore || report.OrePrefab != 0 || instance == null || DistanceXZ(report, peer.m_refPos) > 55f || !TryFindOreAt(report, ui.OreSectors(instance), out var match)) { return; } if (match == null) { Send(peer.m_rpc, Message.OreAbsent, delegate(BinaryWriter writer) { writer.Write(report.LegacyOreQueryKey); }); } else { PublishOre(ReadWorldOre(report, match)); } } private void ReconcileSharedOre(PinRecord pin, List<ZDO>[] sectors) { if (pin.OrePrefab != 0 && pin.IsMissingOre) { return; } ZDO match = ((pin.OrePrefab == 0) ? null : FindObject(pin)); if (!IsSameOre(pin, match) && !TryFindOreAt(pin, sectors, out match)) { return; } if (pin.OrePrefab == 0) { PinRecord replacement = ((match == null) ? null : ReadWorldOre(pin, match)); if (store.ReconcileLegacyOre(pin.Id, replacement, SharedOreThreshold, out var updated)) { RecordChange(pin.Id, null); if (updated != null) { RecordChange(updated.Id, updated); } } } else if (match != null) { PublishOre(ReadWorldOre(pin, match)); } else { PinRecord pinRecord = pin.Copy(); pinRecord.Icon = "ore:missing"; pinRecord.Percent = 0; pinRecord.ObjectUser = 0L; pinRecord.ObjectId = 0u; PublishOre(pinRecord); } } private PinRecord ReadWorldOre(PinRecord source, ZDO zdo) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) PinRecord pinRecord = source.Copy(); pinRecord.OrePrefab = zdo.GetPrefab(); SetPosition(pinRecord, zdo.GetPosition()); SetObject(pinRecord, zdo.m_uid); pinRecord.Icon = ""; GameObject prefab = ZNetScene.instance.GetPrefab(pinRecord.OrePrefab); if (!TryReadOrePercent(zdo, prefab, out pinRecord.Percent)) { pinRecord.Percent = -1; } pinRecord.Name = CleanOreName(pinRecord.Name); return pinRecord; } private void PublishOre(PinRecord ore, bool authoritativePercent = true) { if (store.Upsert(ore, SharedOreThreshold, out var current, authoritativePercent, verifiedOreObject: true)) { RecordChange(ore.Id, current); } } private static string CleanOreName(string name) { return name.Replace("<", " ").Replace(">", " "); } private static Component OreComponent(GameObject prefab) { if (!Object.op_Implicit((Object)(object)prefab)) { return null; } Component component = (Component)(object)prefab.GetComponent<MineRock5>(); if (!Object.op_Implicit((Object)(object)component)) { component = (Component)(object)prefab.GetComponent<MineRock>(); } if (!Object.op_Implicit((Object)(object)component)) { component = (Component)(object)prefab.GetComponent<Destructible>(); } return component; } private static bool IsSameOre(PinRecord pin, ZDO zdo) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !zdo.IsValid()) { return false; } Vector3 position = zdo.GetPosition(); return pin.MatchesOreObject(zdo.GetPrefab(), position.x, position.y, position.z); } private bool TryFindOreAt(PinRecord pin, List<ZDO>[] sectors, out ZDO match) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0191: 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_019f: Unknown result type (might be due to invalid IL or missing references) match = null; if (sectors == null || !Object.op_Implicit((Object)(object)currentNet) || ZNet.m_loadError || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return false; } Vector3 val = SeneaLBindings.Position(pin); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(2f, 0f, 2f); Vector2s zone = ZoneSystem.GetZone(val - val2); Vector2s zone2 = ZoneSystem.GetZone(val + val2); if (!ui.IsZoneGenerated(ZoneSystem.instance, val)) { return false; } int bestRank = 0; bool ambiguous = false; bool flag = false; for (int i = zone.x; i <= zone2.x; i++) { for (int j = zone.y; j <= zone2.y; j++) { uint sector = ZoneSystem.SectorToIndex(i, j).Sector; if (sector == 0 || sector >= sectors.Length) { return false; } List<ZDO> list = sectors[sector]; if (list == null) { continue; } foreach (ZDO item in list) { if (item == null || !item.IsValid()) { continue; } if (pin.OrePrefab != 0) { if (IsSameOre(pin, item)) { match = item; return true; } continue; } Vector3 position = item.GetPosition(); if (pin.DistanceSquared(position.x, position.y, position.z) > 4f) { continue; } GameObject prefab = ZNetScene.instance.GetPrefab(item.GetPrefab()); if (!Object.op_Implicit((Object)(object)prefab)) { flag = true; continue; } Component val3 = OreComponent(prefab); if (Object.op_Implicit((Object)(object)val3)) { string text = ui.OreName(val3); if (!string.IsNullOrEmpty(text)) { int rank = pin.LegacyOreMatchRank(position.x, position.y, position.z, text); PinRecord.ConsiderOreMatch(item, rank, ref match, ref bestRank, ref ambiguous); } } } } } if (!ambiguous) { if (flag) { return bestRank >= 2; } return true; } return false; } private void Awake() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown Instance = this; oreThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("Ore", "StrikeThroughThreshold", -1, new ConfigDescription("Server-wide percentage at or below which an ore pin is struck through. -1 uses SeneaL UI's OreThreshold on the server.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 50), Array.Empty<object>())); removeDepletedOre = ((BaseUnityPlugin)this).Config.Bind<bool>("Ore", "RemoveDepletedOrePins", true, "Hide automatic shared ore pins for every player once they reach the strike-through threshold."); liveMarkers = ((BaseUnityPlugin)this).Config.Bind<bool>("Live markers", "ShareShipsCartsAndPortals", true, "Share SeneaL live tracks discovered by connected players."); artisanMarkers = ((BaseUnityPlugin)this).Config.Bind<bool>("Live markers", "ShareArtisanMarkers", true, "Share supported Artisan Mastery navigation, wreck and crate markers."); harmony = new Harmony("bananas4dragons.valheim.senealsharedpins"); try { ui = new SeneaLBindings(); InstallHooks(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Shared pin hooks resolved. Protocol 2. ServerSideMap marker sharing is overridden to false."); } catch (Exception ex) { harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Logger.LogError((object)("Shared pins disabled because a required hook could not be resolved: " + ex)); ((Behaviour)this).enabled = false; } } private void OnDestroy() { EndSession(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Instance = null; } private void Update() { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < nextTick) { return; } nextTick = realtimeSinceStartup + 0.25f; try { TickNetwork(realtimeSinceStartup); if (ClientActive) { TickClient(realtimeSinceStartup); } } catch (Exception ex) { LogFailure("Shared pin update", ex); } } private void TickNetwork(float now) { ZNet instance = ZNet.instance; World val = (Object.op_Implicit((Object)(object)instance) ? instance.GetWorld() : null); long num = val?.m_uid ?? 0; if ((Object)(object)instance != (Object)(object)currentNet || worldId != num) { EndSession(); currentNet = instance; worldId = num; if (Object.op_Implicit((Object)(object)instance) && num != 0L && instance.IsServer() && instance.IsDedicated()) { LoadWorld(val); } } if (!Object.op_Implicit((Object)(object)instance) || worldId == 0L) { return; } List<ZNetPeer> peers = instance.GetPeers(); foreach (ZNetPeer item in peers) { if (!registered.ContainsKey(item.m_rpc)) { registered.Add(item.m_rpc, item); item.m_rpc.Register<ZPackage>("B4D_SeneaLSharedPins_2", (Action<ZRpc, ZPackage>)Receive); } } KeyValuePair<ZRpc, ZNetPeer>[] array = registered.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<ZRpc, ZNetPeer> keyValuePair = array[i]; if (!peers.Contains(keyValuePair.Value)) { registered.Remove(keyValuePair.Key); subscribers.Remove(keyValuePair.Key); if (serverRpc == keyValuePair.Key) { ResetClient(); } } } if (serverActive) { ScanGalleonMarks(now); if (now >= nextWorldScan) { nextWorldScan = now + 1.5f; RefreshWorldObjects(); } FlushChanges(); if (saveDirty && now >= nextSave) { nextSave = now + 3f; SaveWorld(); } } else if (!instance.IsServer() && Object.op_Implicit((Object)(object)Minimap.instance) && Object.op_Implicit((Object)(object)Player.m_localPlayer) && now >= nextHello) { ZNetPeer val2 = ((IEnumerable<ZNetPeer>)peers).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer peer) => peer.m_server && peer.IsReady())); if (val2 != null && !clientReady) { serverRpc = val2.m_rpc; snapshotStarted = false; nextHello = now + 6f; Send(serverRpc, Message.Hello); } } } private void Receive(ZRpc rpc, ZPackage package) { if (!registered.TryGetValue(rpc, out var value) || !value.IsReady() || (!serverActive && rpc != serverRpc)) { return; } try { byte[] array = package.GetArray(); if (array.Length == 0 || array.Length > 65536) { return; } using BinaryReader binaryReader = new BinaryReader(new MemoryStream(array, writable: false)); Message message = (Message)binaryReader.ReadByte(); if (serverActive) { ReceiveServer(value, message, binaryReader); } else { ReceiveClient(message, binaryReader); } } catch (Exception ex) { LogFailure("Shared pin packet rejected", ex); } } private void ReceiveServer(ZNetPeer peer, Message message, BinaryReader reader) { ZRpc rpc = peer.m_rpc; if (message == Message.Hello) { if (reader.BaseStream.Position == reader.BaseStream.Length) { subscribers.Add(rpc); RefreshWorldObjects(); FlushChanges(); Send(rpc, Message.SnapshotStart); (string, PinRecord)[] changes = store.Pins.Values.Select((PinRecord pinRecord) => (Id: pinRecord.Id, pin: pinRecord)).ToArray(); SendChanges(rpc, changes); Send(rpc, Message.SnapshotEnd, delegate(BinaryWriter writer) { writer.Write(removeDepletedOre.Value); }); } } else { if (!subscribers.Contains(rpc)) { return; } switch (message) { case Message.Upsert: { PinRecord pin = PinRecord.Read(reader); if (reader.BaseStream.Position == reader.BaseStream.Length) { AcceptPin(peer, pin); } break; } case Message.CheckOre: { PinRecord report = PinRecord.Read(reader); if (reader.BaseStream.Position == reader.BaseStream.Length) { CheckLegacyOre(peer, report); } break; } case Message.Delete: { string text = reader.ReadString(); int revision = reader.ReadInt32(); if (reader.BaseStream.Position == reader.BaseStream.Length && Guid.TryParseExact(text, "N", out var _)) { if (store.DeleteManual(text, revision, out var current)) { RecordChange(text, null); } else { Reject(rpc, text, current); } } break; } } } } private void AcceptPin(ZNetPeer peer, PinRecord pin) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) if (pin.Kind == PinKind.Manual) { pin.Name = pin.Name.Replace("<", " ").Replace(">", " "); if (store.Upsert(pin, SharedOreThreshold, out var current)) { RecordChange(pin.Id, current); } else { Reject(peer.m_rpc, pin.Id, current); } return; } if (pin.Kind == PinKind.Ore) { AcceptOre(peer, pin); return; } if (pin.Kind == PinKind.Dungeon) { if (DistanceXZ(pin, peer.m_refPos) > 150f) { return; } } else { ZDO val = FindObject(pin); if (val == null) { return; } GameObject prefab = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(val.GetPrefab()) : null); if (!PrepareLive(pin, val, prefab)) { return; } } pin.Name = pin.Name.Replace("<", " ").Replace(">", " "); if (store.Upsert(pin, SharedOreThreshold, out var current2)) { RecordChange(pin.Id, current2); } } private void RefreshWorldObjects() { ZDOMan instance = ZDOMan.instance; if (instance == null || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } List<ZDO>[] sectors = ui.OreSectors(instance); PinRecord[] array = store.Pins.Values.ToArray(); foreach (PinRecord pinRecord in array) { if (!pinRecord.IsLive && pinRecord.Kind != PinKind.Ore) { continue; } if (pinRecord.Kind == PinKind.Ore) { ReconcileSharedOre(pinRecord, sectors); continue; } ZDO val = FindObject(pinRecord); if (val != null || ObjectMissing(pinRecord.Id)) { if (val != null) { missingObjects.Remove(pinRecord.Id); } PinRecord pinRecord2 = pinRecord.Copy(); GameObject prefab = ((val == null) ? null : ZNetScene.instance.GetPrefab(val.GetPrefab())); PinRecord current; if (val == null || !PrepareLive(pinRecord2, val, prefab)) { store.Pins.Remove(pinRecord.Id); missingObjects.Remove(pinRecord.Id); RecordChange(pinRecord.Id, null); } else if (store.Upsert(pinRecord2, SharedOreThreshold, out current)) { RecordChange(pinRecord.Id, current); } } } } private static bool TryReadOrePercent(ZDO zdo, GameObject prefab, out int percent) { percent = -1; if (!Object.op_Implicit((Object)(object)prefab)) { return false; } MineRock5 component = prefab.GetComponent<MineRock5>(); if (Object.op_Implicit((Object)(object)component)) { percent = OreHealth.ReadRockAreas(zdo.GetString(ZDOVars.s_health, ""), ScaledOreHealth(component.m_health)); } else if (!Object.op_Implicit((Object)(object)prefab.GetComponent<MineRock>())) { Destructible component2 = prefab.GetComponent<Destructible>(); if (Object.op_Implicit((Object)(object)component2) && component2.m_health < 1000000f) { float num = ScaledOreHealth(component2.m_health); percent = OreHealth.Percent(zdo.GetFloat(ZDOVars.s_health, num), num); } } return percent >= 0; } internal static float ScaledOreHealth(float baseHealth) { Game instance = Game.instance; if (!Object.op_Implicit((Object)(object)instance)) { return baseHealth; } return baseHealth * (1f + (float)Game.m_worldLevel * instance.m_worldLevelMineHPMultiplier); } private bool ObjectMissing(string id) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (missingObjects.TryGetValue(id, out var value)) { return realtimeSinceStartup - value >= 4f; } missingObjects.Add(id, realtimeSinceStartup); return false; } private bool PrepareLive(PinRecord pin, ZDO zdo, GameObject prefab) { //IL_0108: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)prefab)) { return false; } switch (pin.Kind) { case PinKind.Ship: if (!liveMarkers.Value || !Object.op_Implicit((Object)(object)prefab.GetComponent<Ship>())) { return false; } break; case PinKind.Cart: if (!liveMarkers.Value || !Object.op_Implicit((Object)(object)prefab.GetComponent<Vagon>())) { return false; } break; case PinKind.Portal: { if (!liveMarkers.Value || !Object.op_Implicit((Object)(object)prefab.GetComponent<TeleportWorld>())) { return false; } string text = zdo.GetString("tag", ""); pin.Name = ((text.Length == 0) ? "$piece_portal" : text.Substring(0, Math.Min(text.Length, 200))); pin.Name = pin.Name.Replace("<", " ").Replace(">", " "); break; } default: if (!artisanMarkers.Value || !ValidateArtisan(pin, zdo, prefab)) { return false; } break; } SetPosition(pin, zdo.GetPosition()); pin.Checked = false; return true; } private ZDO FindObject(PinRecord pin) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!ui.KnowsObjectUser(pin.ObjectUser)) { return null; } ZDOMan instance = ZDOMan.instance; if (instance == null) { return null; } return instance.GetZDO(new ZDOID(pin.ObjectUser, pin.ObjectId)); } private static void SetPosition(PinRecord pin, Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) pin.X = position.x; pin.Y = position.y; pin.Z = position.z; } private static float DistanceXZ(PinRecord pin, Vector3 position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = pin.X - position.x; float num2 = pin.Z - position.z; return Mathf.Sqrt(num * num + num2 * num2); } private static void Send(ZRpc rpc, Message message, Action<BinaryWriter> body = null) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown using MemoryStream memoryStream = new MemoryStream(); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write((byte)message); body?.Invoke(binaryWriter); binaryWriter.Flush(); rpc.Invoke("B4D_SeneaLSharedPins_2", new object[1] { (object)new ZPackage(memoryStream.ToArray()) }); } private static void SendChanges(ZRpc rpc, (string Id, PinRecord Pin)[] changes) { for (int start = 0; start < changes.Length; start += 48) { int count = Math.Min(48, changes.Length - start); Send(rpc, Message.Changes, delegate(BinaryWriter writer) { writer.Write(count); for (int i = start; i < start + count; i++) { WriteChange(writer, changes[i].Id, changes[i].Pin); } }); } } private static void WriteChange(BinaryWriter writer, string id, PinRecord pin) { writer.Write(pin != null); if (pin != null) { pin.Write(writer); } else { writer.Write(id); } } private static void Reject(ZRpc rpc, string id, PinRecord current) { Send(rpc, Message.Reject, delegate(BinaryWriter writer) { WriteChange(writer, id, current); }); } private void RecordChange(string id, PinRecord pin) { serverChanges[id] = pin; saveDirty = true; } private void FlushChanges() { if (serverChanges.Count == 0) { return; } (string, PinRecord)[] changes = serverChanges.Select((KeyValuePair<string, PinRecord> item) => (Key: item.Key, Value: item.Value)).ToArray(); serverChanges.Clear(); foreach (ZRpc subscriber in subscribers) { SendChanges(subscriber, changes); } } private void LoadWorld(World world) { savePath = Path.ChangeExtension(world.GetDBPath(), ".seneal-shared-pins"); try { if (File.Exists(savePath)) { using BinaryReader binaryReader = new BinaryReader(File.OpenRead(savePath)); int num = binaryReader.ReadInt32(); if (binaryReader.BaseStream.Length > 16777216 || (num != 1 && num != 2) || binaryReader.ReadInt64() != worldId) { throw new InvalidDataException("Shared pin file has an invalid header."); } int num2 = binaryReader.ReadInt32(); if (num2 < 0 || num2 > 10000) { throw new InvalidDataException("Shared pin file has an invalid record count."); } for (int i = 0; i < num2; i++) { PinRecord pinRecord = PinRecord.Read(binaryReader, num >= 2); store.Pins.Add(pinRecord.Id, pinRecord); } if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length) { throw new InvalidDataException("Shared pin file has unexpected trailing data."); } backupLegacySave = num == 1; } serverActive = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Shared pin server ready. Loaded " + store.Pins.Count + " records.")); } catch (Exception ex) { store.Pins.Clear(); ((BaseUnityPlugin)this).Logger.LogError((object)("Shared pins disabled for this world. Existing pin data was left in place: " + ex)); } } private void SaveWorld() { if (!serverActive || !saveDirty) { return; } try { Directory.CreateDirectory(Path.GetDirectoryName(savePath)); string text = savePath + ".tmp"; using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None)) { using BinaryWriter binaryWriter = new BinaryWriter(fileStream); binaryWriter.Write(2); binaryWriter.Write(worldId); binaryWriter.Write(store.Pins.Count); foreach (PinRecord value in store.Pins.Values) { value.Write(binaryWriter); } binaryWriter.Flush(); fileStream.Flush(flushToDisk: true); } string text2 = savePath + ".v1.bak"; if (File.Exists(savePath)) { File.Replace(text, savePath, (backupLegacySave && !File.Exists(text2)) ? text2 : null); } else { File.Move(text, savePath); } backupLegacySave = false; saveDirty = false; } catch (Exception ex) { LogFailure("Shared pin save", ex); } } private void EndSession() { SaveWorld(); ResetClient(); registered.Clear(); subscribers.Clear(); store.Pins.Clear(); serverChanges.Clear(); missingObjects.Clear(); serverActive = (saveDirty = (backupLegacySave = false)); savePath = null; galleons.Clear(); galleonSector = 0; nextGalleonScan = 0f; nextHello = (nextWorldScan = (nextSave = 0f)); } private void LogFailure(string operation, Exception ex) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(realtimeSinceStartup < nextFailureLog)) { nextFailureLog = realtimeSinceStartup + 10f; ((BaseUnityPlugin)this).Logger.LogWarning((object)(operation + ": " + ex)); } } } internal static class DungeonNames { internal const string EntranceText = "dungeon:entrance"; internal const string DiscoveryLabel = "dungeon:discovery"; internal const int EntrancePriority = 3; private static readonly Regex RichText = new Regex("<[^<>]*>"); private static readonly HashSet<string> Generic = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Dungeon", "Cave", "Crypt", "Mine", "Entrance", "Enter", "Exit", "Leave", "$location_enter", "$location_exit", "$location_leave", "$location_dungeon" }; private static readonly Dictionary<string, string> Vanilla = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["TrollCave02"] = "Troll Cave", ["Crypt2"] = "Burial Chambers", ["Crypt3"] = "Burial Chambers", ["Crypt4"] = "Burial Chambers", ["SunkenCrypt1"] = "Sunken Crypt", ["SunkenCrypt2"] = "Sunken Crypt", ["SunkenCrypt3"] = "Sunken Crypt", ["SunkenCrypt4"] = "Sunken Crypt", ["MountainCave01"] = "Frost Cave", ["MountainCave02"] = "Frost Cave", ["Mistlands_DvergrTownEntrance1"] = "Infested Mine", ["Mistlands_DvergrTownEntrance2"] = "Infested Mine", ["Mistlands_DvergrBossEntrance1"] = "Infested Citadel", ["Hildir_crypt"] = "Smouldering Tomb", ["Hildir_cave"] = "Howling Cavern", ["Hildir_plainsfortress"] = "Sealed Tower" }; private static readonly Dictionary<string, string> OreMines = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["BOM_FlintMine"] = "Flint Mine", ["BOM_CopperMine"] = "Copper Mine", ["BOM_TinMine"] = "Tin Mine", ["BOM_CoalMine"] = "Coal Mine", ["BOM_IronMine"] = "Iron Mine", ["BOM_SilverMine"] = "Silver Mine", ["BOM_BlackMetalMine"] = "Black Metal Mine", ["BOM_FlametalMine"] = "Flametal Mine" }; internal static string SpecificLabel(string text) { if (string.IsNullOrWhiteSpace(text) || text.Length > 512) { return null; } string text2 = RichText.Replace(text, "").Trim(); if (text2.Length == 0 || text2.Length > 200 || Generic.Contains(text2) || (text2.StartsWith("[", StringComparison.Ordinal) && text2.EndsWith("]", StringComparison.Ordinal))) { return null; } return text2; } internal static int Priority(string source, string label) { if (source == "dungeon:entrance" && SpecificLabel(label) != null) { return 3; } if (source == "dungeon:discovery" && SpecificLabel(label) != null) { return 2; } return (ForPrefab(source) != null) ? 1 : 0; } internal static string ForPrefab(string prefab) { if (string.IsNullOrEmpty(prefab)) { return null; } if (prefab.EndsWith("(Clone)", StringComparison.Ordinal)) { prefab = prefab.Substring(0, prefab.Length - "(Clone)".Length).TrimEnd(); } if (Vanilla.TryGetValue(prefab, out var value)) { return value; } if (!prefab.EndsWith("01", StringComparison.Ordinal) && !prefab.EndsWith("02", StringComparison.Ordinal)) { return null; } if (!OreMines.TryGetValue(prefab.Substring(0, prefab.Length - 2), out var value2)) { return null; } return value2; } } internal static class OreHealth { internal static bool TryGetDisplayHealth(double health, double maximum, out int displayHealth, out int displayMaximum) { displayHealth = (displayMaximum = 0); if (health < 0.0 || double.IsNaN(health) || health > 2147483647.0 || maximum <= 0.0 || double.IsNaN(maximum) || maximum > 2147483647.0) { return false; } displayHealth = (int)Math.Ceiling(health); displayMaximum = (int)Math.Ceiling(maximum); return true; } internal static int Percent(double health, double maximum) { if (double.IsNaN(health) || double.IsInfinity(health) || maximum <= 0.0 || double.IsNaN(maximum) || double.IsInfinity(maximum)) { return -1; } if (health <= 0.0) { return 0; } return Math.Max(1, (int)Math.Round(Math.Min(1.0, health / maximum) * 100.0)); } internal static int ReadRockAreas(string encoded, float areaMaximum) { if (float.IsNaN(areaMaximum) || float.IsInfinity(areaMaximum) || areaMaximum <= 0f) { return -1; } if (string.IsNullOrEmpty(encoded)) { return 100; } byte[] array; try { array = Convert.FromBase64String(encoded); } catch (FormatException) { return -1; } if (array.Length < 8 || (array.Length - 4) % 4 != 0) { return -1; } using BinaryReader binaryReader = new BinaryReader(new MemoryStream(array, writable: false)); int num = binaryReader.ReadInt32(); if (num != (array.Length - 4) / 4) { return -1; } double num2 = 0.0; for (int i = 0; i < num; i++) { float num3 = binaryReader.ReadSingle(); if (float.IsNaN(num3) || float.IsInfinity(num3)) { return -1; } num2 += (double)Math.Max(0f, num3); } return Percent(num2, (double)areaMaximum * (double)num); } } internal enum PinKind : byte { Manual, Ore, Dungeon, Ship, Cart, Portal, ArtisanNavigation, ArtisanWreck, ArtisanCrate } internal sealed class PinRecord { internal string Id = ""; internal string Name = ""; internal PinKind Kind; internal int Revision; internal float X; internal float Y; internal float Z; internal string Icon = ""; internal int Color; internal bool Checked; internal bool Compass; internal int Percent = -1; internal long ObjectUser; internal uint ObjectId; internal int OrePrefab; internal const string MissingOreIcon = "ore:missing"; internal const float LegacyOreRadius = 2f; internal bool IsMissingOre { get { if (Kind == PinKind.Ore) { return Icon == "ore:missing"; } return false; } } internal bool IsLive => (int)Kind >= 3; internal bool IsDiscovery { get { if (Kind != PinKind.Ore) { return Kind == PinKind.Dungeon; } return true; } } internal string ObjectKey => ObjectUser.ToString(CultureInfo.InvariantCulture) + ":" + ObjectId.ToString(CultureInfo.InvariantCulture); internal string CanonicalId { get { if (Kind != PinKind.Dungeon) { if (OrePrefab == 0) { byte kind = (byte)Kind; return kind.ToString(CultureInfo.InvariantCulture) + ":" + ObjectKey; } return OreKey(OrePrefab, X, Y, Z); } return DungeonKey(X, Z); } } internal string LegacyOreQueryKey => Id + ":" + OreKey(0, X, Y, Z); internal PinRecord Copy() { return (PinRecord)MemberwiseClone(); } internal bool SameDisplay(PinRecord other) { if (Name == other.Name && Kind == other.Kind && X == other.X && Y == other.Y && Z == other.Z && Icon == other.Icon && Color == other.Color && Checked == other.Checked && Compass == other.Compass) { return Percent == other.Percent; } return false; } internal bool IsHiddenByOrePolicy(bool removeDepleted) { if (Kind == PinKind.Ore) { if (!IsMissingOre) { if (removeDepleted) { return Checked; } return false; } return true; } return false; } internal float DistanceSquared(float x, float y, float z) { float num = X - x; float num2 = Y - y; float num3 = Z - z; return num * num + num2 * num2 + num3 * num3; } internal bool MatchesOreObject(int prefab, float x, float y, float z) { if (OrePrefab != 0 && OrePrefab == prefab && X == x && Y == y) { return Z == z; } return false; } internal int LegacyOreMatchRank(float x, float y, float z, string name) { float num = DistanceSquared(x, y, z); bool flag = !string.IsNullOrWhiteSpace(Name) && string.Equals(OreNameStem(Name), OreNameStem(name), StringComparison.OrdinalIgnoreCase); if (num <= 1f / 64f) { if (!flag) { return 2; } return 3; } if (!flag || !(num <= 4f)) { return 0; } return 1; } internal static void ConsiderOreMatch<T>(T candidate, int rank, ref T match, ref int bestRank, ref bool ambiguous) where T : class { if (rank != 0 && rank >= bestRank) { if (rank == bestRank) { ambiguous = true; return; } match = candidate; bestRank = rank; ambiguous = false; } } private static string OreNameStem(string name) { string text = (name ?? "").Trim(); if (text.EndsWith(" ore deposit", StringComparison.OrdinalIgnoreCase)) { return text.Substring(0, text.Length - 12); } if (text.EndsWith(" deposit", StringComparison.OrdinalIgnoreCase)) { return text.Substring(0, text.Length - 8); } if (text.EndsWith(" ore", StringComparison.OrdinalIgnoreCase)) { return text.Substring(0, text.Length - 4); } return text; } internal static string OreKey(int prefab, float x, float y, float z) { return string.Format(CultureInfo.InvariantCulture, "o:{0:x8}:{1:R}:{2:R}:{3:R}", prefab, (x == 0f) ? 0f : x, (y == 0f) ? 0f : y, (z == 0f) ? 0f : z); } internal static string DungeonKey(float x, float z) { return "d:" + Math.Round(x).ToString(CultureInfo.InvariantCulture) + ":" + Math.Round(z).ToString(CultureInfo.InvariantCulture); } internal bool IsValid() { if (Name.Length > 200 || Id.Length > 90 || Icon.Length > 80 || Math.Abs(X) > 50000f || Math.Abs(Y) > 50000f || Math.Abs(Z) > 50000f || float.IsNaN(X) || float.IsNaN(Y) || float.IsNaN(Z) || Color < 0 || Color > 7 || Percent < -1 || Percent > 100 || (int)Kind > 8 || Revision < 0 || (OrePrefab != 0 && Kind != PinKind.Ore)) { return false; } if (Kind == PinKind.Manual) { if (Guid.TryParseExact(Id, "N", out var _)) { return Percent == -1; } return false; } if (Id == CanonicalId) { if (Kind != PinKind.Dungeon && OrePrefab == 0) { if (ObjectId != 0) { return ObjectUser != 0; } return false; } return true; } return false; } internal void Write(BinaryWriter writer) { Write(writer, includeOrePrefab: true); } internal void Write(BinaryWriter writer, bool includeOrePrefab) { writer.Write(Id); writer.Write((byte)Kind); writer.Write(Revision); writer.Write(Name); writer.Write(X); writer.Write(Y); writer.Write(Z); writer.Write(Icon); writer.Write(Color); writer.Write(Checked); writer.Write(Compass); writer.Write(Percent); writer.Write(ObjectUser); writer.Write(ObjectId); if (includeOrePrefab) { writer.Write(OrePrefab); } } internal static PinRecord Read(BinaryReader reader, bool includeOrePrefab = true) { PinRecord pinRecord = new PinRecord { Id = reader.ReadString(), Kind = (PinKind)reader.ReadByte(), Revision = reader.ReadInt32(), Name = reader.ReadString(), X = reader.ReadSingle(), Y = reader.ReadSingle(), Z = reader.ReadSingle(), Icon = reader.ReadString(), Color = reader.ReadInt32(), Checked = reader.ReadBoolean(), Compass = reader.ReadBoolean(), Percent = reader.ReadInt32(), ObjectUser = reader.ReadInt64(), ObjectId = reader.ReadUInt32() }; if (includeOrePrefab) { pinRecord.OrePrefab = reader.ReadInt32(); } if (!pinRecord.IsValid()) { throw new InvalidDataException("Invalid shared pin record."); } return pinRecord; } } internal sealed class PinStore { internal const int Capacity = 10000; internal readonly Dictionary<string, PinRecord> Pins = new Dictionary<string, PinRecord>(StringComparer.Ordinal); internal bool Upsert(PinRecord candidate, int threshold, out PinRecord current, bool authoritativeOre = false, bool verifiedOreObject = false) { Pins.TryGetValue(candidate.Id, out current); if (!candidate.IsValid() || (current == null && Pins.Count >= 10000)) { return false; } PinRecord pinRecord = candidate.Copy(); if (pinRecord.Kind == PinKind.Manual) { if (pinRecord.Revision != (current?.Revision ?? 0)) { return false; } } else if (pinRecord.Kind == PinKind.Ore) { if (current != null) { pinRecord = current.Copy(); if (authoritativeOre || verifiedOreObject) { pinRecord.Icon = candidate.Icon; pinRecord.ObjectUser = candidate.ObjectUser; pinRecord.ObjectId = candidate.ObjectId; } if (candidate.Percent >= 0 && (authoritativeOre || current.Percent < 0 || candidate.Percent < current.Percent)) { pinRecord.Percent = candidate.Percent; } if ((authoritativeOre || verifiedOreObject) && current.IsMissingOre && !candidate.IsMissingOre) { pinRecord.Percent = candidate.Percent; } } pinRecord.Checked = pinRecord.Percent >= 0 && pinRecord.Percent <= threshold; } else if (pinRecord.Kind == PinKind.Dungeon) { int num = DungeonNames.Priority(pinRecord.Icon, pinRecord.Name); string text = DungeonNames.ForPrefab(pinRecord.Icon); if (num > 1) { text = DungeonNames.SpecificLabel(pinRecord.Name); } if (text != null) { pinRecord.Name = text; } else { pinRecord.Icon = ""; } if (current != null) { if (num <= DungeonNames.Priority(current.Icon, current.Name)) { return false; } pinRecord = current.Copy(); pinRecord.Name = text; pinRecord.Icon = candidate.Icon; } } if (current != null && pinRecord.SameDisplay(current) && pinRecord.ObjectUser == current.ObjectUser && pinRecord.ObjectId == current.ObjectId) { return false; } pinRecord.Revision = (current?.Revision ?? 0) + 1; Pins[pinRecord.Id] = pinRecord; current = pinRecord; return true; } internal bool DeleteManual(string id, int revision, out PinRecord current) { if (!Pins.TryGetValue(id, out current) || current.Kind != PinKind.Manual || current.Revision != revision) { return false; } Pins.Remove(id); return true; } internal bool ReconcileLegacyOre(string id, PinRecord replacement, int threshold, out PinRecord updated) { updated = null; if (!Pins.TryGetValue(id, out var value) || value.Kind != PinKind.Ore || value.OrePrefab != 0 || (replacement != null && (replacement.OrePrefab == 0 || !replacement.IsValid()))) { return false; } Pins.Remove(id); if (replacement != null && Upsert(replacement, threshold, out var current, authoritativeOre: true)) { updated = current; } return true; } } internal sealed class SeneaLBindings { internal static readonly string[] CustomIcons = new string[5] { "", "ore", "chest", "danger", "ship" }; internal readonly Type Pins = RequireType("SeneaLUI.Map.MapPins"); internal readonly Type OreScout = RequireType("SeneaLUI.Map.OreScout"); internal readonly Type LiveMarks = RequireType("SeneaLUI.Map.LiveMarks"); internal readonly Type PinDialog = RequireType("SeneaLUI.Map.PinDialog"); internal readonly Type MapView = RequireType("SeneaLUI.Map.MapView"); private readonly MethodInfo dungeonWanted; private readonly MethodInfo zoneGenerated; private readonly MethodInfo oreName; private readonly MethodInfo saveOreDiscoveries; private readonly PropertyInfo detectionRadius; private readonly MethodInfo extend; private readonly MethodInfo sprite; private readonly MethodInfo invalidate; private readonly MethodInfo request; private readonly MethodInfo percent; private readonly MethodInfo liveOn; private readonly MethodInfo liveKey; private readonly MethodInfo saveLiveHidden; private readonly MethodInfo vanillaSprite; private readonly FieldInfo liveHidden; private readonly FieldInfo rockAreas; private readonly FieldInfo rockAreaHealth; internal readonly MethodInfo ReadTargetHealth; private readonly FieldInfo known; private readonly FieldInfo rocks; private readonly FieldInfo places; private readonly FieldInfo tracked; private readonly FieldInfo player; private readonly FieldInfo entryPin; private readonly FieldInfo entryId; private readonly FieldInfo entryPos; private readonly FieldInfo entryZdo; private readonly FieldInfo entryDungeon; private readonly FieldInfo entryOff; private readonly FieldInfo entryName; private readonly FieldInfo trackPin; private readonly FieldInfo trackId; private readonly FieldInfo trackHidden; private readonly FieldInfo customIcons; private readonly FieldInfo selected; private readonly FieldInfo color; private readonly FieldInfo compass; private readonly FieldInfo editing; private readonly PropertyInfo dialogOpen; private readonly PropertyInfo compassValue; private readonly PropertyInfo oreDetect; private readonly PropertyInfo orePin; private readonly PropertyInfo oreThreshold; private readonly PropertyInfo allowOre; private readonly FieldInfo liveShowHidden; private readonly FieldInfo oreType; private readonly FieldInfo dungeonType; private readonly FieldInfo vehicleType; private readonly FieldInfo shipType; private readonly FieldInfo cartType; private readonly FieldInfo portalType; private readonly FieldInfo mapPins; private readonly FieldInfo namePin; private readonly FieldInfo mapUpdate; private readonly FieldInfo knownObjectUsers; private readonly FieldInfo oreSectors; internal const BindingFlags Members = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; internal bool CanShowDiscoveries { get { if ((bool)allowOre.GetValue(null)) { return ((ConfigEntry<bool>)orePin.GetValue(null)).Value; } return false; } } internal bool CanReportDiscoveries { get { if (CanShowDiscoveries) { return ((ConfigEntry<bool>)oreDetect.GetValue(null)).Value; } return false; } } internal float DetectionRadius => (float)detectionRadius.GetValue(null); internal bool WantsDungeons => (bool)dungeonWanted.Invoke(null, new object[1] { "dungeon" }); internal IDictionary Known => (IDictionary)known.GetValue(null); internal int OreThreshold => (oreThreshold.GetValue(null) as ConfigEntry<int>)?.Value ?? 5; internal IEnumerable<Location> DungeonPlaces => (IEnumerable<Location>)places.GetValue(null); internal IDictionary Rocks => (IDictionary)rocks.GetValue(null); internal IDictionary Tracks => (IDictionary)tracked.GetValue(null); internal bool OwnPlayerLoaded => player.GetValue(null) == Player.m_localPlayer; internal SeneaLBindings() { Type nestedType = OreScout.GetNestedType("Entry", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); Type nestedType2 = LiveMarks.GetNestedType("Track", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); extend = Method(Pins, "Extend"); sprite = Method(RequireType("SeneaLUI.Theme.Icons"), "Get", new Type[1] { typeof(string) }); invalidate = Method(Pins, "Invalidate"); request = Method(MapView, "RequestPins"); percent = Method(OreScout, "Percent"); oreName = Method(OreScout, "OreName", new Type[1] { typeof(Component) }); saveOreDiscoveries = Method(OreScout, "Save", new Type[1] { typeof(Player) }); Type type = RequireType("SeneaLUI.Hud.TargetHpView"); Type type2 = typeof(int).MakeByRefType(); ReadTargetHealth = Method(type, "Read", new Type[3] { typeof(Component), type2, type2 }); rockAreas = Field(typeof(MineRock5), "m_hitAreas"); rockAreaHealth = Field(rockAreas.FieldType.GetGenericArguments()[0], "m_health"); liveOn = Method(LiveMarks, "On"); liveHidden = Field(LiveMarks, "Hidden"); liveKey = Method(LiveMarks, "KeyOf"); saveLiveHidden = Method(LiveMarks, "SaveHidden"); vanillaSprite = Method(typeof(Minimap), "GetSprite"); liveShowHidden = Field(LiveMarks, "ShowHidden"); known = Field(OreScout, "Known"); rocks = Field(OreScout, "Live"); places = Field(OreScout, "Places"); player = Field(OreScout, "_of"); tracked = Field(LiveMarks, "Tracks"); entryPin = Field(nestedType, "Pin"); entryId = Field(nestedType, "Id"); entryPos = Field(nestedType, "Pos"); entryZdo = Field(nestedType, "Zdo"); entryDungeon = Field(nestedType, "Dungeon"); entryOff = Field(nestedType, "Off"); entryName = Field(nestedType, "Name"); trackPin = Field(nestedType2, "Pin"); trackId = Field(nestedType2, "Id"); trackHidden = Field(nestedType2, "Hid"); customIcons = Field(PinDialog, "Customs"); selected = Field(PinDialog, "_selected"); color = Field(PinDialog, "_color"); compass = Field(PinDialog, "_compass"); editing = Field(PinDialog, "_edit"); dialogOpen = PinDialog.GetProperty("Open", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException(PinDialog.FullName, "Open"); compassValue = compass.FieldType.GetProperty("Value", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException(compass.FieldType.FullName, "Value"); Type type3 = RequireType("SeneaLUI.Plugin"); oreDetect = Property(type3, "OreDetect"); orePin = Property(type3, "OrePin"); oreThreshold = Property(type3, "OreThreshold"); Type type4 = RequireType("SeneaLUI.Net.Policy"); allowOre = Property(type4, "CanOreDetect"); detectionRadius = Property(type4, "OreRadius"); dungeonWanted = Method(OreScout, "Wanted", new Type[1] { typeof(string) }); zoneGenerated = Method(typeof(ZoneSystem), "IsZoneGenerated", new Type[1] { typeof(Vector2s) }); oreType = Field(Pins, "Ore"); dungeonType = Field(Pins, "Dungeon"); vehicleType = Field(Pins, "Vehicle"); shipType = Field(LiveMarks, "PinShip"); cartType = Field(LiveMarks, "PinCart"); portalType = Field(LiveMarks, "PinPortal"); mapPins = Field(typeof(Minimap), "m_pins"); namePin = Field(typeof(Minimap), "m_namePin"); mapUpdate = Field(typeof(Minimap), "m_pinUpdateRequired"); knownObjectUsers = Field(typeof(ZDOID), "m_userIDs"); oreSectors = Field(typeof(ZDOMan), "m_objectsBySector"); } internal static Type FindType(string name) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { Type type = assemblies[i