Decompiled source of BotanicOwnerDebug v1.0.7

BotanicOwnerDebug.dll

Decompiled 15 hours ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("1.0.7.0")]
namespace BotanicOwnerDebug;

internal static class BuildingVisuals
{
	internal static bool Eligible(ZNetView view)
	{
		if (!Object.op_Implicit((Object)(object)view) || !view.m_persistent || view.m_distant || Object.op_Implicit((Object)(object)((Component)view).GetComponentInParent<Player>()) || Object.op_Implicit((Object)(object)((Component)view).GetComponentInParent<ItemDrop>()))
		{
			return false;
		}
		if (Object.op_Implicit((Object)(object)((Component)view).GetComponent<Character>()))
		{
			return true;
		}
		if (!Object.op_Implicit((Object)(object)((Component)view).GetComponentInParent<Character>()))
		{
			if (!Object.op_Implicit((Object)(object)((Component)view).GetComponent<Piece>()) && !Object.op_Implicit((Object)(object)((Component)view).GetComponent<WearNTear>()))
			{
				return Interactive(view);
			}
			return true;
		}
		return false;
	}

	internal static bool Interactive(ZNetView view)
	{
		MonoBehaviour[] componentsInChildren = ((Component)view).GetComponentsInChildren<MonoBehaviour>(true);
		foreach (MonoBehaviour val in componentsInChildren)
		{
			if (Object.op_Implicit((Object)(object)val) && val is Interactable)
			{
				Container val2 = (Container)(object)((val is Container) ? val : null);
				ZNetView val3 = ((Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.m_rootObjectOverride)) ? val2.m_rootObjectOverride : ((Component)val).GetComponentInParent<ZNetView>());
				if ((Object)(object)val3 == (Object)(object)view)
				{
					return true;
				}
			}
		}
		return false;
	}

	internal static bool Solid(Material source)
	{
		if (!Object.op_Implicit((Object)(object)source) || !Object.op_Implicit((Object)(object)source.shader) || source.renderQueue >= 2450)
		{
			return false;
		}
		string tag = source.GetTag("RenderType", false, "");
		if (!string.IsNullOrEmpty(tag) && !string.Equals(tag, "Opaque", StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		if (source.HasProperty("_Mode") && source.GetFloat("_Mode") != 0f)
		{
			return false;
		}
		if (source.HasProperty("_SrcBlend") && source.GetInt("_SrcBlend") != 1)
		{
			return false;
		}
		if (source.HasProperty("_DstBlend") && source.GetInt("_DstBlend") != 0)
		{
			return false;
		}
		string name = ((Object)source.shader).name;
		return !EffectShader(name);
	}

	private static bool EffectShader(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return true;
		}
		if (!name.StartsWith("Hidden/", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("UI/", StringComparison.OrdinalIgnoreCase) && !name.StartsWith("Skybox/", StringComparison.OrdinalIgnoreCase) && !Contains(name, "particle") && !Contains(name, "effect") && !Contains(name, "vfx") && !Contains(name, "billboard") && !Contains(name, "distort") && !Contains(name, "glow") && !Contains(name, "halo") && !Contains(name, "flare") && !Contains(name, "TextMeshPro") && !Contains(name, "smoke") && !Contains(name, "fog") && !Contains(name, "cloud") && !Contains(name, "beam") && !Contains(name, "flame") && !Contains(name, "depthonly"))
		{
			return Contains(name, "invisible");
		}
		return true;
	}

	private static bool Contains(string value, string token)
	{
		return value.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0;
	}

	internal static bool Tintable(Material source)
	{
		//IL_003b: 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)
		if (!Solid(source))
		{
			return false;
		}
		if (source.HasProperty("_ZWrite") && source.GetInt("_ZWrite") == 0)
		{
			return false;
		}
		if (!source.HasProperty("_Color"))
		{
			return true;
		}
		Color color = source.GetColor("_Color");
		if (color.a > 0f && !float.IsInfinity(color.a) && !float.IsNaN(color.r) && !float.IsNaN(color.g) && !float.IsNaN(color.b) && !float.IsInfinity(color.r) && !float.IsInfinity(color.g))
		{
			return !float.IsInfinity(color.b);
		}
		return false;
	}
}
internal sealed class DebugView : IDisposable
{
	private struct Surface
	{
		internal int Index;

		internal int Cull;
	}

	private sealed class Part
	{
		internal Renderer Renderer;

		internal Entry Entry;

		internal Surface[] Surfaces;
	}

	private sealed class Entry
	{
		internal ZDO Data;

		internal ZNetView View;

		internal OwnerKey Owner;

		internal bool Mob;

		internal bool Interactive;

		internal bool Live;

		internal int CheckedFrame;

		internal readonly HashSet<Renderer> Parts = new HashSet<Renderer>();
	}

	private readonly Harmony hooks = new Harmony("botanic.owner.debug");

	private readonly Dictionary<ZDOID, Entry> entries = new Dictionary<ZDOID, Entry>();

	private readonly Dictionary<ZNetView, ZDOID> byView = new Dictionary<ZNetView, ZDOID>();

	private readonly Dictionary<Renderer, Part> parts = new Dictionary<Renderer, Part>();

	private readonly Dictionary<Renderer, Part> interactiveParts = new Dictionary<Renderer, Part>();

	private readonly Dictionary<Renderer, Part> mobParts = new Dictionary<Renderer, Part>();

	private bool interactiveOnly;

	private bool mobsOnly;

	private readonly HashSet<ZDOID> changed = new HashSet<ZDOID>();

	private readonly HashSet<ZDOID> models = new HashSet<ZDOID>();

	private readonly List<ZDOID> retiredEntries = new List<ZDOID>();

	private readonly List<Renderer> retiredParts = new List<Renderer>();

	private readonly HashSet<Renderer> seen = new HashSet<Renderer>();

	private readonly List<Material> sourceMaterials = new List<Material>();

	private readonly List<Surface> surfaces = new List<Surface>();

	private readonly Dictionary<long, string> names = new Dictionary<long, string>();

	private readonly NativeColours colours = new NativeColours();

	internal readonly List<KeyValuePair<OwnerKey, string>> Legend = new List<KeyValuePair<OwnerKey, string>>();

	internal readonly string Note = "Spieler, Effekte und Cluster ausgenommen";

	private readonly Func<ZNetScene, Dictionary<ZDO, ZNetView>> instances;

	private readonly Plane[] planes = (Plane[])(object)new Plane[6];

	private ZDOMan world;

	private ZNetScene scene;

	private ZNet network;

	private Camera camera;

	private CommandBuffer commands;

	private float nextRefresh;

	private float maxDistance;

	private long server;

	private int frame;

	internal string ModeLabel
	{
		get
		{
			if (!mobsOnly)
			{
				if (!interactiveOnly)
				{
					return "Alle Bauteile + Mobs";
				}
				return "Interaktive Objekte + alle Mobs";
			}
			return "Nur Mobs";
		}
	}

	internal int PaintedCount { get; private set; }

	internal int TrackedCount
	{
		get
		{
			if (!mobsOnly)
			{
				if (!interactiveOnly)
				{
					return parts.Count;
				}
				return interactiveParts.Count;
			}
			return mobParts.Count;
		}
	}

	internal DebugView()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Expected O, but got Unknown
		FieldInfo field = typeof(ZNetScene).GetField("m_instances", BindingFlags.Instance | BindingFlags.NonPublic);
		if (field == null || field.FieldType != typeof(Dictionary<ZDO, ZNetView>))
		{
			throw new NotSupportedException("Native instance registry changed.");
		}
		DynamicMethod dynamicMethod = new DynamicMethod("BotanicDebugInstances", field.FieldType, new Type[1] { typeof(ZNetScene) }, typeof(DebugView).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Ldfld, field);
		iLGenerator.Emit(OpCodes.Ret);
		instances = (Func<ZNetScene, Dictionary<ZDO, ZNetView>>)dynamicMethod.CreateDelegate(typeof(Func<ZNetScene, Dictionary<ZDO, ZNetView>>));
		try
		{
			hooks.CreateClassProcessor(typeof(DebugHooks)).Patch();
		}
		catch
		{
			hooks.UnpatchSelf();
			throw;
		}
	}

	private bool Context()
	{
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: 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)
		if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsDedicated() || !Object.op_Implicit((Object)(object)ZNetScene.instance) || ZDOMan.instance == null)
		{
			return false;
		}
		if (!object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(scene, ZNetScene.instance) || !object.ReferenceEquals(network, ZNet.instance))
		{
			Clear();
			world = ZDOMan.instance;
			scene = ZNetScene.instance;
			network = ZNet.instance;
			foreach (KeyValuePair<ZDO, ZNetView> item in instances(scene))
			{
				Add(item.Key, item.Value);
			}
		}
		ZNetPeer serverPeer = network.GetServerPeer();
		long num = (network.IsServer() ? ZDOMan.GetSessionID() : ((serverPeer != null && serverPeer.IsReady()) ? serverPeer.m_uid : 0));
		if (server != num)
		{
			server = num;
			foreach (ZDOID key in entries.Keys)
			{
				changed.Add(key);
			}
			nextRefresh = 0f;
		}
		return true;
	}

	internal void Add(ZDO data, ZNetView view)
	{
		//IL_005c: 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_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: 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)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		if (data == null || !BuildingVisuals.Eligible(view) || !view.IsValid() || !object.ReferenceEquals(view.GetZDO(), data) || ((ZDOID)(ref data.m_uid)).IsNone())
		{
			return;
		}
		if (byView.TryGetValue(view, out var value) && !((ZDOID)(ref value)).Equals(data.m_uid))
		{
			Remove(view);
		}
		if (entries.TryGetValue(data.m_uid, out var value2))
		{
			if (object.ReferenceEquals(value2.Data, data) && object.ReferenceEquals(value2.View, view))
			{
				return;
			}
			Remove(value2.View);
		}
		entries.Add(data.m_uid, new Entry
		{
			Data = data,
			View = view,
			Mob = Object.op_Implicit((Object)(object)((Component)view).GetComponent<Character>())
		});
		byView[view] = data.m_uid;
		changed.Add(data.m_uid);
		models.Add(data.m_uid);
	}

	internal void Remove(ZNetView view)
	{
		//IL_0024: 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_003e: 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)
		if (!byView.TryGetValue(view, out var value))
		{
			return;
		}
		byView.Remove(view);
		changed.Remove(value);
		models.Remove(value);
		if (!entries.TryGetValue(value, out var value2) || !object.ReferenceEquals(value2.View, view))
		{
			return;
		}
		foreach (Renderer part in value2.Parts)
		{
			parts.Remove(part);
			interactiveParts.Remove(part);
			mobParts.Remove(part);
		}
		entries.Remove(value);
	}

	internal void Changed(ZDO data)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		if (data != null && world != null && object.ReferenceEquals(world, ZDOMan.instance) && entries.ContainsKey(data.m_uid))
		{
			changed.Add(data.m_uid);
		}
	}

	internal void ModelsChanged(Component source)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)source))
		{
			ZNetView componentInParent = source.GetComponentInParent<ZNetView>();
			if (Object.op_Implicit((Object)(object)componentInParent) && byView.TryGetValue(componentInParent, out var value))
			{
				models.Add(value);
			}
		}
	}

	private bool Valid(ZDOID id, Entry entry)
	{
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)entry.View) && entry.View.IsValid() && object.ReferenceEquals(entry.View.GetZDO(), entry.Data) && entry.Data.IsValid())
		{
			return object.ReferenceEquals(world.GetZDO(id), entry.Data);
		}
		return false;
	}

	private void Scan(Entry entry)
	{
		entry.Interactive = !entry.Mob && BuildingVisuals.Interactive(entry.View);
		Character component = ((Component)entry.View).GetComponent<Character>();
		seen.Clear();
		Renderer[] componentsInChildren = ((Component)entry.View).GetComponentsInChildren<Renderer>(true);
		foreach (Renderer val in componentsInChildren)
		{
			if (!Object.op_Implicit((Object)(object)val) || (!(val is MeshRenderer) && !(val is SkinnedMeshRenderer)) || (Object)(object)((Component)val).GetComponentInParent<ZNetView>() != (Object)(object)entry.View || Object.op_Implicit((Object)(object)((Component)val).GetComponentInParent<ItemDrop>()))
			{
				continue;
			}
			Character componentInParent = ((Component)val).GetComponentInParent<Character>();
			if (componentInParent is Player || (Object.op_Implicit((Object)(object)componentInParent) && !object.ReferenceEquals(componentInParent, component)))
			{
				continue;
			}
			seen.Add(val);
			entry.Parts.Add(val);
			sourceMaterials.Clear();
			val.GetSharedMaterials(sourceMaterials);
			surfaces.Clear();
			for (int j = 0; j < sourceMaterials.Count; j++)
			{
				Material val2 = sourceMaterials[j];
				if (BuildingVisuals.Tintable(val2))
				{
					int num = (val2.HasProperty("_Cull") ? val2.GetInt("_Cull") : 2);
					if (num < 0 || num > 2)
					{
						num = 2;
					}
					surfaces.Add(new Surface
					{
						Index = j,
						Cull = num
					});
				}
			}
			sourceMaterials.Clear();
			if (!parts.TryGetValue(val, out var value))
			{
				Part part = new Part();
				part.Renderer = val;
				part.Entry = entry;
				value = part;
				parts.Add(val, value);
			}
			bool flag = value.Surfaces != null && value.Surfaces.Length == surfaces.Count;
			int num2 = 0;
			while (flag && num2 < surfaces.Count)
			{
				flag = value.Surfaces[num2].Index == surfaces[num2].Index && value.Surfaces[num2].Cull == surfaces[num2].Cull;
				num2++;
			}
			if (!flag)
			{
				value.Surfaces = surfaces.ToArray();
			}
			if (entry.Mob || entry.Interactive)
			{
				interactiveParts[val] = value;
			}
			else
			{
				interactiveParts.Remove(val);
			}
			if (entry.Mob)
			{
				mobParts[val] = value;
			}
			else
			{
				mobParts.Remove(val);
			}
		}
		retiredParts.Clear();
		foreach (Renderer part2 in entry.Parts)
		{
			if (!seen.Contains(part2))
			{
				retiredParts.Add(part2);
			}
		}
		foreach (Renderer retiredPart in retiredParts)
		{
			parts.Remove(retiredPart);
			interactiveParts.Remove(retiredPart);
			mobParts.Remove(retiredPart);
			entry.Parts.Remove(retiredPart);
		}
		seen.Clear();
		retiredParts.Clear();
		surfaces.Clear();
	}

	private OwnerKey Resolve(ZDO data)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		return OwnerKey.Resolve(data != null && data.IsValid() && object.ReferenceEquals(world.GetZDO(data.m_uid), data), (data == null) ? 0 : data.GetOwner(), server);
	}

	private void Refresh()
	{
		//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_005a: 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_006c: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: 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_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: 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)
		names.Clear();
		Legend.Clear();
		HashSet<OwnerKey> hashSet = new HashSet<OwnerKey>();
		foreach (PlayerInfo player in network.GetPlayerList())
		{
			string text = (string.IsNullOrEmpty(player.m_name) ? "Spieler" : player.m_name);
			ZDOID characterID = player.m_characterID;
			if (!((ZDOID)(ref characterID)).IsNone())
			{
				ZDOID characterID2 = player.m_characterID;
				if (((ZDOID)(ref characterID2)).UserID != 0)
				{
					ZDOID characterID3 = player.m_characterID;
					long userID = ((ZDOID)(ref characterID3)).UserID;
					names[userID] = text;
					hashSet.Add(OwnerKey.Resolve(known: true, userID, server));
					continue;
				}
			}
			Legend.Add(new KeyValuePair<OwnerKey, string>(OwnerKey.Resolve(known: false, 0L, server), text + " (Besitz-ID lädt)"));
		}
		hashSet.Add(OwnerKey.Resolve(known: true, server, server));
		hashSet.Add(OwnerKey.Resolve(known: true, 0L, server));
		hashSet.Add(OwnerKey.Resolve(known: false, 0L, server));
		retiredEntries.Clear();
		foreach (KeyValuePair<ZDOID, Entry> entry in entries)
		{
			Entry value = entry.Value;
			if (!Valid(entry.Key, value))
			{
				retiredEntries.Add(entry.Key);
				continue;
			}
			bool num;
			if (!mobsOnly)
			{
				if (!interactiveOnly || value.Mob)
				{
					goto IL_01bc;
				}
				num = value.Interactive;
			}
			else
			{
				num = value.Mob;
			}
			if (num)
			{
				goto IL_01bc;
			}
			goto IL_01ca;
			IL_01bc:
			hashSet.Add(value.Owner);
			goto IL_01ca;
			IL_01ca:
			if (value.Mob)
			{
				Scan(value);
			}
		}
		foreach (ZDOID retiredEntry in retiredEntries)
		{
			Remove(entries[retiredEntry].View);
		}
		retiredEntries.Clear();
		foreach (OwnerKey item in hashSet)
		{
			string value3;
			string value2 = ((item.Kind != OwnerKind.Server) ? ((item.Kind != OwnerKind.Unowned) ? ((item.Kind != OwnerKind.Unknown) ? ((names.TryGetValue(item.Uid, out value3) ? value3 : "Besitzer") + " [" + item.Uid + "]") : "Besitz unbekannt / ZDO fehlt") : "Kein Besitzer") : "Server");
			Legend.Add(new KeyValuePair<OwnerKey, string>(item, value2));
		}
		Legend.Sort(delegate(KeyValuePair<OwnerKey, string> a, KeyValuePair<OwnerKey, string> b)
		{
			int num2 = a.Key.Kind.CompareTo(b.Key.Kind);
			if (num2 != 0)
			{
				return num2;
			}
			long uid = a.Key.Uid;
			int num3 = uid.CompareTo(b.Key.Uid);
			return (num3 == 0) ? StringComparer.Ordinal.Compare(a.Value, b.Value) : num3;
		});
		nextRefresh = Time.unscaledTime + 1f;
	}

	private void UpdateEntries()
	{
		//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_0028: 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_0042: 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_008b: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		retiredEntries.Clear();
		foreach (ZDOID item in changed)
		{
			if (entries.TryGetValue(item, out var value))
			{
				if (!Valid(item, value))
				{
					retiredEntries.Add(item);
				}
				else
				{
					value.Owner = Resolve(value.Data);
				}
			}
		}
		foreach (ZDOID retiredEntry in retiredEntries)
		{
			Remove(entries[retiredEntry].View);
		}
		retiredEntries.Clear();
		changed.Clear();
		foreach (ZDOID model in models)
		{
			if (entries.TryGetValue(model, out var value2) && Valid(model, value2))
			{
				Scan(value2);
			}
		}
		models.Clear();
	}

	private void Attach(Camera next)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		if (!object.ReferenceEquals(camera, next) || commands == null)
		{
			Detach();
			camera = next;
			CommandBuffer val = new CommandBuffer();
			val.name = "BotanicOwnerDebugOnly";
			commands = val;
			camera.AddCommandBuffer((CameraEvent)18, commands);
		}
	}

	private void Detach()
	{
		CommandBuffer val = commands;
		commands = null;
		try
		{
			if (Object.op_Implicit((Object)(object)camera) && val != null)
			{
				camera.RemoveCommandBuffer((CameraEvent)18, val);
			}
		}
		finally
		{
			camera = null;
			if (val != null)
			{
				val.Release();
			}
		}
	}

	internal void Draw(int seed, float strength, float distance, bool onlyInteractive = false, bool onlyMobs = false)
	{
		//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_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Invalid comparison between Unknown and I4
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ba: 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)
		PaintedCount = 0;
		if (!Context())
		{
			Clear();
			return;
		}
		Camera main = Camera.main;
		if (!Object.op_Implicit((Object)(object)main))
		{
			Detach();
			return;
		}
		if (interactiveOnly != onlyInteractive || mobsOnly != onlyMobs)
		{
			interactiveOnly = onlyInteractive;
			mobsOnly = onlyMobs;
			nextRefresh = 0f;
		}
		Attach(main);
		commands.Clear();
		maxDistance = distance * distance;
		colours.Configure(seed, strength);
		frame++;
		UpdateEntries();
		if (Time.unscaledTime >= nextRefresh)
		{
			Refresh();
		}
		GeometryUtility.CalculateFrustumPlanes(camera, planes);
		Vector3 position = ((Component)camera).transform.position;
		foreach (Part value in (mobsOnly ? mobParts : (interactiveOnly ? interactiveParts : parts)).Values)
		{
			Renderer renderer = value.Renderer;
			if (!Object.op_Implicit((Object)(object)renderer) || !renderer.enabled || value.Surfaces.Length == 0 || renderer.forceRenderingOff || !((Component)renderer).gameObject.activeInHierarchy || !renderer.isVisible || (int)renderer.shadowCastingMode == 3)
			{
				continue;
			}
			Bounds bounds = renderer.bounds;
			int layer = ((Component)renderer).gameObject.layer;
			if ((camera.cullingMask & (1 << layer)) == 0)
			{
				continue;
			}
			Vector3 val = ((Bounds)(ref bounds)).center - position;
			if (((Vector3)(ref val)).sqrMagnitude > maxDistance || !GeometryUtility.TestPlanesAABB(planes, bounds))
			{
				continue;
			}
			Entry entry = value.Entry;
			if (entry.CheckedFrame != frame)
			{
				entry.Live = Valid(entry.Data.m_uid, entry);
				entry.CheckedFrame = frame;
			}
			if (entry.Live)
			{
				Surface[] array = value.Surfaces;
				for (int i = 0; i < array.Length; i++)
				{
					Surface surface = array[i];
					commands.DrawRenderer(renderer, colours.Get(value.Entry.Owner, surface.Cull), surface.Index, 0);
				}
				PaintedCount++;
			}
		}
		colours.Sweep();
	}

	internal void Clear()
	{
		Detach();
		colours.Clear();
		entries.Clear();
		byView.Clear();
		parts.Clear();
		interactiveParts.Clear();
		mobParts.Clear();
		changed.Clear();
		models.Clear();
		retiredEntries.Clear();
		retiredParts.Clear();
		seen.Clear();
		sourceMaterials.Clear();
		surfaces.Clear();
		names.Clear();
		Legend.Clear();
		world = null;
		scene = null;
		network = null;
		server = 0L;
		nextRefresh = 0f;
		PaintedCount = 0;
	}

	public void Dispose()
	{
		try
		{
			hooks.UnpatchSelf();
		}
		finally
		{
			Clear();
		}
	}
}
[HarmonyPatch]
internal static class DebugHooks
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		yield return AccessTools.Method(typeof(ZNetScene), "AddInstance", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(ZNetView), "ResetZDO", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(ZNetView), "OnDestroy", (Type[])null, (Type[])null);
		yield return AccessTools.Method(typeof(ZDO), "SetOwnerInternal", (Type[])null, (Type[])null);
		yield return AccessTools.PropertySetter(typeof(ZDO), "OwnerRevision");
		yield return AccessTools.Method(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null);
		try
		{
			string[] array = new string[4] { "AttachItem", "AttachArmor", "AttachBackItem", "SetRightItemVisual" };
			foreach (string name in array)
			{
				MethodInfo method = AccessTools.Method(typeof(VisEquipment), name, (Type[])null, (Type[])null);
				if (method != null)
				{
					yield return method;
				}
			}
		}
		finally
		{
		}
	}

	[HarmonyPrefix]
	private static void Before(object __instance, MethodBase __originalMethod)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		DebugView debugView = (Object.op_Implicit((Object)(object)Plugin.Instance) ? Plugin.Instance.View : null);
		if (debugView == null)
		{
			return;
		}
		try
		{
			if (__instance is ZNetView)
			{
				debugView.Remove((ZNetView)__instance);
			}
			else if (__originalMethod.Name == "Shutdown")
			{
				debugView.Clear();
			}
		}
		catch
		{
		}
	}

	[HarmonyPostfix]
	private static void After(object __instance, object[] __args, MethodBase __originalMethod)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		//IL_004a: 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_005c: Expected O, but got Unknown
		//IL_005c: Expected O, but got Unknown
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		DebugView debugView = (Object.op_Implicit((Object)(object)Plugin.Instance) ? Plugin.Instance.View : null);
		if (debugView == null)
		{
			return;
		}
		try
		{
			if (__instance is ZDO)
			{
				debugView.Changed((ZDO)__instance);
			}
			else if (__originalMethod.Name == "AddInstance")
			{
				debugView.Add((ZDO)__args[0], (ZNetView)__args[1]);
			}
			else if (__instance is VisEquipment)
			{
				debugView.ModelsChanged((Component)(VisEquipment)__instance);
			}
		}
		catch
		{
		}
	}
}
internal enum DisplayMode
{
	Off,
	All,
	InteractiveAndMobs,
	MobsOnly
}
internal static class DisplayModes
{
	internal static DisplayMode Current(bool enabled, bool interactiveOnly, bool mobsOnly = false)
	{
		if (enabled)
		{
			if (!mobsOnly)
			{
				if (!interactiveOnly)
				{
					return DisplayMode.All;
				}
				return DisplayMode.InteractiveAndMobs;
			}
			return DisplayMode.MobsOnly;
		}
		return DisplayMode.Off;
	}

