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 PortalTracking v1.0.0
BepInEx\plugins\PortalTracking.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PortalTracking")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+31490a69edb7828a6a46bce36461cbbde547abc4")] [assembly: AssemblyProduct("PortalTracking")] [assembly: AssemblyTitle("PortalTracking")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PortalTracking { internal static class EffectiveConfig { private static bool _fromServer; private static bool _clusterHubs; private static float _hubRadius; private static HubIconType _hubIcon; internal static bool ClusterHubs { get { if (!_fromServer) { return PortalTrackingPlugin.ClusterHubs.Value; } return _clusterHubs; } } internal static float HubRadius => Mathf.Clamp(_fromServer ? _hubRadius : PortalTrackingPlugin.HubRadius.Value, 0f, 500f); internal static HubIconType HubIcon { get { if (!_fromServer) { return PortalTrackingPlugin.HubIcon.Value; } return _hubIcon; } } internal static void ClearServerOverrides() { _fromServer = false; } internal static void ApplyFromServer(bool overridesEnabled, bool clusterHubs, float hubRadius, HubIconType hubIcon) { if (!overridesEnabled) { _fromServer = false; return; } _fromServer = true; _clusterHubs = clusterHubs; _hubRadius = hubRadius; _hubIcon = hubIcon; } } internal static class PortalCatalog { internal readonly struct PrefabInfo { public readonly string Prefab; public readonly string Label; public readonly int Hash; public PrefabInfo(string prefab, string label) { Prefab = prefab; Label = label; Hash = StringExtensionMethods.GetStableHashCode(prefab); } } internal static readonly PrefabInfo[] Prefabs = new PrefabInfo[3] { new PrefabInfo("portal_wood", "Portal"), new PrefabInfo("portal_stone", "Stone portal"), new PrefabInfo("portal", "Portal") }; private static readonly Dictionary<int, PrefabInfo> ByHash = BuildLookup(); private static Dictionary<int, PrefabInfo> BuildLookup() { Dictionary<int, PrefabInfo> dictionary = new Dictionary<int, PrefabInfo>(); PrefabInfo[] prefabs = Prefabs; for (int i = 0; i < prefabs.Length; i++) { PrefabInfo value = prefabs[i]; dictionary[value.Hash] = value; } return dictionary; } internal static bool TryGet(int prefabHash, out PrefabInfo info) { return ByHash.TryGetValue(prefabHash, out info); } internal static bool IsPortalPrefab(int prefabHash) { if (ByHash.ContainsKey(prefabHash)) { return true; } try { if ((Object)(object)Game.instance != (Object)null && Game.instance.PortalPrefabHash != null) { return Game.instance.PortalPrefabHash.Contains(prefabHash); } } catch { } return false; } internal static string TypeLabel(int prefabHash) { if (TryGet(prefabHash, out var info)) { return info.Label; } return "Portal"; } internal static string DisplayName(string tag) { if (!string.IsNullOrWhiteSpace(tag)) { return tag.Trim(); } return string.Empty; } internal static int HubIconPrefabHash() { return StringExtensionMethods.GetStableHashCode((EffectiveConfig.HubIcon == HubIconType.Stone) ? "portal_stone" : "portal_wood"); } } internal sealed class DisplayPin { public ZDOID Key; public Vector3 Position; public string Name = string.Empty; public int PrefabHash; public int MemberCount = 1; } internal static class PortalHubs { internal static List<DisplayPin> Build(IReadOnlyList<PortalSnapshot> portals) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0105: 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_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024f: Unknown result type (might be due to invalid IL or missing references) List<DisplayPin> list = new List<DisplayPin>(); if (portals == null || portals.Count == 0) { return list; } List<PortalSnapshot> list2 = new List<PortalSnapshot>(portals.Count); foreach (PortalSnapshot portal in portals) { if (IsSanePosition(portal.Position)) { list2.Add(portal); } } if (list2.Count == 0) { return list; } float hubRadius = EffectiveConfig.HubRadius; if (hubRadius <= 0f || !EffectiveConfig.ClusterHubs) { foreach (PortalSnapshot item in list2) { list.Add(ToSingle(item)); } return list; } int count = list2.Count; int[] parent = new int[count]; for (int i = 0; i < count; i++) { parent[i] = i; } float num = hubRadius * hubRadius; for (int j = 0; j < count; j++) { for (int k = j + 1; k < count; k++) { if (HorizontalDistanceSq(list2[j].Position, list2[k].Position) <= num) { Union(j, k); } } } Dictionary<int, List<int>> dictionary = new Dictionary<int, List<int>>(); for (int l = 0; l < count; l++) { int key = Find(l); if (!dictionary.TryGetValue(key, out var value)) { value = (dictionary[key] = new List<int>()); } value.Add(l); } foreach (List<int> value2 in dictionary.Values) { if (value2.Count == 1) { list.Add(ToSingle(list2[value2[0]])); continue; } Vector3 val = Vector3.zero; ZDOID id = list2[value2[0]].Id; foreach (int item2 in value2) { val += list2[item2].Position; } list.Add(new DisplayPin { Key = id, Position = val / (float)value2.Count, Name = "Hub", PrefabHash = PortalCatalog.HubIconPrefabHash(), MemberCount = value2.Count }); } return list; int Find(int num2) { while (parent[num2] != num2) { parent[num2] = parent[parent[num2]]; num2 = parent[num2]; } return num2; } void Union(int a, int b) { a = Find(a); b = Find(b); if (a != b) { parent[b] = a; } } } private static DisplayPin ToSingle(PortalSnapshot portal) { //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new DisplayPin { Key = portal.Id, Position = portal.Position, Name = (portal.Name ?? string.Empty), PrefabHash = portal.PrefabHash, MemberCount = 1 }; } private static float HorizontalDistanceSq(Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) float num = a.x - b.x; float num2 = a.z - b.z; return num * num + num2 * num2; } private static bool IsSanePosition(Vector3 pos) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (float.IsNaN(pos.x) || float.IsNaN(pos.y) || float.IsNaN(pos.z)) { return false; } if (float.IsInfinity(pos.x) || float.IsInfinity(pos.y) || float.IsInfinity(pos.z)) { return false; } if (((Vector3)(ref pos)).sqrMagnitude < 0.01f) { return false; } return true; } } internal static class PortalPins { private sealed class MarkerState { public Vector3 Pos; public string Name = string.Empty; public int PrefabHash; public GameObject? Marker; } private static readonly Dictionary<ZDOID, MarkerState> Markers = new Dictionary<ZDOID, MarkerState>(); private static readonly Dictionary<int, Sprite?> SpriteCache = new Dictionary<int, Sprite>(); private static Sprite? _fallbackSprite; private static int _lastLoggedCount = -1; private static bool _loggedDrawError; private static Font? _mapFont; internal static void Clear() { foreach (MarkerState value in Markers.Values) { DestroyMarker(value); } Markers.Clear(); _lastLoggedCount = -1; } internal static void Apply(IReadOnlyList<PortalSnapshot> portals) { try { ApplyInternal(portals); } catch (Exception arg) { PortalTrackingPlugin.Log.LogError((object)$"PortalPins.Apply failed: {arg}"); } } private static void ApplyInternal(IReadOnlyList<PortalSnapshot> portals) { //IL_0030: 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_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_005c: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) if (!PortalTrackingPlugin.ShowPins.Value) { Clear(); return; } List<DisplayPin> list = PortalHubs.Build(portals); HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); foreach (DisplayPin item in list) { hashSet.Add(item.Key); if (!Markers.TryGetValue(item.Key, out MarkerState value)) { value = new MarkerState(); Markers[item.Key] = value; } value.Pos = item.Position; value.Name = item.Name ?? string.Empty; value.PrefabHash = item.PrefabHash; } List<ZDOID> list2 = new List<ZDOID>(); foreach (ZDOID key in Markers.Keys) { if (!hashSet.Contains(key)) { list2.Add(key); } } foreach (ZDOID item2 in list2) { RemoveTracked(item2); } if (Markers.Count == _lastLoggedCount) { return; } _lastLoggedCount = Markers.Count; PortalTrackingPlugin.Log.LogInfo((object)$"Map markers tracked: {Markers.Count} (from {portals.Count} portal(s))"); foreach (KeyValuePair<ZDOID, MarkerState> marker in Markers) { string arg = (string.IsNullOrWhiteSpace(marker.Value.Name) ? PortalCatalog.TypeLabel(marker.Value.PrefabHash) : marker.Value.Name); PortalTrackingPlugin.Log.LogInfo((object)$" {arg} @ {marker.Value.Pos}"); } } internal static void UpdateDrawnMarkers() { //IL_019e: Unknown result type (might be due to invalid IL or missing references) try { if (!PortalTrackingPlugin.ShowPins.Value || !Object.op_Implicit((Object)(object)Minimap.instance) || Markers.Count == 0) { if (Markers.Count == 0) { return; } { foreach (MarkerState value in Markers.Values) { DestroyMarker(value); } return; } } Minimap instance = Minimap.instance; GameObject field = GetField<GameObject>(instance, "m_largeRoot"); RawImage field2 = GetField<RawImage>(instance, "m_mapImageLarge"); RawImage field3 = GetField<RawImage>(instance, "m_mapImageSmall"); RectTransform field4 = GetField<RectTransform>(instance, "m_pinRootLarge"); RectTransform field5 = GetField<RectTransform>(instance, "m_pinRootSmall"); GameObject field6 = GetField<GameObject>(instance, "m_pinPrefab"); if (!Object.op_Implicit((Object)(object)field) || !Object.op_Implicit((Object)(object)field2) || !Object.op_Implicit((Object)(object)field3) || !Object.op_Implicit((Object)(object)field4) || !Object.op_Implicit((Object)(object)field5) || !Object.op_Implicit((Object)(object)field6)) { return; } bool activeSelf = field.activeSelf; RawImage val = (activeSelf ? field2 : field3); RectTransform val2 = (activeSelf ? field4 : field5); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)((Graphic)val).rectTransform)) { return; } float num = (activeSelf ? GetField<float>(instance, "m_pinSizeLarge") : GetField<float>(instance, "m_pinSizeSmall")); if (num <= 0f) { num = (activeSelf ? 32f : 16f); } foreach (MarkerState item in new List<MarkerState>(Markers.Values)) { if (item != null) { if (!IsPointVisible(item.Pos, val, instance)) { DestroyMarker(item); } else { DrawMarker(item, num, val2, val, field6, activeSelf, instance); } } } } catch (Exception arg) { if (!_loggedDrawError) { _loggedDrawError = true; PortalTrackingPlugin.Log.LogWarning((object)$"UpdateDrawnMarkers: {arg}"); } } } private static void DrawMarker(MarkerState state, float size, RectTransform parent, RawImage rawImage, GameObject pinPrefab, bool large, Minimap mm) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) GameObject val = state.Marker; if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val.transform.parent != (Object)(object)parent) { if (Object.op_Implicit((Object)(object)val)) { Object.Destroy((Object)(object)val); } val = (state.Marker = Object.Instantiate<GameObject>(pinPrefab)); val.transform.SetParent((Transform)(object)parent, false); Image component = val.GetComponent<Image>(); if (Object.op_Implicit((Object)(object)component)) { component.sprite = ResolveSprite(state.PrefabHash) ?? component.sprite; } Transform transform = val.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (Object.op_Implicit((Object)(object)val2)) { val2.SetSizeWithCurrentAnchors((Axis)0, size * 1.25f); val2.SetSizeWithCurrentAnchors((Axis)1, size * 1.25f); } } WorldToMapPoint(state.Pos, mm, out var mx, out var my); Vector2 anchoredPosition = MapPointToLocalGuiPos(mx, my, rawImage); Transform transform2 = val.transform; RectTransform val3 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null); if (Object.op_Implicit((Object)(object)val3)) { val3.anchoredPosition = anchoredPosition; } Transform val4 = val.transform.Find("Checked"); if (Object.op_Implicit((Object)(object)val4)) { ((Component)val4).gameObject.SetActive(false); } SetMarkerName(val, state.Name, large, size); } private static void SetMarkerName(GameObject marker, string name, bool large, float pinSize) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) Transform val = marker.transform.Find("Name"); if (Object.op_Implicit((Object)(object)val)) { ((Component)val).gameObject.SetActive(false); } Transform val2 = marker.transform.Find("PortalTrackingName"); Text val4; if (!Object.op_Implicit((Object)(object)val2)) { GameObject val3 = new GameObject("PortalTrackingName"); val2 = val3.transform; val2.SetParent(marker.transform, false); LayoutLabelRect(val3.AddComponent<RectTransform>(), pinSize); val4 = val3.AddComponent<Text>(); val4.font = GetMapFont(); val4.fontSize = Mathf.Clamp(Mathf.RoundToInt(pinSize * 0.35f), 10, 14); val4.fontStyle = (FontStyle)1; val4.alignment = (TextAnchor)1; ((Graphic)val4).color = new Color(0.75f, 0.9f, 1f, 1f); val4.horizontalOverflow = (HorizontalWrapMode)1; val4.verticalOverflow = (VerticalWrapMode)1; ((Graphic)val4).raycastTarget = false; val4.supportRichText = false; Outline obj = val3.AddComponent<Outline>(); ((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.9f); ((Shadow)obj).effectDistance = new Vector2(1.2f, -1.2f); } else { val4 = ((Component)val2).GetComponent<Text>(); RectTransform val5 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null); if (val5 != null) { LayoutLabelRect(val5, pinSize); } } ((Component)val2).gameObject.SetActive(large && !string.IsNullOrWhiteSpace(name)); if (large && !((Object)(object)val4 == (Object)null)) { val4.text = ((Localization.instance != null) ? Localization.instance.Localize(name) : name); } } private static void LayoutLabelRect(RectTransform rt, float pinSize) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0062: 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) rt.sizeDelta = new Vector2(220f, 48f); rt.pivot = new Vector2(0.5f, 1f); rt.anchorMin = new Vector2(0.5f, 0.5f); rt.anchorMax = new Vector2(0.5f, 0.5f); rt.anchoredPosition = new Vector2(0f, (0f - pinSize) * 0.4f); ((Transform)rt).localScale = Vector3.one; } private static Font GetMapFont() { if (Object.op_Implicit((Object)(object)_mapFont)) { return _mapFont; } try { if (Object.op_Implicit((Object)(object)Minimap.instance)) { Text[] componentsInChildren = ((Component)Minimap.instance).GetComponentsInChildren<Text>(true); foreach (Text val in componentsInChildren) { if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.font)) { _mapFont = val.font; return _mapFont; } } } } catch { } _mapFont = Resources.GetBuiltinResource<Font>("Arial.ttf"); if (!Object.op_Implicit((Object)(object)_mapFont)) { _mapFont = Font.CreateDynamicFontFromOSFont("Arial", 16); } return _mapFont; } private static Sprite? ResolveSprite(int prefabHash) { if (SpriteCache.TryGetValue(prefabHash, out Sprite value)) { return value ?? GetFallbackSprite(); } Sprite val = null; try { if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); if (Object.op_Implicit((Object)(object)prefab)) { Piece component = prefab.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component)) { object? obj = AccessTools.Field(typeof(Piece), "m_icon")?.GetValue(component); val = (Sprite)((obj is Sprite) ? obj : null); } } } } catch { } SpriteCache[prefabHash] = val; return val ?? GetFallbackSprite(); } private static Sprite? GetFallbackSprite() { if (Object.op_Implicit((Object)(object)_fallbackSprite)) { return _fallbackSprite; } try { Minimap instance = Minimap.instance; if (!Object.op_Implicit((Object)(object)instance)) { return null; } if (GetField<object>(instance, "m_icons") is IList list) { foreach (object item in list) { if (item == null) { continue; } string text = item.GetType().GetField("m_name")?.GetValue(item)?.ToString() ?? item.GetType().GetField("m_type")?.GetValue(item)?.ToString(); object? obj = item.GetType().GetField("m_icon")?.GetValue(item); Sprite val = (Sprite)(((obj is Sprite) ? obj : null) ?? ((object)/*isinst with value type is only supported in some contexts*/)); if (!((Object)(object)val == (Object)null)) { if (text != null && (text.Contains("Icon") || text.Contains("Death"))) { _fallbackSprite = val; return _fallbackSprite; } if (_fallbackSprite == null) { _fallbackSprite = val; } } } } } catch { } return _fallbackSprite; } private static bool IsPointVisible(Vector3 p, RawImage map, Minimap mm) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) WorldToMapPoint(p, mm, out var mx, out var my); Rect uvRect = map.uvRect; if (mx > ((Rect)(ref uvRect)).xMin && mx < ((Rect)(ref uvRect)).xMax && my > ((Rect)(ref uvRect)).yMin) { return my < ((Rect)(ref uvRect)).yMax; } return false; } private static void WorldToMapPoint(Vector3 p, Minimap mm, out float mx, out float my) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) int field = GetField<int>(mm, "m_textureSize"); float field2 = GetField<float>(mm, "m_pixelSize"); if (field <= 0 || field2 <= 0f) { mx = 0f; my = 0f; return; } int num = field / 2; mx = p.x / field2 + (float)num; my = p.z / field2 + (float)num; mx /= field; my /= field; } private static Vector2 MapPointToLocalGuiPos(float mx, float my, RawImage img) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_009d: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)img) || !Object.op_Implicit((Object)(object)((Graphic)img).rectTransform)) { return Vector2.zero; } Rect uvRect = img.uvRect; if (((Rect)(ref uvRect)).width <= 0f || ((Rect)(ref uvRect)).height <= 0f) { return Vector2.zero; } Vector2 result = default(Vector2); ((Vector2)(ref result))..ctor((mx - ((Rect)(ref uvRect)).xMin) / ((Rect)(ref uvRect)).width, (my - ((Rect)(ref uvRect)).yMin) / ((Rect)(ref uvRect)).height); Rect rect = ((Graphic)img).rectTransform.rect; result.x *= ((Rect)(ref rect)).width; result.y *= ((Rect)(ref rect)).height; return result; } private static T? GetField<T>(object obj, string name) { FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), name); if (fieldInfo == null) { return default(T); } object value = fieldInfo.GetValue(obj); if (value is T) { return (T)value; } if (typeof(T).IsValueType && value != null) { return (T)value; } return default(T); } private static void RemoveTracked(ZDOID id) { //IL_0005: 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) if (Markers.TryGetValue(id, out MarkerState value)) { DestroyMarker(value); Markers.Remove(id); } } private static void DestroyMarker(MarkerState state) { if (Object.op_Implicit((Object)(object)state.Marker)) { Object.Destroy((Object)(object)state.Marker); state.Marker = null; } } private static bool IsSanePosition(Vector3 pos) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (float.IsNaN(pos.x) || float.IsNaN(pos.y) || float.IsNaN(pos.z)) { return false; } if (float.IsInfinity(pos.x) || float.IsInfinity(pos.y) || float.IsInfinity(pos.z)) { return false; } if (((Vector3)(ref pos)).sqrMagnitude < 0.01f) { return false; } return true; } } internal sealed class PortalSnapshot { public ZDOID Id; public Vector3 Position; public string Name = string.Empty; public int PrefabHash; } internal static class PortalSync { private static float _timer; private static readonly List<PortalSnapshot> Latest = new List<PortalSnapshot>(); private static readonly object Gate = new object(); private static int _lastLoggedCount = -1; private static bool _loggedProtocolMismatch; internal static IReadOnlyList<PortalSnapshot> GetLatest() { lock (Gate) { return Latest.ToArray(); } } internal static void Tick(float dt) { if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || !ZNet.instance.IsServer()) { return; } float num = Mathf.Clamp(PortalTrackingPlugin.SyncInterval.Value, 1f, 30f); _timer += dt; if (_timer < num) { return; } _timer = 0f; try { BroadcastNow(); } catch (Exception arg) { PortalTrackingPlugin.Log.LogError((object)$"Portal sync failed: {arg}"); } } internal static void BroadcastNow() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZDOMan.instance != null) { List<PortalSnapshot> list = CollectPortals(); ZPackage val = WritePackage(list); lock (Gate) { Latest.Clear(); Latest.AddRange(list); } if (list.Count != _lastLoggedCount) { _lastLoggedCount = list.Count; PortalTrackingPlugin.Log.LogInfo((object)$"Broadcasting {list.Count} portal pin(s) to clients."); } if (!ZNet.instance.IsDedicated() && Object.op_Implicit((Object)(object)Minimap.instance)) { PortalPins.Apply(list); } ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PortalTracking.Sync", new object[1] { val }); } } internal static void RequestFromServer() { if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null) { return; } if (ZNet.instance.IsServer()) { BroadcastNow(); return; } long serverPeerIdSafe = GetServerPeerIdSafe(); if (serverPeerIdSafe != 0L) { ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerIdSafe, "PortalTracking.RequestSync", Array.Empty<object>()); } } internal static void HandleRequestSync(long sender) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { ZPackage val = WritePackage(CollectPortals()); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PortalTracking.Sync", new object[1] { val }); } } internal static void HandleSync(long sender, ZPackage pkg) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsDedicated() || pkg == null || pkg.Size() == 0) { return; } try { pkg.SetPos(0); if (TryReadPackage(pkg, out List<PortalSnapshot> portals)) { lock (Gate) { Latest.Clear(); Latest.AddRange(portals); } if (portals.Count != _lastLoggedCount) { _lastLoggedCount = portals.Count; PortalTrackingPlugin.Log.LogInfo((object)$"Received {portals.Count} portal pin(s) from server."); } PortalPins.Apply(portals); } } catch (Exception arg) { PortalTrackingPlugin.Log.LogError((object)$"Portal sync receive failed: {arg}"); } } private static long GetServerPeerIdSafe() { try { MethodInfo methodInfo = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null); if (methodInfo != null && ZRoutedRpc.instance != null && methodInfo.Invoke(ZRoutedRpc.instance, null) is long result) { return result; } } catch (Exception ex) { PortalTrackingPlugin.Log.LogWarning((object)("GetServerPeerID reflection failed: " + ex.Message)); } try { ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null); if (val != null) { FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "m_uid"); if (fieldInfo != null && fieldInfo.GetValue(val) is long result2) { return result2; } } } catch (Exception ex2) { PortalTrackingPlugin.Log.LogWarning((object)("GetServerPeer fallback failed: " + ex2.Message)); } return 0L; } private static List<PortalSnapshot> CollectPortals() { List<PortalSnapshot> result = new List<PortalSnapshot>(); ZDOMan instance = ZDOMan.instance; if (instance == null) { return result; } HashSet<ZDOID> seen = new HashSet<ZDOID>(); CollectFromPortalList(instance, Consider); if (result.Count == 0) { CollectFromObjectsById(instance, Consider); } if (result.Count == 0) { CollectFromPrefabIterative(instance, Consider); } return result; void Consider(ZDO? zdo) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_006b: 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) if (zdo != null && zdo.IsValid() && seen.Add(zdo.m_uid)) { int prefab = zdo.GetPrefab(); if (PortalCatalog.IsPortalPrefab(prefab)) { string text = ReadPortalTag(zdo); if (PortalTrackingPlugin.ShowUntagged.Value || !string.IsNullOrWhiteSpace(text)) { Vector3 position = zdo.GetPosition(); result.Add(new PortalSnapshot { Id = zdo.m_uid, Position = position, Name = PortalCatalog.DisplayName(text), PrefabHash = prefab }); } } } } } private static string ReadPortalTag(ZDO zdo) { try { return zdo.GetString(ZDOVars.s_tag, string.Empty) ?? string.Empty; } catch { return zdo.GetString("tag", string.Empty) ?? string.Empty; } } private static void CollectFromPortalList(ZDOMan zdoMan, Action<ZDO?> consider) { try { List<ZDO> portalList = zdoMan.GetPortalList(); if (portalList == null) { return; } foreach (ZDO item in portalList) { consider(item); } } catch (Exception ex) { PortalTrackingPlugin.Log.LogWarning((object)("GetPortalList failed: " + ex.Message)); } } private static void CollectFromObjectsById(ZDOMan zdoMan, Action<ZDO?> consider) { try { object obj = AccessTools.Field(typeof(ZDOMan), "m_objectsByID")?.GetValue(zdoMan); if (obj is IDictionary dictionary) { { foreach (object value2 in dictionary.Values) { ZDO val = (ZDO)((value2 is ZDO) ? value2 : null); if (val != null) { consider(val); } } return; } } if (!(obj is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { ZDO val2 = (ZDO)((item is ZDO) ? item : null); if (val2 != null) { consider(val2); } else if (item is DictionaryEntry dictionaryEntry) { object? value = dictionaryEntry.Value; ZDO val3 = (ZDO)((value is ZDO) ? value : null); if (val3 != null) { consider(val3); } } } } catch (Exception ex) { PortalTrackingPlugin.Log.LogWarning((object)("ZDO id scan failed: " + ex.Message)); } } private static void CollectFromPrefabIterative(ZDOMan zdoMan, Action<ZDO?> consider) { List<ZDO> list = new List<ZDO>(); PortalCatalog.PrefabInfo[] prefabs = PortalCatalog.Prefabs; for (int i = 0; i < prefabs.Length; i++) { PortalCatalog.PrefabInfo prefabInfo = prefabs[i]; list.Clear(); int num = 0; int num2 = 0; while (!zdoMan.GetAllZDOsWithPrefabIterative(prefabInfo.Prefab, list, ref num)) { if (++num2 > 100000) { PortalTrackingPlugin.Log.LogWarning((object)("Prefab scan aborted for " + prefabInfo.Prefab)); break; } } foreach (ZDO item in list) { consider(item); } } } private static ZPackage WritePackage(List<PortalSnapshot> portals) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_007b: 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) ZPackage val = new ZPackage(); val.Write(2); val.Write(PortalTrackingPlugin.ServerOverridesClient.Value); val.Write(PortalTrackingPlugin.ClusterHubs.Value); val.Write(Mathf.Clamp(PortalTrackingPlugin.HubRadius.Value, 0f, 500f)); val.Write((int)PortalTrackingPlugin.HubIcon.Value); val.Write(portals.Count); foreach (PortalSnapshot portal in portals) { val.Write(portal.Id); val.Write(portal.Position); val.Write(portal.Name ?? string.Empty); val.Write(portal.PrefabHash); } return val; } private static bool TryReadPackage(ZPackage pkg, out List<PortalSnapshot> portals) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: 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) portals = new List<PortalSnapshot>(); int num = pkg.ReadInt(); if (num != 2) { NotifyProtocolMismatch(num); return false; } bool overridesEnabled = pkg.ReadBool(); bool clusterHubs = pkg.ReadBool(); float hubRadius = pkg.ReadSingle(); HubIconType hubIcon = ((pkg.ReadInt() == 1) ? HubIconType.Stone : HubIconType.Wood); EffectiveConfig.ApplyFromServer(overridesEnabled, clusterHubs, hubRadius, hubIcon); int num2 = pkg.ReadInt(); if (num2 < 0 || num2 > 1024) { PortalTrackingPlugin.Log.LogWarning((object)$"Portal sync rejected: unreasonable portal count {num2}."); return false; } portals = new List<PortalSnapshot>(num2); for (int i = 0; i < num2; i++) { portals.Add(new PortalSnapshot { Id = pkg.ReadZDOID(), Position = pkg.ReadVector3(), Name = (pkg.ReadString() ?? string.Empty), PrefabHash = pkg.ReadInt() }); } return true; } private static void NotifyProtocolMismatch(int serverVersion) { if (_loggedProtocolMismatch) { return; } _loggedProtocolMismatch = true; int num = 2; string text = ((serverVersion >= num) ? $"PortalTracking: your mod is outdated (server protocol {serverVersion}, you have {num}). Update PortalTracking to match the server." : $"PortalTracking: server mod is outdated (server protocol {serverVersion}, you have {num}). Ask the host to update PortalTracking."); PortalTrackingPlugin.Log.LogWarning((object)text); try { if (Object.op_Implicit((Object)(object)MessageHud.instance)) { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true); } } catch (Exception ex) { PortalTrackingPlugin.Log.LogWarning((object)("Could not show protocol mismatch HUD: " + ex.Message)); } try { if (Object.op_Implicit((Object)(object)Chat.instance)) { ((Terminal)Chat.instance).AddString("PortalTracking", text, (Type)1, false); } } catch { } } } public enum HubIconType { Wood, Stone } [BepInPlugin("matthb1.portaltracking", "PortalTracking", "1.0.0")] public class PortalTrackingPlugin : BaseUnityPlugin { public const string GUID = "matthb1.portaltracking"; public const string NAME = "PortalTracking"; public const string VERSION = "1.0.0"; public const int ProtocolVersion = 2; public const string RpcSync = "PortalTracking.Sync"; public const string RpcRequestSync = "PortalTracking.RequestSync"; internal static PortalTrackingPlugin Instance; internal static ConfigEntry<bool> ShowPins; internal static ConfigEntry<bool> ShowUntagged; internal static ConfigEntry<float> SyncInterval; internal static ConfigEntry<bool> ClusterHubs; internal static ConfigEntry<float> HubRadius; internal static ConfigEntry<HubIconType> HubIcon; internal static ConfigEntry<bool> ServerOverridesClient; internal static ManualLogSource Log; private void Awake() { //IL_010e: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ShowPins = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowPins", true, "Show portal pins on the minimap and world map."); ShowUntagged = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowUntagged", true, "Show pins for portals that have no tag/name yet."); SyncInterval = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SyncInterval", 3f, "Seconds between server portal syncs (1-30)."); ClusterHubs = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ClusterHubs", true, "Merge nearby portals into a single Hub pin."); HubRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "HubRadius", 50f, "Horizontal meters - portals within this range share one Hub pin (0-500)."); HubIcon = ((BaseUnityPlugin)this).Config.Bind<HubIconType>("General", "HubIcon", HubIconType.Wood, "Map icon for Hub pins: Wood or Stone portal."); ServerOverridesClient = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ServerOverridesClient", true, "Server only: push ClusterHubs, HubRadius, and HubIcon to clients (overrides their local values)."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"PortalTracking 1.0.0 loaded - server syncs portal map pins (wood + stone)."); new Harmony("matthb1.portaltracking").PatchAll(); } private void Update() { PortalSync.Tick(Time.deltaTime); } } } namespace PortalTracking.Patches { [HarmonyPatch(typeof(Game), "Start")] internal static class GameStartPatch { private static void Prefix() { ZRoutedRpc.instance.Register("PortalTracking.RequestSync", (Action<long>)PortalSync.HandleRequestSync); ZRoutedRpc.instance.Register<ZPackage>("PortalTracking.Sync", (Action<long, ZPackage>)PortalSync.HandleSync); } } [HarmonyPatch(typeof(Game), "Logout")] internal static class GameLogoutPatch { private static void Prefix() { PortalPins.Clear(); EffectiveConfig.ClearServerOverrides(); } } [HarmonyPatch(typeof(Minimap), "OnDestroy")] internal static class MinimapDestroyPatch { private static void Postfix() { PortalPins.Clear(); } } [HarmonyPatch(typeof(Minimap), "Awake")] internal static class MinimapAwakePatch { private static void Postfix() { PortalSync.RequestFromServer(); } } [HarmonyPatch(typeof(Minimap), "UpdatePins")] internal static class MinimapUpdatePinsPatch { private static void Postfix() { PortalPins.UpdateDrawnMarkers(); } } [HarmonyPatch(typeof(Minimap), "UpdateMap")] internal static class MinimapUpdateRefreshPatch { private static float _next; private static void Postfix() { if (!(Time.time < _next)) { _next = Time.time + 5f; IReadOnlyList<PortalSnapshot> latest = PortalSync.GetLatest(); if (latest.Count > 0) { PortalPins.Apply(latest); } } } } [HarmonyPatch(typeof(TeleportWorld), "RPC_SetTag")] internal static class PortalTagSyncPatch { private static void Postfix() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { PortalSync.BroadcastNow(); } catch (Exception ex) { PortalTrackingPlugin.Log.LogWarning((object)("Tag sync broadcast failed: " + ex.Message)); } } } }