	internal static DisplayMode Next(DisplayMode mode)
	{
		return mode switch
		{
			DisplayMode.InteractiveAndMobs => DisplayMode.MobsOnly, 
			DisplayMode.All => DisplayMode.InteractiveAndMobs, 
			DisplayMode.Off => DisplayMode.All, 
			_ => DisplayMode.Off, 
		};
	}
}
internal sealed class NativeColours
{
	private struct Key : IEquatable<Key>
	{
		internal OwnerKey Owner;

		internal int Cull;

		public bool Equals(Key other)
		{
			if (Owner.Equals(other.Owner))
			{
				return Cull == other.Cull;
			}
			return false;
		}

		public override bool Equals(object other)
		{
			if (other is Key)
			{
				return Equals((Key)other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return Owner.GetHashCode() ^ (Cull * 397);
		}
	}

	private sealed class Paint
	{
		internal Material Material;

		internal int Frame;
	}

	private readonly Dictionary<Key, Paint> cache = new Dictionary<Key, Paint>();

	private readonly List<Key> retired = new List<Key>();

	private Shader shader;

	private int seed;

	private int frame;

	private float strength;

	private bool configured;

	private static readonly int colour = Shader.PropertyToID("_Color");

	internal void Configure(int nextSeed, float nextStrength)
	{
		frame++;
		nextStrength = Mathf.Clamp01(nextStrength);
		if (configured && seed == nextSeed && strength == nextStrength)
		{
			return;
		}
		seed = nextSeed;
		strength = nextStrength;
		configured = true;
		foreach (KeyValuePair<Key, Paint> item in cache)
		{
			SetColour(item.Key.Owner, item.Value.Material);
		}
	}

	private void SetColour(OwnerKey owner, Material material)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		Rgb rgb = OwnerColour.Get(owner, seed);
		material.SetColor(colour, new Color(rgb.R, rgb.G, rgb.B, strength));
	}

	internal Material Get(OwnerKey owner, int cull)
	{
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		Key key = new Key
		{
			Owner = owner,
			Cull = cull
		};
		if (!cache.TryGetValue(key, out var value))
		{
			if (!Object.op_Implicit((Object)(object)shader))
			{
				shader = Shader.Find("Hidden/Internal-Colored");
			}
			if (!Object.op_Implicit((Object)(object)shader) || !shader.isSupported)
			{
				throw new NotSupportedException("PUP-style flat shader unavailable.");
			}
			Material val = new Material(shader);
			((Object)val).name = "BotanicOwnerDebugPaint";
			((Object)val).hideFlags = (HideFlags)61;
			val.enableInstancing = false;
			Material val2 = val;
			Paint paint = new Paint();
			paint.Material = val2;
			value = paint;
			cache.Add(key, value);
			val2.SetInt("_SrcBlend", 5);
			val2.SetInt("_DstBlend", 10);
			val2.SetInt("_ZWrite", 0);
			val2.SetInt("_ZTest", 8);
			val2.SetInt("_Cull", cull);
			SetColour(owner, val2);
		}
		value.Frame = frame;
		return value.Material;
	}

	internal void Sweep()
	{
		retired.Clear();
		foreach (KeyValuePair<Key, Paint> item in cache)
		{
			if (item.Value.Frame < frame - 2)
			{
				retired.Add(item.Key);
			}
		}
		foreach (Key item2 in retired)
		{
			Object.Destroy((Object)(object)cache[item2].Material);
			cache.Remove(item2);
		}
		retired.Clear();
	}

	internal void Clear()
	{
		foreach (Paint value in cache.Values)
		{
			if (Object.op_Implicit((Object)(object)value.Material))
			{
				Object.Destroy((Object)(object)value.Material);
			}
		}
		cache.Clear();
		retired.Clear();
		shader = null;
	}
}
internal enum OwnerKind
{
	Unknown,
	Unowned,
	Server,
	Player
}
internal struct OwnerKey : IEquatable<OwnerKey>
{
	internal OwnerKind Kind;

	internal long Uid;

	internal static OwnerKey Resolve(bool known, long owner, long server)
	{
		if (!known)
		{
			return new OwnerKey
			{
				Kind = OwnerKind.Unknown
			};
		}
		if (owner == 0)
		{
			return new OwnerKey
			{
				Kind = OwnerKind.Unowned
			};
		}
		return new OwnerKey
		{
			Kind = ((owner == server && server != 0) ? OwnerKind.Server : OwnerKind.Player),
			Uid = owner
		};
	}

	public bool Equals(OwnerKey other)
	{
		if (Kind == other.Kind)
		{
			return Uid == other.Uid;
		}
		return false;
	}

	public override bool Equals(object other)
	{
		if (other is OwnerKey)
		{
			return Equals((OwnerKey)other);
		}
		return false;
	}

	public override int GetHashCode()
	{
		return ((int)Kind * 397) ^ Uid.GetHashCode();
	}
}
internal struct Rgb
{
	internal float R;

	internal float G;

	internal float B;

	internal Rgb(float r, float g, float b)
	{
		R = r;
		G = g;
		B = b;
	}
}
internal static class OwnerColour
{
	internal static Rgb Get(OwnerKey key, int seed)
	{
		if (key.Kind == OwnerKind.Server)
		{
			return new Rgb(1f, 1f, 1f);
		}
		if (key.Kind == OwnerKind.Unowned)
		{
			return new Rgb(0.18f, 0.18f, 0.18f);
		}
		if (key.Kind == OwnerKind.Unknown)
		{
			return new Rgb(0.22f, 0.3f, 0.4f);
		}
		ulong num = (ulong)key.Uid ^ ((ulong)(uint)seed << 32) ^ 0x9E3779B97F4A7C15uL;
		num = (num ^ (num >> 30)) * 13787848793156543929uL;
		num = (num ^ (num >> 27)) * 10723151780598845931uL;
		num ^= num >> 31;
		float num2 = (float)(num & 0xFFFFFF) / 16777216f * 6f;
		int num3 = (int)num2;
		float num4 = num2 - (float)num3;
		float num5 = 1f - num4;
		float num6 = num4;
		return num3 switch
		{
			0 => new Rgb(1f, num6, 0f), 
			1 => new Rgb(num5, 1f, 0f), 
			2 => new Rgb(0f, 1f, num6), 
			3 => new Rgb(0f, num5, 1f), 
			4 => new Rgb(num6, 0f, 1f), 
			_ => new Rgb(1f, 0f, num5), 
		};
	}
}
[BepInPlugin("botanic.owner.debug", "BotanicOwnerDebug", "1.0.7")]
public sealed class Plugin : BaseUnityPlugin
{
	internal const string Id = "botanic.owner.debug";

	internal static Plugin Instance;

	internal DebugView View;

	private ConfigEntry<bool> show;

	private ConfigEntry<bool> interactiveOnly;

	private ConfigEntry<bool> mobsOnly;

	private ConfigEntry<bool> legend;

	private ConfigEntry<bool> logs;

	private ConfigEntry<KeyboardShortcut> hotkey;

	private ConfigEntry<int> seed;

	private ConfigEntry<float> opacity;

	private ConfigEntry<float> distance;

	private bool rejected;

	private void Awake()
	{
		//IL_0085: 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)
		//IL_0119: Expected O, but got Unknown
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Expected O, but got Unknown
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Invalid comparison between Unknown and I4
		Instance = this;
		show = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ShowOwnerColours", false, "Local flat owner overlay for buildings and mobs; players, effects and loose items excluded. F10 cycles Off -> All -> Interactions and mobs -> Mobs only -> Off. No server/client ownership changes.");
		interactiveOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "InteractiveObjectsOnly", false, "When ShowOwnerColours is enabled, hide buildings without an Interactable belonging to their network object. All NPC mobs remain visible. F10 cycles this setting. Independent of Authority server categories/config; existing player/effect/loose-item exclusions remain.");
		mobsOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "MobsOnly", false, "When ShowOwnerColours is enabled, show only NPC mobs (wild and tamed); players remain excluded. Takes precedence over InteractiveObjectsOnly. F10 cycles this setting.");
		hotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "ToggleKey", new KeyboardShortcut((KeyCode)291, (KeyCode[])(object)new KeyCode[0]), "Cycle Off -> All -> Interactions and mobs -> Mobs only -> Off while playing. No toggle while typing or a game menu is open.");
		legend = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ShowLegend", true, "Show owner colours and names. White=server, dark grey=unowned, saturated colours=players, blue-grey=unknown.");
		seed = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "ColourSeed", 1337, "Same owner ID and seed give the same pseudo-random colour on all clients. Does not use game RNG.");
		opacity = ((BaseUnityPlugin)this).Config.Bind<float>("Debug", "ColourStrength", 1f, new ConfigDescription("Owner colour strength, default full colour. Original materials stay unchanged. Old Opacity setting is no longer used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.15f, 1f), new object[0]));
		distance = ((BaseUnityPlugin)this).Config.Bind<float>("Debug", "MaximumDistance", 200f, new ConfigDescription("Overlay range from camera; native renderer visibility checked every frame.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 1000f), new object[0]));
		logs = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableLogs", false, "Log optional debug errors. Default false.");
		if ((int)SystemInfo.graphicsDeviceType == 4)
		{
			((Behaviour)this).enabled = false;
		}
	}

	private void Update()
	{
		//IL_0031: 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)
		if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated())
		{
			Stop();
			return;
		}
		if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
		{
			KeyboardShortcut value = hotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown() && !InputBlocked())
			{
				DisplayMode displayMode = DisplayModes.Next(DisplayModes.Current(show.Value, interactiveOnly.Value, mobsOnly.Value));
				interactiveOnly.Value = displayMode == DisplayMode.InteractiveAndMobs;
				mobsOnly.Value = displayMode == DisplayMode.MobsOnly;
				show.Value = displayMode != DisplayMode.Off;
				rejected = false;
			}
		}
		if (!show.Value || !Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || ZDOMan.instance == null)
		{
			Stop();
			if (!show.Value)
			{
				rejected = false;
			}
		}
		else if (!rejected && View == null)
		{
			try
			{
				View = new DebugView();
			}
			catch (Exception error)
			{
				Error(error);
			}
		}
	}

	private static bool InputBlocked()
	{
		if ((!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)TextInput.instance) || !TextInput.IsVisible()) && !Menu.IsVisible())
		{
			return InventoryGui.IsVisible();
		}
		return true;
	}

	private void LateUpdate()
	{
		if (View == null)
		{
			return;
		}
		try
		{
			View.Draw(seed.Value, opacity.Value, distance.Value, interactiveOnly.Value, mobsOnly.Value);
		}
		catch (Exception error)
		{
			Error(error);
		}
	}

	private void OnGUI()
	{
		//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: 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_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		if (View == null || !legend.Value)
		{
			return;
		}
		Color color = GUI.color;
		try
		{
			List<KeyValuePair<OwnerKey, string>> list = View.Legend;
			GUI.Box(new Rect(12f, 50f, 370f, (float)(111 + list.Count * 22)), "Botanic Besitz-Debug 1.0.7 – " + hotkey.Value);
			GUI.Label(new Rect(22f, 78f, 350f, 22f), "Modus: " + View.ModeLabel);
			int num = 100;
			foreach (KeyValuePair<OwnerKey, string> item in list)
			{
				Rgb rgb = OwnerColour.Get(item.Key, seed.Value);
				GUI.color = new Color(rgb.R, rgb.G, rgb.B, 1f);
				GUI.Label(new Rect(23f, (float)num, 22f, 22f), "■");
				GUI.color = color;
				GUI.Label(new Rect(49f, (float)num, 320f, 22f), item.Value);
				num += 22;
			}
			GUI.Label(new Rect(22f, (float)(num + 4), 350f, 24f), View.Note);
			GUI.Label(new Rect(22f, (float)(num + 25), 350f, 24f), "Gezeichnet: " + View.PaintedCount + " / erfasst: " + View.TrackedCount);
		}
		finally
		{
			GUI.color = color;
		}
	}

	private void Error(Exception error)
	{
		rejected = true;
		Stop();
		if (logs.Value)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Owner debug disabled: " + error.Message));
		}
	}

	private void Stop()
	{
		DebugView view = View;
		View = null;
		view?.Dispose();
	}

	private void OnDestroy()
	{
		Stop();
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void OnDisable()
	{
		Stop();
	}
}