Decompiled source of BotanicPupPatch v2.1.0

BotanicPupPatch.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

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

internal abstract class CellReadiness
{
	internal abstract bool Ready(Vector3 point, float radius);

	internal static CellReadiness Create(MonoBehaviour pup)
	{
		Type type = ((object)pup).GetType();
		Type type2 = type.GetField("multiplayerRemoteCells", BindingFlags.Instance | BindingFlags.NonPublic).FieldType.GetGenericArguments()[1];
		Type type3 = type.GetField("fastColliderCells", BindingFlags.Instance | BindingFlags.NonPublic).FieldType.GetGenericArguments()[1];
		Type type4 = type.GetField("records", BindingFlags.Instance | BindingFlags.NonPublic).FieldType.GetGenericArguments()[1].GetGenericArguments()[0];
		return (CellReadiness)Activator.CreateInstance(typeof(TypedCellReadiness<, , >).MakeGenericType(type2, type3, type4), pup);
	}
}
internal sealed class TypedCellReadiness<R, F, B> : CellReadiness where R : class where F : class
{
	private readonly MonoBehaviour pup;

	private readonly Func<object, Dictionary<Vector2Int, List<B>>> records;

	private readonly Func<object, Dictionary<Vector2Int, R>> remotes;

	private readonly Func<object, Dictionary<Vector2Int, F>> fasts;

	private readonly Func<object, Dictionary<Vector2Int, GameObject>> roots;

	private readonly Func<object, Dictionary<Vector2Int, string>> ids;

	private readonly Func<object, Dictionary<GameObject, string>> validated;

	private readonly Func<object, Dictionary<Vector2Int, bool>> active;

	private readonly Func<object, Dictionary<Vector2Int, HashSet<string>>> loaded;

	private readonly Func<object, HashSet<Vector2Int>> physics;

	private readonly Func<object, HashSet<Vector2Int>> verified;

	private readonly Func<object, HashSet<Vector2Int>> worldRestores;

	private readonly Func<object, HashSet<Vector2Int>> vanillaRestores;

	private readonly Func<object, HashSet<Vector2Int>> loading;

	private readonly Func<object, int> queued;

	private readonly Func<R, string> remoteId;

	private readonly Func<F, string> fastId;

	private readonly Func<F, GameObject> fastRoot;

	private readonly Func<F, HashSet<string>> required;

	private readonly Func<Vector2Int, bool> pending;

	public TypedCellReadiness(MonoBehaviour value)
	{
		pup = value;
		Type type = ((object)pup).GetType();
		records = Read<Dictionary<Vector2Int, List<B>>>(type, "records");
		remotes = Read<Dictionary<Vector2Int, R>>(type, "multiplayerRemoteCells");
		fasts = Read<Dictionary<Vector2Int, F>>(type, "fastColliderCells");
		roots = Read<Dictionary<Vector2Int, GameObject>>(type, "clusters");
		ids = Read<Dictionary<Vector2Int, string>>(type, "cacheIds");
		validated = Read<Dictionary<GameObject, string>>(type, "validatedReplacementCacheIds");
		active = Read<Dictionary<Vector2Int, bool>>(type, "colliderCellActive");
		loaded = Read<Dictionary<Vector2Int, HashSet<string>>>(type, "fastLoadedSubclusters");
		physics = Read<HashSet<Vector2Int>>(type, "multiplayerRemotePhysicsReady");
		verified = Read<HashSet<Vector2Int>>(type, "verifiedRemoteReplacements");
		worldRestores = Read<HashSet<Vector2Int>>(type, "pendingWorldSaveRestores");
		vanillaRestores = Read<HashSet<Vector2Int>>(type, "multiplayerPendingVanillaRestore");
		loading = Read<HashSet<Vector2Int>>(type, "loadingCells");
		queued = Read<int>(type, "queuedColliderCount");
		remoteId = FieldReader.Create<R, string>(Field(typeof(R), "cacheId"));
		fastId = FieldReader.Create<F, string>(Field(typeof(F), "cacheId"));
		fastRoot = FieldReader.Create<F, GameObject>(Field(typeof(F), "root"));
		required = FieldReader.Create<F, HashSet<string>>(Field(typeof(F), "required"));
		pending = (Func<Vector2Int, bool>)Delegate.CreateDelegate(typeof(Func<Vector2Int, bool>), pup, type.GetMethod("HasPendingCollidersForCell", BindingFlags.Instance | BindingFlags.NonPublic));
	}

	private static FieldInfo Field(Type type, string name)
	{
		return type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
	}

	private static Func<object, T> Read<T>(Type type, string name)
	{
		return FieldReader.Create<object, T>(Field(type, name));
	}

	internal override bool Ready(Vector3 point, float radius)
	{
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_01af: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_029e: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0392: Unknown result type (might be due to invalid IL or missing references)
		//IL_0306: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0338: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
		int num = Mathf.FloorToInt((point.x - radius) / 64f);
		int num2 = Mathf.FloorToInt((point.x + radius) / 64f);
		int num3 = Mathf.FloorToInt((point.z - radius) / 64f);
		int num4 = Mathf.FloorToInt((point.z + radius) / 64f);
		Dictionary<Vector2Int, R> dictionary = remotes(pup);
		Dictionary<Vector2Int, GameObject> dictionary2 = roots(pup);
		Dictionary<Vector2Int, bool> dictionary3 = active(pup);
		Vector2Int val = default(Vector2Int);
		for (int i = num; i <= num2; i++)
		{
			for (int j = num3; j <= num4; j++)
			{
				float num5 = Mathf.Max(0f, Mathf.Max((float)i * 64f - point.x, point.x - (float)(i + 1) * 64f));
				float num6 = Mathf.Max(0f, Mathf.Max((float)j * 64f - point.z, point.z - (float)(j + 1) * 64f));
				if (num5 * num5 + num6 * num6 > radius * radius)
				{
					continue;
				}
				((Vector2Int)(ref val))..ctor(i, j);
				R value;
				bool flag = dictionary.TryGetValue(val, out value);
				List<B> value2;
				bool flag2 = records(pup).TryGetValue(val, out value2);
				if (!flag && !flag2)
				{
					continue;
				}
				if (worldRestores(pup).Contains(val) || vanillaRestores(pup).Contains(val) || !flag2 || value2 == null || value2.Count == 0 || !ids(pup).TryGetValue(val, out var value3) || string.IsNullOrEmpty(value3) || value3 == "RESTORE_PENDING" || (flag && value3 != remoteId(value)))
				{
					return false;
				}
				GameObject value4;
				if (flag)
				{
					if (!physics(pup).Contains(val) || !verified(pup).Contains(val) || !dictionary2.TryGetValue(val, out value4) || !Object.op_Implicit((Object)(object)value4) || !value4.activeInHierarchy || !validated(pup).TryGetValue(value4, out var value5) || value5 != remoteId(value))
					{
						return false;
					}
				}
				else
				{
					if (ZNet.instance.IsDedicated() || (!ids(pup).ContainsKey(val) && !dictionary2.ContainsKey(val)))
					{
						continue;
					}
					if (fasts(pup).TryGetValue(val, out var value6))
					{
						value4 = fastRoot(value6);
						if (!Object.op_Implicit((Object)(object)value4) || !value4.activeInHierarchy || !ids(pup).TryGetValue(val, out var value7) || value7 != fastId(value6) || !loaded(pup).TryGetValue(val, out var value8))
						{
							return false;
						}
						foreach (string item in required(value6))
						{
							if (!value8.Contains(item))
							{
								return false;
							}
						}
					}
					else if (!dictionary2.TryGetValue(val, out value4) || !Object.op_Implicit((Object)(object)value4) || !value4.activeInHierarchy || loading(pup).Contains(val) || (queued(pup) > 0 && pending(val)))
					{
						return false;
					}
				}
				if (dictionary3.TryGetValue(val, out var value9) && !value9)
				{
					return false;
				}
			}
		}
		return true;
	}
}
internal static class FgnAttackRetry
{
	private sealed class Ticket
	{
		internal Humanoid Mob;

		internal ZDO Data;

		internal ZDOMan World;

		internal ZNet Network;

		internal long Owner;

		internal long Epoch;

		internal uint Revision;
	}

	private static ConditionalWeakTable<Attack, Ticket> waiting = new ConditionalWeakTable<Attack, Ticket>();

	private static Func<Humanoid, Attack> currentAttack;

	private static SynchronizationContext context;

	private static Action<Humanoid, ZDO> retry;

	private static Action<Exception> fail;

	private static readonly SendOrPostCallback dispatch = Dispatch;

	private static long epoch;

	private static int armedCount;

	internal static void Install(Harmony harmony, Action<Humanoid, ZDO> onEnd, Action<Exception> onFailure)
	{
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Expected O, but got Unknown
		SynchronizationContext current = SynchronizationContext.Current;
		FieldInfo fieldInfo = AccessTools.Field(typeof(Humanoid), "m_currentAttack");
		MethodInfo methodInfo = AccessTools.Method(typeof(Attack), "Stop", Type.EmptyTypes, (Type[])null);
		if (current == null || current.GetType().FullName != "UnityEngine.UnitySynchronizationContext" || fieldInfo == null || fieldInfo.IsStatic || fieldInfo.FieldType != typeof(Attack) || methodInfo == null || methodInfo.IsStatic || methodInfo.ReturnType != typeof(void))
		{
			throw new NotSupportedException("Native attack-end retry contract unavailable.");
		}
		currentAttack = FieldReader.Create<Humanoid, Attack>(fieldInfo);
		context = current;
		retry = onEnd;
		fail = onFailure;
		Reset();
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(FgnAttackRetry), "Stopped", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	internal static bool Arm(Character mob, ZDO data)
	{
		Humanoid val = (Humanoid)(object)((mob is Humanoid) ? mob : null);
		if (!Object.op_Implicit((Object)(object)val) || data == null || currentAttack == null || context == null)
		{
			return false;
		}
		Attack val2 = currentAttack(val);
		if (val2 == null || val2.IsDone())
		{
			return false;
		}
		if (waiting.TryGetValue(val2, out var value))
		{
			if (value.Epoch == epoch && object.ReferenceEquals(value.Data, data) && object.ReferenceEquals(value.World, ZDOMan.instance) && object.ReferenceEquals(value.Network, ZNet.instance) && value.Owner == data.GetOwner() && value.Revision == data.OwnerRevision)
			{
				return true;
			}
			waiting.Remove(val2);
			armedCount--;
		}
		waiting.Add(val2, new Ticket
		{
			Mob = val,
			Data = data,
			World = ZDOMan.instance,
			Network = ZNet.instance,
			Owner = data.GetOwner(),
			Revision = data.OwnerRevision,
			Epoch = epoch
		});
		armedCount++;
		return true;
	}

	private static void Stopped(Attack __instance)
	{
		if (armedCount == 0)
		{
			return;
		}
		try
		{
			if (__instance.IsDone() && waiting.TryGetValue(__instance, out var value))
			{
				waiting.Remove(__instance);
				armedCount--;
				context.Post(dispatch, value);
			}
		}
		catch (Exception error)
		{
			Failed(error);
		}
	}

	private static void Dispatch(object state)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Ticket ticket = (Ticket)state;
			if (ticket.Epoch == epoch && object.ReferenceEquals(ticket.World, ZDOMan.instance) && object.ReferenceEquals(ticket.Network, ZNet.instance) && ticket.World != null && Object.op_Implicit((Object)(object)ticket.Mob) && ticket.Data.IsValid() && object.ReferenceEquals(ticket.World.GetZDO(ticket.Data.m_uid), ticket.Data) && ticket.Data.GetOwner() == ticket.Owner && ticket.Data.OwnerRevision == ticket.Revision && ticket.Owner == ZDOMan.GetSessionID())
			{
				retry(ticket.Mob, ticket.Data);
			}
		}
		catch (Exception error)
		{
			Failed(error);
		}
	}

	private static void Failed(Exception error)
	{
		try
		{
			if (fail != null)
			{
				fail(error);
			}
		}
		catch
		{
		}
	}

	internal static void Reset()
	{
		epoch++;
		waiting = new ConditionalWeakTable<Attack, Ticket>();
		armedCount = 0;
	}
}
internal sealed class FgnCombatBridge
{
	internal delegate bool EchoRtt(ZNetPeer peer, out float milliseconds);

	private const BindingFlags Flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	internal readonly MethodInfo Balance;

	internal readonly MethodInfo TryHandOff;

	internal readonly MethodInfo Veto;

	internal readonly MethodInfo OnResult;

	internal readonly Func<ZDO, double, bool> TryTransfer;

	internal readonly Func<bool> Active;

	internal readonly Func<bool> Simulation;

	internal readonly Func<double> NextPass;

	internal readonly Func<ZNetPeer, bool> Matched;

	internal readonly Func<ZNetPeer, float> Rtt;

	internal readonly EchoRtt MeasuredEcho;

	internal readonly Func<ZNetPeer, int> SocketPing;

	internal readonly Func<ZDO, bool> Held;

	internal readonly Func<ZDO, bool> Possessed;

	internal readonly Func<ZDOID, double, bool> Pending;

	internal readonly Action<ZDOID, long, double> MarkPending;

	internal readonly Action<ZDOID> ClearStreak;

	internal readonly Action<ZDOID> ClearOwned;

	internal readonly Dictionary<long, int> PerOwner;

	internal readonly Dictionary<ZDOID, double> Cooldowns;

	internal readonly List<ZNetPeer> Candidates;

	internal readonly MethodInfo TargetReader;

	internal FgnCombatBridge(Assembly assembly)
	{
		Type type = assembly.GetType("FiresGhettoNetworkMod.CreatureOwnership", throwOnError: true);
		RequireConstant(type, "PassSeconds", 2.0);
		RequireConstant(type, "PendingSeconds", 5.0);
		RequireConstant(type, "DeclinedCooldownSeconds", 6.0);
		RequireConstant(type, "ForwardHitsSeconds", 5.0);
		RequireConstant(type, "MaxHandOffsPerPass", 16);
		RequireConstant(type, "MaxHandOffsPerOwner", 6);
		Balance = Method(type, "Balance", typeof(void), typeof(ZDOMan));
		TryHandOff = Method(type, "TryHandOffCreature", typeof(bool), typeof(ZDO), typeof(double));
		TryTransfer = Bind<Func<ZDO, double, bool>>(TryHandOff);
		Veto = Method(type, "Veto", typeof(string), typeof(ZDO), typeof(long));
		OnResult = Method(type, "OnResult", typeof(void), typeof(ZRpc), typeof(ZDOID), typeof(bool), typeof(string));
		Active = Bind<Func<bool>>(Method(type, "Active", typeof(bool)));
		NextPass = Static<double>(Field(type, "s_nextPass", typeof(double)));
		Rtt = Bind<Func<ZNetPeer, float>>(Method(type, "Rtt", typeof(float), typeof(ZNetPeer)));
		Held = Bind<Func<ZDO, bool>>(Method(type, "IsHeld", typeof(bool), typeof(ZDO)));
		Type type2 = assembly.GetType("FiresGhettoNetworkMod.ValcastPossession", throwOnError: false);
		Possessed = ((type2 == null) ? new Func<ZDO, bool>(NoPossession) : Bind<Func<ZDO, bool>>(Method(type2, "IsPossessed", typeof(bool), typeof(ZDO))));
		Type type3 = assembly.GetType("FiresGhettoNetworkMod.FgnVersionGate", throwOnError: true);
		Matched = Bind<Func<ZNetPeer, bool>>(Method(type3, "IsMatched", typeof(bool), typeof(ZNetPeer)));
		Type type4 = assembly.GetType("FiresGhettoNetworkMod.ServerAuthorityPatches", throwOnError: true);
		Simulation = Static<bool>(Field(type4, "SimulationActive", typeof(bool)));
		Type type5 = assembly.GetType("FiresGhettoNetworkMod.FiresGhettoNetworkMod", throwOnError: true);
		ConfigEntry<bool> val = (ConfigEntry<bool>)Field(type5, "ConfigEnableServerOwnership", typeof(ConfigEntry<bool>)).GetValue(null);
		ConfigEntry<bool> val2 = (ConfigEntry<bool>)Field(type5, "ConfigEnableServerOwnershipSelective", typeof(ConfigEntry<bool>)).GetValue(null);
		if (val == null || val2 == null || val.Value || val2.Value)
		{
			throw new NotSupportedException("FGN AI ownership requires both FGN server ZDO ownership options OFF.");
		}
		Type type6 = assembly.GetType("FiresGhettoNetworkMod.ConnectionEcho", throwOnError: true);
		MeasuredEcho = Bind<EchoRtt>(Method(type6, "TryRtt", typeof(bool), typeof(ZNetPeer), typeof(float).MakeByRefType()));
		Type type7 = assembly.GetType("FiresGhettoNetworkMod.LinkController", throwOnError: true);
		SocketPing = Bind<Func<ZNetPeer, int>>(Method(type7, "PingMs", typeof(int), typeof(ZNetPeer)));
		PerOwner = (Dictionary<long, int>)Field(type, "s_perOwner", typeof(Dictionary<long, int>)).GetValue(null);
		Cooldowns = (Dictionary<ZDOID, double>)Field(type, "s_cooldowns", typeof(Dictionary<ZDOID, double>)).GetValue(null);
		Candidates = (List<ZNetPeer>)Field(type, "s_candidates", typeof(List<ZNetPeer>)).GetValue(null);
		FieldInfo field = type.GetField("s_pending", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		FieldInfo field2 = type.GetField("s_lastHitBy", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null || field2 == null || field.FieldType != field2.FieldType || !field.IsStatic)
		{
			throw new NotSupportedException("FGN stamp dictionary contract changed.");
		}
		Type type8 = field.FieldType.GetGenericArguments()[1];
		if (!type8.IsValueType || field.FieldType != typeof(Dictionary<, >).MakeGenericType(typeof(ZDOID), type8) || type8.GetField("Peer").FieldType != typeof(long) || type8.GetField("At").FieldType != typeof(double))
		{
			throw new NotSupportedException("FGN stamp contract changed.");
		}
		Pending = PendingReader(field, type8);
		MarkPending = StampWriter(field, type8);
		FieldInfo field3 = type.GetField("s_streaks", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field3 == null || !field3.IsStatic || !field3.FieldType.IsGenericType || field3.FieldType.GetGenericTypeDefinition() != typeof(Dictionary<, >) || field3.FieldType.GetGenericArguments()[0] != typeof(ZDOID))
		{
			throw new NotSupportedException("FGN ping streak contract changed.");
		}
		DynamicMethod dynamicMethod = new DynamicMethod("BotanicCombatClearStreak", typeof(void), new Type[1] { typeof(ZDOID) }, typeof(FgnCombatBridge).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldsfld, field3);
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Callvirt, field3.FieldType.GetMethod("Remove", new Type[1] { typeof(ZDOID) }));
		iLGenerator.Emit(OpCodes.Pop);
		iLGenerator.Emit(OpCodes.Ret);
		ClearStreak = (Action<ZDOID>)dynamicMethod.CreateDelegate(typeof(Action<ZDOID>));
		FieldInfo field4 = type.GetField("s_owned", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field4 == null || !field4.IsStatic || !field4.FieldType.IsGenericType || field4.FieldType.GetGenericTypeDefinition() != typeof(Dictionary<, >) || field4.FieldType.GetGenericArguments()[0] != typeof(ZDOID))
		{
			throw new NotSupportedException("FGN ownership hold contract changed.");
		}
		DynamicMethod dynamicMethod2 = new DynamicMethod("BotanicCombatClearOwned", typeof(void), new Type[1] { typeof(ZDOID) }, typeof(FgnCombatBridge).Module, skipVisibility: true);
		ILGenerator iLGenerator2 = dynamicMethod2.GetILGenerator();
		iLGenerator2.Emit(OpCodes.Ldsfld, field4);
		iLGenerator2.Emit(OpCodes.Ldarg_0);
		iLGenerator2.Emit(OpCodes.Callvirt, field4.FieldType.GetMethod("Remove", new Type[1] { typeof(ZDOID) }));
		iLGenerator2.Emit(OpCodes.Pop);
		iLGenerator2.Emit(OpCodes.Ret);
		ClearOwned = (Action<ZDOID>)dynamicMethod2.CreateDelegate(typeof(Action<ZDOID>));
		TargetReader = typeof(FgnTargetReader<>).MakeGenericType(type8).GetMethod("TryGetValue", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		TargetReader.DeclaringType.GetMethod("Initialize", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Invoke(null, null);
	}

	internal bool TryMeasuredRtt(ZNetPeer peer, out float value)
	{
		if (MeasuredEcho(peer, out value) && value > 0f && !float.IsNaN(value) && !float.IsInfinity(value))
		{
			return true;
		}
		int num = SocketPing(peer);
		value = num;
		return num > 0;
	}

	private static T Bind<T>(MethodInfo method) where T : class
	{
		return (T)(object)Delegate.CreateDelegate(typeof(T), method);
	}

	private static bool NoPossession(ZDO data)
	{
		return false;
	}

	private static void RequireConstant(Type type, string name, object expected)
	{
		FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null || !field.IsLiteral || !object.Equals(field.GetRawConstantValue(), expected))
		{
			throw new NotSupportedException("FGN ownership contract changed: " + name);
		}
	}

	private static MethodInfo Method(Type type, string name, Type returns, params Type[] args)
	{
		MethodInfo method = type.GetMethod(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
		if (method == null || method.ReturnType != returns)
		{
			throw new NotSupportedException("FGN method changed: " + name);
		}
		return method;
	}

	private static FieldInfo Field(Type type, string name, Type value)
	{
		FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null || field.FieldType != value || !field.IsStatic)
		{
			throw new NotSupportedException("FGN field changed: " + name);
		}
		return field;
	}

	private static Func<T> Static<T>(FieldInfo field)
	{
		DynamicMethod dynamicMethod = new DynamicMethod("BotanicCombatRead_" + field.Name, typeof(T), Type.EmptyTypes, typeof(FgnCombatBridge).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldsfld, field);
		iLGenerator.Emit(OpCodes.Ret);
		return (Func<T>)dynamicMethod.CreateDelegate(typeof(Func<T>));
	}

	private static Func<ZDOID, double, bool> PendingReader(FieldInfo field, Type stamp)
	{
		DynamicMethod dynamicMethod = new DynamicMethod("BotanicCombatPending", typeof(bool), new Type[2]
		{
			typeof(ZDOID),
			typeof(double)
		}, typeof(FgnCombatBridge).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		LocalBuilder local = iLGenerator.DeclareLocal(stamp);
		Label label = iLGenerator.DefineLabel();
		iLGenerator.Emit(OpCodes.Ldsfld, field);
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Callvirt, field.FieldType.GetMethod("TryGetValue"));
		iLGenerator.Emit(OpCodes.Brfalse, label);
		iLGenerator.Emit(OpCodes.Ldarg_1);
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Ldfld, stamp.GetField("At"));
		iLGenerator.Emit(OpCodes.Sub);
		iLGenerator.Emit(OpCodes.Ldc_R8, 5.0);
		iLGenerator.Emit(OpCodes.Clt);
		iLGenerator.Emit(OpCodes.Ret);
		iLGenerator.MarkLabel(label);
		iLGenerator.Emit(OpCodes.Ldc_I4_0);
		iLGenerator.Emit(OpCodes.Ret);
		return (Func<ZDOID, double, bool>)dynamicMethod.CreateDelegate(typeof(Func<ZDOID, double, bool>));
	}

	private static Action<ZDOID, long, double> StampWriter(FieldInfo field, Type stamp)
	{
		DynamicMethod dynamicMethod = new DynamicMethod("BotanicCombatPendingWrite", typeof(void), new Type[3]
		{
			typeof(ZDOID),
			typeof(long),
			typeof(double)
		}, typeof(FgnCombatBridge).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		LocalBuilder local = iLGenerator.DeclareLocal(stamp);
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Initobj, stamp);
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Ldarg_1);
		iLGenerator.Emit(OpCodes.Stfld, stamp.GetField("Peer"));
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Ldarg_2);
		iLGenerator.Emit(OpCodes.Stfld, stamp.GetField("At"));
		iLGenerator.Emit(OpCodes.Ldsfld, field);
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Ldloc, local);
		iLGenerator.Emit(OpCodes.Callvirt, field.FieldType.GetProperty("Item").GetSetMethod());
		iLGenerator.Emit(OpCodes.Ret);
		return (Action<ZDOID, long, double>)dynamicMethod.CreateDelegate(typeof(Action<ZDOID, long, double>));
	}
}
internal static class FgnTargetReader<TStamp>
{
	private static readonly Func<long, double, TStamp> create = Create();

	internal static void Initialize()
	{
		if (create == null)
		{
			throw new NotSupportedException();
		}
	}

	internal static bool TryGetValue(Dictionary<ZDOID, TStamp> unused, ZDOID id, out TStamp value)
	{
		//IL_0000: 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)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		if (!FgnCombatOwnership.ShouldOverride(id))
		{
			return unused.TryGetValue(id, out value);
		}
		if (!FgnCombatOwnership.TryTargetPeer(id, out var peer) || peer == 0)
		{
			value = default(TStamp);
			return false;
		}
		value = create(peer, Time.realtimeSinceStartupAsDouble);
		return true;
	}

	private static Func<long, double, TStamp> Create()
	{
		DynamicMethod dynamicMethod = new DynamicMethod("BotanicCombatTargetStamp", typeof(TStamp), new Type[2]
		{
			typeof(long),
			typeof(double)
		}, typeof(FgnCombatBridge).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		LocalBuilder local = iLGenerator.DeclareLocal(typeof(TStamp));
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Initobj, typeof(TStamp));
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Stfld, typeof(TStamp).GetField("Peer"));
		iLGenerator.Emit(OpCodes.Ldloca, local);
		iLGenerator.Emit(OpCodes.Ldarg_1);
		iLGenerator.Emit(OpCodes.Stfld, typeof(TStamp).GetField("At"));
		iLGenerator.Emit(OpCodes.Ldloc, local);
		iLGenerator.Emit(OpCodes.Ret);
		return (Func<long, double, TStamp>)dynamicMethod.CreateDelegate(typeof(Func<long, double, TStamp>));
	}
}
internal static class FgnCombatOwnership
{
	private sealed class Report
	{
		internal ZDO Data;

		internal long Owner;

		internal uint Revision;

		internal ZDOID Target;
	}

	private sealed class Lease
	{
		internal ZDO Data;

		internal long ReturnPeer;

		internal long SourceOwner;

		internal uint SourceRevision;

		internal double Requested;

		internal uint Pass;
	}

	private sealed class Forward
	{
		internal ZDO Data;

		internal long Owner;

		internal double Until;
	}

	private sealed class Watch
	{
		internal ZDO Data;

		internal long Owner;

		internal uint Revision;

		internal uint Pass;

		internal bool Acknowledged;

		internal bool AttackRejected;

		internal bool AttackReady;
	}

	private const long Protocol = 3L;

	internal static bool Enabled;

	internal static FgnCombatBridge Fgn;

	private static ConfigEntry<bool> configured;

	private static Version detectedVersion;

	internal static string InstallationStatus = "not initialized";

	private static ConfigEntry<int> maximumPing;

	private static ConfigEntry<float> nearbyRadius;

	private static Harmony harmony;

	private static Harmony sessionHarmony;

	private static bool serverConfirmed;

	private static ZDO localCapability;

	private static ConditionalWeakTable<BaseAI, Report> reports = new ConditionalWeakTable<BaseAI, Report>();

	private static readonly Dictionary<ZDOID, Lease> managed = new Dictionary<ZDOID, Lease>();

	private static readonly Dictionary<ZDOID, Forward> forwarded = new Dictionary<ZDOID, Forward>();

	private static readonly Dictionary<ZDOID, Watch> watching = new Dictionary<ZDOID, Watch>();

	private static readonly Dictionary<ZDOID, Report> receivedTargets = new Dictionary<ZDOID, Report>();

	private static readonly List<ZDOID> scratch = new List<ZDOID>();

	private static readonly KeyValuePair<int, int> targetHash = ZDO.GetHashZDOID("botanic.fgn.ai.target");

	private static readonly int sourceHash = StringExtensionMethods.GetStableHashCode("botanic.fgn.ai.source");

	private static readonly int revisionHash = StringExtensionMethods.GetStableHashCode("botanic.fgn.ai.revision");

	private static readonly int capabilityHash = StringExtensionMethods.GetStableHashCode("botanic.fgn.ai.protocol");

	private static readonly Func<BaseAI, ZNetView> aiView = FieldReader.Create<BaseAI, ZNetView>(AccessTools.Field(typeof(BaseAI), "m_nview"));

	private static ZDOMan world;

	private static ZNet network;

	private static int passMoves;

	private static uint pass;

	private static double forwardExpiry;

	private static bool collecting;

	private static bool Active
	{
		get
		{
			if (Enabled && Fgn != null)
			{
				return Fgn.Active();
			}
			return false;
		}
	}

	internal static void Install(ConfigFile config, Assembly assembly, Version version)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Expected O, but got Unknown
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Expected O, but got Unknown
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Expected O, but got Unknown
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Expected O, but got Unknown
		configured = config.Bind<bool>("FGN AI Ownership", "EnableAIOwnership", assembly != null, "Use the mob's active AI player target instead of its last attacker for FGN ownership. The existing two-second scan enables target-change events only with two eligible nearby players. Ongoing attacks block transfer; attack rejection adds no cooldown and retries once after native attack end, even without a target change. All FGN protections and periodic retries remain. Default: on with FGN. Same version on server and clients; restart required. False keeps normal FGN ownership.");
		maximumPing = config.Bind<int>("FGN AI Ownership", "ServerFallbackPingMs", 200, new ConfigDescription("Target ping limit in milliseconds. Above it, server takeover requires another eligible player within ServerFallbackRadius and active FGN SSS. 0: target ping must exceed the best other active candidate's ping by 250 ms. Server setting.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2000), new object[0]));
		nearbyRadius = config.Bind<float>("FGN AI Ownership", "ServerFallbackRadius", 6f, new ConfigDescription("Maximum distance in metres from the mob to another eligible player for high-ping server takeover. Only existing FGN candidates whose 3x3-sector scan includes the mob count. Otherwise the AI target keeps ownership. Server setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), new object[0]));
		detectedVersion = version;
		InstallationStatus = ((assembly == null) ? "FGN absent" : "disabled in config");
		if (assembly != null)
		{
			try
			{
				sessionHarmony = new Harmony("botanic.pup.patch.fgn.aimode");
				sessionHarmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "OnNewConnection", (Type[])null, (Type[])null), Hook("SessionConnection"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch (Exception ex)
			{
				if (sessionHarmony != null)
				{
					try
					{
						sessionHarmony.UnpatchSelf();
					}
					catch
					{
					}
				}
				InstallationStatus = "mode endpoint failed: " + ex.GetBaseException().Message;
				if (Plugin.Diagnostics)
				{
					Plugin.Warn("Optional FGN AI mode unavailable: " + ex.Message);
				}
				return;
			}
		}
		if (!configured.Value || assembly == null)
		{
			return;
		}
		try
		{
			Fgn = new FgnCombatBridge(assembly);
			RewriteTarget(PatchProcessor.GetOriginalInstructions((MethodBase)Fgn.TryHandOff, (ILGenerator)null));
			RewriteVeto(PatchProcessor.GetOriginalInstructions((MethodBase)Fgn.Veto, (ILGenerator)null));
			MethodInfo methodInfo = AccessTools.Method(typeof(ZDOMan), "ReleaseNearbyZDOS", (Type[])null, (Type[])null);
			RewriteNativeOwner(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo, (ILGenerator)null));
			harmony = new Harmony("botanic.pup.patch.fgn.aiownership");
			harmony.Patch((MethodBase)Fgn.TryHandOff, Hook("TryPrefix"), Hook("TryPostfix"), Hook("RewriteTarget"), (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)Fgn.Veto, (HarmonyMethod)null, Hook("VetoPostfix"), Hook("RewriteVeto"), (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)Fgn.OnResult, Hook("ResultPrefix"), Hook("ResultPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)Fgn.Balance, Hook("BalancePrefix"), Hook("BalancePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, Hook("RewriteNativeOwner"), (HarmonyMethod)null, (HarmonyMethod)null);
			FgnTargetEvents.Install(harmony, assembly, Watching, Observe, Fail);
			FgnAttackRetry.Install(harmony, OnAttackEnded, Fail);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(BaseAI), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, Hook("AiAwakePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Player), "OnDestroy", (Type[])null, (Type[])null), Hook("PlayerDestroyPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "SetCharacterID", (Type[])null, (Type[])null), (HarmonyMethod)null, Hook("CapabilityPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null), Hook("DamagePrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null), Hook("Reset"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Enabled = true;
			InstallationStatus = "installed";
		}
		catch (Exception ex2)
		{
			Enabled = false;
			if (harmony != null)
			{
				try
				{
					harmony.UnpatchSelf();
				}
				catch
				{
				}
			}
			Fgn = null;
			InstallationStatus = "installation failed: " + ex2.GetBaseException().Message;
			if (Plugin.Diagnostics)
			{
				Plugin.Warn("Optional FGN AI ownership not installed: " + ex2.Message);
			}
		}
	}

	private static HarmonyMethod Hook(string name)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected O, but got Unknown
		return new HarmonyMethod(AccessTools.Method(typeof(FgnCombatOwnership), name, (Type[])null, (Type[])null));
	}

	internal static IEnumerable<string> DescribeStatus()
	{
		yield return "BotanicPupPatch 2.1.0; FGN " + ((detectedVersion == null) ? "absent" : detectedVersion.ToString());
		yield return "AI ownership: config=" + ((configured == null) ? "unknown" : configured.Value.ToString()) + "; hooks=" + Enabled + "; " + InstallationStatus;
		bool server = Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated();
		yield return "Mode: " + ((!Object.op_Implicit((Object)(object)ZNet.instance)) ? "no session" : (server ? "dedicated server" : "client/local game")) + "; server balance=" + (server ? Active.ToString() : "n/a") + "; server confirmed=" + serverConfirmed;
		bool current = ZDOMan.instance != null && object.ReferenceEquals(world, ZDOMan.instance) && object.ReferenceEquals(network, ZNet.instance);
		yield return "Current session=" + current + "; watched=" + watching.Count + "; reports=" + receivedTargets.Count + "; server leases=" + managed.Count;
		if (!current || !Object.op_Implicit((Object)(object)ZNet.instance))
		{
			yield break;
		}
		if (!server)
		{
			ZDO player = world.GetZDO(ZNet.instance.LocalPlayerCharacterID);
			yield return string.Concat("Local peer=", ZDOMan.GetSessionID(), "; player=", ZNet.instance.LocalPlayerCharacterID, "; protocol=", (player == null) ? "missing player ZDO" : player.GetLong(capabilityHash, 0L).ToString(), " (required: ", 3L, ")");
		}
		else
		{
			if (Fgn == null)
			{
				yield break;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null)
				{
					ZDO player2 = world.GetZDO(peer.m_characterID);
					yield return string.Concat("Peer ", peer.m_uid, "; player=", peer.m_characterID, ": ready=", peer.IsReady(), "; FGN matched=", Fgn.Matched(peer), "; protocol=", (player2 == null) ? "missing player ZDO" : player2.GetLong(capabilityHash, 0L).ToString());
				}
			}
		}
	}

	internal unsafe static string DescribeMob(ZDO data, BaseAI ai)
	{
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		if (data == null || !data.IsValid())
		{
			return "mob ZDO unavailable";
		}
		ZDOID target;
		string text = ((!ReadTarget(data, out target)) ? "missing/stale" : (((ZDOID)(ref target)).IsNone() ? "none/non-player" : ((object)(*(ZDOID*)(&target))/*cast due to .constrained prefix*/).ToString()));
		string text2 = "unavailable";
		ZNetView val = (Object.op_Implicit((Object)(object)ai) ? aiView(ai) : null);
		if (Object.op_Implicit((Object)(object)ai) && Object.op_Implicit((Object)(object)val) && val.IsValid() && object.ReferenceEquals(val.GetZDO(), data))
		{
			Character targetCreature = ai.GetTargetCreature();
			text2 = ((Object.op_Implicit((Object)(object)targetCreature) && targetCreature is Player && !targetCreature.IsDead()) ? ((object)targetCreature.GetZDOID()/*cast due to .constrained prefix*/).ToString() : "none/non-player") + (val.IsOwner() ? " (authoritative)" : " (replica; not authoritative)");
		}
		return string.Concat("mob=", data.m_uid, "; owner=", data.GetOwner(), "; revision=", data.OwnerRevision, "; rule=", DiagnosticRule(data), "; AI=", text2, "; report=", text, DiagnosticLastHit(data.m_uid));
	}

	private static string DiagnosticRule(ZDO data)
	{
		//IL_00ee: 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_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled || Fgn == null)
		{
			return "stock FGN: " + InstallationStatus;
		}
		if (!object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || !Object.op_Implicit((Object)(object)ZNet.instance))
		{
			return "stock FGN: session not initialized";
		}
		bool flag = ZNet.instance.IsDedicated();
		if (flag && !Active)
		{
			return "stock FGN: server balance inactive";
		}
		if (!flag && !serverConfirmed)
		{
			return "stock FGN: server mode unconfirmed";
		}
		if (data.GetBool(ZDOVars.s_tamed, false))
		{
			return "outside AI policy: tamed (ownership unchanged)";
		}
		if (data.GetLong(ZDOVars.s_user, 0L) != 0)
		{
			return "outside AI policy: controlled/ridden";
		}
		if (Fgn.Held(data))
		{
			return "outside AI policy: FGN combat hold";
		}
		if (Fgn.Possessed(data))
		{
			return "outside AI policy: Valcast possessed";
		}
		if (flag && data.GetOwner() == ZDOMan.GetSessionID() && !managed.ContainsKey(data.m_uid))
		{
			return "outside AI policy: server-owned";
		}
		if (flag && data.GetOwner() == ZDOMan.GetSessionID() && ShouldOverride(data.m_uid))
		{
			return "AI target (temporary server lease)";
		}
		if (flag && data.GetOwner() != ZDOMan.GetSessionID() && !Participating(ZNet.instance.GetPeer(data.GetOwner())))
		{
			return "stock FGN: owner protocol/FGN participation missing";
		}
		if (!flag && data.GetOwner() != ZDOMan.GetSessionID())
		{
			return "observer: only the current owner receives the server watch";
		}
		if (!Watched(data))
		{
			return "stock FGN: not marked by two-player scan; check SERVER row";
		}
		Watch watch = watching[data.m_uid];
		if (flag && !watch.Acknowledged)
		{
			return "waiting for owner's initial target report";
		}
		if (!ShouldOverride(data.m_uid))
		{
			return "stock FGN: no valid target report";
		}
		return "AI target";
	}

	private static string DiagnosticLastHit(ZDOID id)
	{
		//IL_0044: 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)
		if (Fgn == null)
		{
			return "";
		}
		FieldInfo fieldInfo = AccessTools.Field(Fgn.TryHandOff.DeclaringType, "s_lastHitBy");
		IDictionary dictionary = ((fieldInfo == null) ? null : (fieldInfo.GetValue(null) as IDictionary));
		if (dictionary == null || !dictionary.Contains(id))
		{
			return "; last attacker=none";
		}
		object obj = dictionary[id];
		Type type = obj.GetType();
		long num = (long)type.GetField("Peer").GetValue(obj);
		double num2 = (double)type.GetField("At").GetValue(obj);
		return "; last attacker=" + num + "; age=" + (Time.realtimeSinceStartupAsDouble - num2).ToString("F1", CultureInfo.InvariantCulture) + "s";
	}

	internal static string DescribeServerMob(ZDOID id)
	{
		//IL_0048: 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_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0262: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0379: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0488: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_04eb: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
		{
			return "SERVER: dedicated session unavailable";
		}
		if (Fgn == null || !Enabled)
		{
			return "SERVER: " + InstallationStatus;
		}
		ZDO val = ((ZDOMan.instance == null) ? null : ZDOMan.instance.GetZDO(id));
		if (val == null || !val.IsValid())
		{
			return string.Concat("SERVER mob=", id, ": ZDO unavailable");
		}
		string text = "SERVER " + DescribeMob(val, null) + "; scan pass=" + pass;
		Type declaringType = Fgn.Balance.DeclaringType;
		FieldInfo fieldInfo = AccessTools.Field(declaringType, "s_prefabsScanned");
		FieldInfo fieldInfo2 = AccessTools.Field(declaringType, "s_creaturePrefabs");
		FieldInfo fieldInfo3 = AccessTools.Field(declaringType, "s_creatureTypeMask");
		FieldInfo fieldInfo4 = AccessTools.Field(declaringType, "s_visited");
		HashSet<int> hashSet = ((fieldInfo2 == null) ? null : (fieldInfo2.GetValue(null) as HashSet<int>));
		HashSet<ZDOID> hashSet2 = ((fieldInfo4 == null) ? null : (fieldInfo4.GetValue(null) as HashSet<ZDOID>));
		string text2 = text;
		text = text2 + "; next scan=" + (Fgn.NextPass() - Time.realtimeSinceStartupAsDouble).ToString("F1", CultureInfo.InvariantCulture) + "s; prefabs scanned=" + ((fieldInfo == null) ? "unknown" : fieldInfo.GetValue(null).ToString()) + "; prefab=" + ((hashSet == null) ? "unknown" : hashSet.Contains(val.GetPrefab()).ToString()) + "; type=" + ((fieldInfo3 == null) ? "unknown" : (((int)fieldInfo3.GetValue(null) & (1 << (int)val.Type)) != 0).ToString()) + "; visited=" + ((hashSet2 == null) ? "unknown" : hashSet2.Contains(id).ToString());
		int num = 0;
		foreach (ZNetPeer candidate in Fgn.Candidates)
		{
			if (Candidate(candidate, val.GetPosition()))
			{
				num++;
			}
		}
		object obj = text;
		text = string.Concat(obj, "; FGN candidates=", Fgn.Candidates.Count, "; eligible near mob=", num);
		ZNetPeer peer = ZNet.instance.GetPeer(val.GetOwner());
		ZDO val2 = ((ZDOMan.instance == null || peer == null) ? null : ZDOMan.instance.GetZDO(peer.m_characterID));
		object obj2 = text;
		text = string.Concat(obj2, "; owner ready=", peer != null && peer.IsReady(), "; owner matched=", peer != null && Fgn.Matched(peer), "; owner in area=", Candidate(peer, val.GetPosition()), "; owner protocol=", (val2 == null) ? "missing player ZDO" : val2.GetLong(capabilityHash, 0L).ToString(), "; player ZDO owner=", (val2 == null) ? "missing" : val2.GetOwner().ToString());
		if (watching.TryGetValue(id, out var value))
		{
			object obj3 = text;
			text = string.Concat(obj3, "; watch owner=", value.Owner, "; watch revision=", value.Revision, "; acknowledged=", value.Acknowledged);
		}
		else
		{
			text += "; server watch=missing";
		}
		object obj4 = text;
		long peer2;
		double value2;
		return string.Concat(obj4, "; reported target peer=", (!TryTargetPeer(id, out peer2)) ? "missing/stale" : ((peer2 == 0) ? "none/non-player" : peer2.ToString()), "; transfer pending=", Fgn.Pending(id, Time.realtimeSinceStartupAsDouble), "; cooldown=", (Fgn.Cooldowns.TryGetValue(id, out value2) ? Math.Max(0.0, value2 - Time.realtimeSinceStartupAsDouble) : 0.0).ToString("F1", CultureInfo.InvariantCulture), "s");
	}

	private static void EnsureWorld()
	{
		if (!object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance))
		{
			Reset();
			world = ZDOMan.instance;
			network = ZNet.instance;
			if (world != null)
			{
				KeyValuePair<int, int> keyValuePair = targetHash;
				ZDOExtraData.AddSessionHash(keyValuePair.Key);
				KeyValuePair<int, int> keyValuePair2 = targetHash;
				ZDOExtraData.AddSessionHash(keyValuePair2.Value);
				ZDOExtraData.AddSessionHash(sourceHash);
				ZDOExtraData.AddSessionHash(revisionHash);
				ZDOExtraData.AddSessionHash(capabilityHash);
			}
		}
	}

	private static void Reset()
	{
		managed.Clear();
		forwarded.Clear();
		watching.Clear();
		receivedTargets.Clear();
		scratch.Clear();
		reports = new ConditionalWeakTable<BaseAI, Report>();
		FgnTargetEvents.Reset();
		FgnAttackRetry.Reset();
		collecting = false;
		FgnCombatStatus.Reset();
		serverConfirmed = false;
		localCapability = null;
		world = null;
		network = null;
		passMoves = 0;
		forwardExpiry = 0.0;
	}

	internal static void Stop()
	{
		bool enabled = Enabled;
		Enabled = false;
		if (enabled)
		{
			InstallationStatus = "stopped";
		}
		if (enabled)
		{
			NotifyServerOff();
		}
		try
		{
			RevokeCapability();
			ReleaseManaged();
		}
		catch (Exception ex)
		{
			if (Plugin.Diagnostics)
			{
				Plugin.Warn("Optional FGN AI cleanup: " + ex.Message);
			}
		}
		finally
		{
			Enabled = false;
			Reset();
		}
		if (harmony != null)
		{
			try
			{
				harmony.UnpatchSelf();
			}
			catch (Exception ex2)
			{
				if (Plugin.Diagnostics)
				{
					Plugin.Warn(ex2.Message);
				}
			}
		}
		harmony = null;
		Fgn = null;
		if (sessionHarmony != null)
		{
			try
			{
				sessionHarmony.UnpatchSelf();
			}
			catch (Exception ex3)
			{
				if (Plugin.Diagnostics)
				{
					Plugin.Warn(ex3.Message);
				}
			}
		}
		sessionHarmony = null;
	}

	private static void Fail(Exception error)
	{
		bool enabled = Enabled;
		Enabled = false;
		InstallationStatus = "runtime failure: " + error.GetBaseException().Message;
		if (enabled)
		{
			NotifyServerOff();
		}
		try
		{
			RevokeCapability();
			Enabled = false;
			ReleaseManaged();
		}
		catch (Exception ex)
		{
			if (Plugin.Diagnostics)
			{
				Plugin.Warn("Optional FGN AI cleanup: " + ex.Message);
			}
		}
		finally
		{
			Enabled = false;
			managed.Clear();
			forwarded.Clear();
			watching.Clear();
			receivedTargets.Clear();
			FgnTargetEvents.Reset();
			FgnAttackRetry.Reset();
			forwardExpiry = 0.0;
		}
		if (Plugin.Diagnostics)
		{
			Plugin.Warn("Optional FGN AI ownership disabled: " + error.Message);
		}
	}

	private static void NotifyServerOff()
	{
		try
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer == null || !peer.IsReady() || peer.m_rpc == null)
				{
					continue;
				}
				try
				{
					peer.m_rpc.Invoke("Botanic_AI_Mode", new object[1] { 3 });
				}
				catch (Exception ex)
				{
					if (Plugin.Diagnostics)
					{
						Plugin.Warn(ex.Message);
					}
				}
			}
		}
		catch (Exception ex2)
		{
			if (Plugin.Diagnostics)
			{
				Plugin.Warn(ex2.Message);
			}
		}
	}

	private static void ReleaseManaged()
	{
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated() && object.ReferenceEquals(world, ZDOMan.instance))
		{
			foreach (Lease value in managed.Values)
			{
				if (value.Data.IsValid() && value.Data.GetOwner() == ZDOMan.GetSessionID() && (Fgn == null || !Fgn.Possessed(value.Data)))
				{
					value.Data.SetOwner(0L);
					if (Fgn != null)
					{
						Fgn.ClearOwned(value.Data.m_uid);
					}
				}
			}
		}
		managed.Clear();
	}

	private static void SessionConnection(ZNetPeer __0)
	{
		if (__0 == null || __0.m_rpc == null)
		{
			return;
		}
		ZNetPeer peer = __0;
		FgnCombatStatus.Connect(peer);
		peer.m_rpc.Register<int>("Botanic_AI_Mode", (Action<ZRpc, int>)delegate(ZRpc rpc, int mode)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance))
				{
					if (ZNet.instance.IsDedicated())
					{
						if (mode == 1)
						{
							peer.m_rpc.Invoke("Botanic_AI_Mode", new object[1] { Active ? 5 : 3 });
						}
					}
					else if (peer.m_server && object.ReferenceEquals(ZNet.instance.GetServerPeer(), peer) && (mode == 2 || mode == 3 || mode == 4 || mode == 5))
					{
						EnsureWorld();
						serverConfirmed = mode == 5;
						if (!serverConfirmed)
						{
							RevokeCapability();
						}
						else
						{
							PublishCapability(ZNet.instance.LocalPlayerCharacterID);
						}
					}
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		});
		peer.m_rpc.Register<ZDOID, long, long>("Botanic_AI_Watch", (Action<ZRpc, ZDOID, long, long>)delegate(ZRpc rpc, ZDOID id, long owner, long revision)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ReceiveWatch(peer, id, owner, revision);
			}
			catch (Exception error)
			{
				Fail(error);
			}
		});
		peer.m_rpc.Register<ZDOID, ZDOID, long>("Botanic_AI_Target", (Action<ZRpc, ZDOID, ZDOID, long>)delegate(ZRpc rpc, ZDOID id, ZDOID target, long revision)
		{
			//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)
			try
			{
				ReceiveTarget(peer, id, target, revision);
			}
			catch (Exception error)
			{
				Fail(error);
			}
		});
		peer.m_rpc.Register<ZDOID, long>("Botanic_AI_AttackReady", (Action<ZRpc, ZDOID, long>)delegate(ZRpc rpc, ZDOID id, long revision)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ReceiveAttackReady(peer, id, revision);
			}
			catch (Exception error)
			{
				Fail(error);
			}
		});
	}

	private static void CapabilityPostfix(ZDOID __0)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled || ZDOMan.instance == null || ((ZDOID)(ref __0)).IsNone())
		{
			return;
		}
		try
		{
			EnsureWorld();
			if (!ZNet.instance.IsDedicated())
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null && serverPeer.IsReady())
				{
					serverPeer.m_rpc.Invoke("Botanic_AI_Mode", new object[1] { 1 });
				}
				if (serverConfirmed)
				{
					PublishCapability(__0);
				}
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static void PublishCapability(ZDOID id)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (Enabled && serverConfirmed && world != null)
		{
			ZDO zDO = world.GetZDO(id);
			if (zDO != null && zDO.IsValid() && zDO.GetOwner() == ZDOMan.GetSessionID())
			{
				zDO.Set(capabilityHash, 3L);
				localCapability = zDO;
			}
		}
	}

	private static void RevokeCapability()
	{
		if (localCapability != null && localCapability.IsValid() && object.ReferenceEquals(world, ZDOMan.instance) && localCapability.GetOwner() == ZDOMan.GetSessionID())
		{
			localCapability.Set(capabilityHash, 0L);
		}
		localCapability = null;
		serverConfirmed = false;
		if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated())
		{
			watching.Clear();
			receivedTargets.Clear();
			FgnTargetEvents.Reset();
			FgnAttackRetry.Reset();
		}
	}

	internal static bool ShouldOverride(ZDOID mob)
	{
		//IL_0028: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled || Fgn == null || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZNet.instance))
		{
			return false;
		}
		ZDO zDO = ZDOMan.instance.GetZDO(mob);
		if (zDO == null || !zDO.IsValid() || Fgn.Possessed(zDO))
		{
			return false;
		}
		if (ZNet.instance.IsDedicated() && zDO.GetOwner() == ZDOMan.GetSessionID() && managed.TryGetValue(mob, out var value) && object.ReferenceEquals(value.Data, zDO))
		{
			return true;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(zDO.GetOwner());
		if (!ZNet.instance.IsDedicated())
		{
			ZDOID target;
			if (serverConfirmed && Watched(zDO))
			{
				return ReadTarget(zDO, out target);
			}
			return false;
		}
		if (Participating(peer) && Watched(zDO) && watching.TryGetValue(mob, out var value2))
		{
			return value2.Acknowledged;
		}
		return false;
	}

	private static bool Participating(ZNetPeer peer)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		if (peer == null || !peer.IsReady() || !Fgn.Matched(peer) || ((ZDOID)(ref peer.m_characterID)).IsNone())
		{
			return false;
		}
		ZDO zDO = world.GetZDO(peer.m_characterID);
		if (zDO != null && zDO.IsValid() && zDO.GetOwner() == peer.m_uid)
		{
			return zDO.GetLong(capabilityHash, 0L) == 3;
		}
		return false;
	}

	private static void AiAwakePostfix(BaseAI __instance)
	{
		if (Watching(__instance))
		{
			FgnTargetEvents.Schedule(__instance);
		}
	}

	private static void PlayerDestroyPrefix(Player __instance)
	{
		if (!Enabled || watching.Count == 0 || object.ReferenceEquals(__instance, null) || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance))
		{
			return;
		}
		try
		{
			long sessionID = ZDOMan.GetSessionID();
			foreach (Watch value in watching.Values)
			{
				if (value.Owner == sessionID)
				{
					ZNetView val = ((value.Data == null) ? null : ZNetScene.instance.FindInstance(value.Data));
					BaseAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<BaseAI>() : null);
					if (Watching(val2) && object.ReferenceEquals(val2.GetTargetCreature(), __instance))
					{
						FgnTargetEvents.Schedule(val2);
					}
				}
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static bool Watched(ZDO data)
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		if (data != null && watching.TryGetValue(data.m_uid, out var value) && (value.Data == null || object.ReferenceEquals(value.Data, data)) && value.Owner == data.GetOwner())
		{
			return value.Revision == data.OwnerRevision;
		}
		return false;
	}

	internal static bool Watching(BaseAI ai)
	{
		if (!Enabled || !Object.op_Implicit((Object)(object)ai) || !((Behaviour)ai).isActiveAndEnabled || !Object.op_Implicit((Object)(object)ZNet.instance) || ZDOMan.instance == null || (!ZNet.instance.IsDedicated() && !serverConfirmed))
		{
			return false;
		}
		ZNetView val = aiView(ai);
		if (Object.op_Implicit((Object)(object)val) && val.IsValid() && val.IsOwner() && object.ReferenceEquals(world, ZDOMan.instance) && object.ReferenceEquals(network, ZNet.instance))
		{
			return Watched(val.GetZDO());
		}
		return false;
	}

	internal static void Observe(BaseAI ai)
	{
		//IL_008c: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: 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_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
		if (!Watching(ai))
		{
			return;
		}
		try
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || (!ZNet.instance.IsDedicated() && !serverConfirmed))
			{
				return;
			}
			ZNetView val = aiView(ai);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner())
			{
				return;
			}
			ZDO zDO = val.GetZDO();
			if (zDO.GetBool(ZDOVars.s_tamed, false) || zDO.GetLong(ZDOVars.s_user, 0L) != 0)
			{
				return;
			}
			EnsureWorld();
			Watch watch = watching[zDO.m_uid];
			if (watch.Data == null)
			{
				watch.Data = zDO;
			}
			Character targetCreature = ai.GetTargetCreature();
			ZDOID val2 = ((Object.op_Implicit((Object)(object)targetCreature) && targetCreature is Player && !targetCreature.IsDead()) ? targetCreature.GetZDOID() : ZDOID.None);
			Report orCreateValue = reports.GetOrCreateValue(ai);
			long owner = zDO.GetOwner();
			if (object.ReferenceEquals(orCreateValue.Data, zDO) && orCreateValue.Owner == owner && orCreateValue.Revision == zDO.OwnerRevision && orCreateValue.Target == val2)
			{
				return;
			}
			zDO.Set(targetHash, val2);
			zDO.Set(sourceHash, owner);
			zDO.Set(revisionHash, (long)zDO.OwnerRevision);
			orCreateValue.Data = zDO;
			orCreateValue.Owner = owner;
			orCreateValue.Revision = zDO.OwnerRevision;
			orCreateValue.Target = val2;
			if (ZNet.instance.IsDedicated())
			{
				watch.Acknowledged = true;
				receivedTargets[zDO.m_uid] = orCreateValue;
				ProcessTargetEvent(zDO, val2);
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null && serverPeer.IsReady())
			{
				serverPeer.m_rpc.Invoke("Botanic_AI_Target", new object[3]
				{
					zDO.m_uid,
					val2,
					(long)zDO.OwnerRevision
				});
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	internal static bool ReadTarget(ZDO data, out ZDOID target)
	{
		//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_0045: 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_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		target = ZDOID.None;
		if (data != null && data.IsValid() && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated() && object.ReferenceEquals(world, ZDOMan.instance) && receivedTargets.TryGetValue(data.m_uid, out var value) && object.ReferenceEquals(value.Data, data) && value.Owner == data.GetOwner() && value.Revision == data.OwnerRevision)
		{
			target = value.Target;
			return true;
		}
		if (data == null || !data.IsValid() || data.GetOwner() == 0 || data.GetLong(sourceHash, 0L) != data.GetOwner() || data.GetLong(revisionHash, -1L) != data.OwnerRevision)
		{
			return false;
		}
		target = data.GetZDOID(targetHash);
		return true;
	}

	internal static bool TryTargetPeer(ZDOID mob, out long peer)
	{
		//IL_0010: 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_0057: Unknown result type (might be due to invalid IL or missing references)
		peer = 0L;
		if (ZDOMan.instance == null || !ReadTarget(ZDOMan.instance.GetZDO(mob), out var target))
		{
			return false;
		}
		if (((ZDOID)(ref target)).IsNone())
		{
			return true;
		}
		foreach (ZNetPeer peer2 in ZNet.instance.GetPeers())
		{
			if (peer2 != null && peer2.IsReady() && peer2.m_characterID == target)
			{
				peer = peer2.m_uid;
				return true;
			}
		}
		return false;
	}

	private static void ReceiveWatch(ZNetPeer peer, ZDOID id, long owner, long revision)
	{
		//IL_0067: 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_0089: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
		if (Enabled && Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated() && serverConfirmed && peer.m_server && object.ReferenceEquals(ZNet.instance.GetServerPeer(), peer) && !((ZDOID)(ref id)).IsNone() && revision >= 0 && revision <= 65535)
		{
			EnsureWorld();
			if (owner == 0)
			{
				watching.Remove(id);
				receivedTargets.Remove(id);
			}
			else if (owner == ZDOMan.GetSessionID())
			{
				watching[id] = new Watch
				{
					Data = world.GetZDO(id),
					Owner = owner,
					Revision = (uint)revision
				};
				QueueBootstrap(world.GetZDO(id));
			}
		}
	}

	private static void ReceiveTarget(ZNetPeer peer, ZDOID id, ZDOID target, long revision)
	{
		//IL_0070: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || !ZNet.instance.IsDedicated() || world == null || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || revision < 0 || revision > 65535 || !Participating(peer) || !object.ReferenceEquals(network.GetPeer(peer.m_uid), peer))
		{
			return;
		}
		ZDO zDO = world.GetZDO(id);
		if (Eligible(zDO) && zDO.GetOwner() == peer.m_uid && zDO.OwnerRevision == revision && Watched(zDO) && watching.TryGetValue(id, out var value))
		{
			Report value2;
			bool flag = receivedTargets.TryGetValue(id, out value2) && object.ReferenceEquals(value2.Data, zDO) && value2.Owner == peer.m_uid && value2.Revision == revision;
			value.Acknowledged = true;
			if (!flag || !(value2.Target == target))
			{
				receivedTargets[id] = new Report
				{
					Data = zDO,
					Owner = peer.m_uid,
					Revision = (uint)revision,
					Target = target
				};
				ProcessTargetEvent(zDO, target);
			}
		}
	}

	private static void ProcessTargetEvent(ZDO data, ZDOID target)
	{
		if (!((ZDOID)(ref target)).IsNone() && Active && Watched(data))
		{
			Fgn.TryTransfer(data, Time.realtimeSinceStartupAsDouble);
		}
	}

	private static void RememberAttack(ZDO data, Character mob)
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		if (Enabled && Fgn != null && Object.op_Implicit((Object)(object)ZNet.instance) && ZDOMan.instance != null && (ZNet.instance.IsDedicated() || serverConfirmed) && object.ReferenceEquals(world, ZDOMan.instance) && object.ReferenceEquals(network, ZNet.instance) && Eligible(data) && data.GetOwner() == ZDOMan.GetSessionID() && ShouldOverride(data.m_uid) && ReadTarget(data, out var target) && !((ZDOID)(ref target)).IsNone())
		{
			if (!Object.op_Implicit((Object)(object)mob))
			{
				mob = LoadedCharacter(data);
			}
			if (Object.op_Implicit((Object)(object)mob))
			{
				FgnAttackRetry.Arm(mob, data);
			}
		}
	}

	private static void OnAttackEnded(Humanoid mob, ZDO data)
	{
		//IL_0060: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled || Fgn == null || !Object.op_Implicit((Object)(object)ZNet.instance) || !Eligible(data) || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || !object.ReferenceEquals(LoadedCharacter(data), mob) || data.GetOwner() != ZDOMan.GetSessionID() || !ShouldOverride(data.m_uid))
		{
			return;
		}
		if (((Character)mob).InAttack())
		{
			FgnAttackRetry.Arm((Character)(object)mob, data);
			return;
		}
		BaseAI component = ((Component)mob).GetComponent<BaseAI>();
		Player val = (Player)(Object.op_Implicit((Object)(object)component) ? /*isinst with value type is only supported in some contexts*/: null);
		if (!Object.op_Implicit((Object)(object)val) || ((Character)val).IsDead())
		{
			return;
		}
		if (ZNet.instance.IsDedicated())
		{
			if (Active)
			{
				CaptureLeaseTarget(data);
				Fgn.TryTransfer(data, Time.realtimeSinceStartupAsDouble);
			}
		}
		else
		{
			if (!serverConfirmed || !Watching(component))
			{
				return;
			}
			ZDOID zDOID = ((Character)val).GetZDOID();
			if (!ReadTarget(data, out var target) || target != zDOID)
			{
				Observe(component);
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null && serverPeer.IsReady())
			{
				serverPeer.m_rpc.Invoke("Botanic_AI_AttackReady", new object[2]
				{
					data.m_uid,
					(long)data.OwnerRevision
				});
			}
		}
	}

	private static void ReceiveAttackReady(ZNetPeer peer, ZDOID id, long revision)
	{
		//IL_0070: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || !ZNet.instance.IsDedicated() || world == null || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || revision < 0 || revision > 65535 || !Participating(peer) || !object.ReferenceEquals(network.GetPeer(peer.m_uid), peer))
		{
			return;
		}
		ZDO zDO = world.GetZDO(id);
		if (Eligible(zDO) && zDO.GetOwner() == peer.m_uid && zDO.OwnerRevision == revision && Watched(zDO) && watching.TryGetValue(id, out var value) && value.Acknowledged && ReadTarget(zDO, out var target) && !((ZDOID)(ref target)).IsNone())
		{
			if (Fgn.Pending(id, Time.realtimeSinceStartupAsDouble))
			{
				value.AttackReady = true;
			}
			else if (value.AttackRejected)
			{
				value.AttackReady = (value.AttackRejected = false);
				ProcessTargetEvent(zDO, target);
			}
		}
	}

	private static void QueueBootstrap(ZDO data)
	{
		ZNetView val = ((data != null && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.FindInstance(data) : null);
		BaseAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<BaseAI>() : null);
		if (Object.op_Implicit((Object)(object)val2))
		{
			reports.Remove(val2);
			FgnTargetEvents.Schedule(val2);
		}
	}

	private static bool TwoCandidates(ZDO data)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		Vector3 position = data.GetPosition();
		foreach (ZNetPeer candidate in Fgn.Candidates)
		{
			if (Candidate(candidate, position) && ++num >= 2)
			{
				return true;
			}
		}
		return false;
	}

	private static bool MarkWatch(ZDO data)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: 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_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: 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)
		long owner = data.GetOwner();
		bool flag = owner == ZDOMan.GetSessionID() && managed.ContainsKey(data.m_uid);
		ZNetPeer peer = ZNet.instance.GetPeer(owner);
		if (!Eligible(data) || (!flag && (!Participating(peer) || !Candidate(peer, data.GetPosition()))) || !TwoCandidates(data))
		{
			RemoveWatch(data.m_uid);
			return false;
		}
		if (!watching.TryGetValue(data.m_uid, out var value) || !object.ReferenceEquals(value.Data, data) || value.Owner != owner || value.Revision != data.OwnerRevision)
		{
			RemoveWatch(data.m_uid);
			Watch watch = new Watch();
			watch.Data = data;
			watch.Owner = owner;
			watch.Revision = data.OwnerRevision;
			value = watch;
			watching[data.m_uid] = value;
		}
		value.Pass = pass;
		if (!value.Acknowledged)
		{
			if (owner == ZDOMan.GetSessionID())
			{
				QueueBootstrap(data);
			}
			else
			{
				ZNetPeer peer2 = ZNet.instance.GetPeer(owner);
				peer2.m_rpc.Invoke("Botanic_AI_Watch", new object[3]
				{
					data.m_uid,
					owner,
					(long)data.OwnerRevision
				});
			}
		}
		return true;
	}

	private static void RemoveWatch(ZDOID id)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		if (!watching.TryGetValue(id, out var value))
		{
			return;
		}
		watching.Remove(id);
		if (!managed.ContainsKey(id) || value.Data == null || value.Data.GetOwner() != ZDOMan.GetSessionID())
		{
			receivedTargets.Remove(id);
		}
		if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated() && value.Owner != ZDOMan.GetSessionID())
		{
			ZNetPeer peer = ZNet.instance.GetPeer(value.Owner);
			if (peer != null && peer.IsReady() && peer.m_rpc != null)
			{
				peer.m_rpc.Invoke("Botanic_AI_Watch", new object[3]
				{
					id,
					0L,
					(long)value.Revision
				});
			}
		}
	}

	private static void BalancePrefix(out bool __state)
	{
		__state = false;
		if (!Enabled || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
		{
			return;
		}
		collecting = false;
		try
		{
			EnsureWorld();
			if (!Active)
			{
				ReleaseManaged();
				ClearWatches();
				PruneForwarded();
				return;
			}
			__state = Time.realtimeSinceStartupAsDouble >= Fgn.NextPass();
			if (__state)
			{
				passMoves = 0;
				pass++;
				Fgn.PerOwner.Clear();
				collecting = true;
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static void BalancePostfix(bool __state)
	{
		//IL_0030: 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_003b: 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_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: 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)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: 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_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: 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)
		collecting = false;
		if (!__state || !Active)
		{
			return;
		}
		try
		{
			scratch.Clear();
			foreach (ZDOID key in watching.Keys)
			{
				scratch.Add(key);
			}
			foreach (ZDOID item in scratch)
			{
				if (watching.TryGetValue(item, out var value) && value.Pass != pass)
				{
					ZDO zDO = world.GetZDO(item);
					if (object.ReferenceEquals(zDO, value.Data) && zDO.IsValid())
					{
						MarkWatch(zDO);
					}
					else
					{
						RemoveWatch(item);
					}
				}
			}
			scratch.Clear();
			PruneForwarded();
			scratch.Clear();
			foreach (ZDOID key2 in managed.Keys)
			{
				scratch.Add(key2);
			}
			foreach (ZDOID item2 in scratch)
			{
				if (!managed.TryGetValue(item2, out var value2))
				{
					continue;
				}
				ZDO zDO2 = world.GetZDO(item2);
				if (!object.ReferenceEquals(zDO2, value2.Data) || !Eligible(zDO2) || !Object.op_Implicit((Object)(object)LoadedCharacter(zDO2)))
				{
					managed.Remove(item2);
					if (!watching.ContainsKey(item2))
					{
						receivedTargets.Remove(item2);
					}
				}
				else if (zDO2.GetOwner() != ZDOMan.GetSessionID())
				{
					if (Time.realtimeSinceStartupAsDouble - value2.Requested >= 5.0)
					{
						managed.Remove(item2);
						if (!watching.ContainsKey(item2))
						{
							receivedTargets.Remove(item2);
						}
					}
				}
				else if (passMoves < 16 && value2.Pass != pass)
				{
					bool result = false;
					Handle(zDO2, Time.realtimeSinceStartupAsDouble, ref result);
					if (result)
					{
						passMoves++;
					}
				}
			}
			scratch.Clear();
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static void ClearWatches()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: 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_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: 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)
		if (watching.Count == 0 && receivedTargets.Count == 0)
		{
			return;
		}
		scratch.Clear();
		foreach (ZDOID key in watching.Keys)
		{
			scratch.Add(key);
		}
		foreach (ZDOID item in scratch)
		{
			RemoveWatch(item);
		}
		scratch.Clear();
		receivedTargets.Clear();
		FgnTargetEvents.Reset();
		FgnAttackRetry.Reset();
	}

	private static void CaptureLeaseTarget(ZDO data)
	{
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: 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_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: 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)
		Character val = LoadedCharacter(data);
		BaseAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<BaseAI>() : null);
		ZNetView val3 = (Object.op_Implicit((Object)(object)val2) ? aiView(val2) : null);
		if (Object.op_Implicit((Object)(object)val3) && val3.IsOwner() && object.ReferenceEquals(val3.GetZDO(), data))
		{
			Character targetCreature = val2.GetTargetCreature();
			ZDOID target = ((Object.op_Implicit((Object)(object)targetCreature) && targetCreature is Player && !targetCreature.IsDead()) ? targetCreature.GetZDOID() : ZDOID.None);
			if (!receivedTargets.TryGetValue(data.m_uid, out var value))
			{
				value = (receivedTargets[data.m_uid] = new Report());
			}
			value.Data = data;
			value.Owner = data.GetOwner();
			value.Revision = data.OwnerRevision;
			value.Target = target;
		}
	}

	private static void PruneForwarded()
	{
		//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_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
		if (forwarded.Count == 0 || realtimeSinceStartupAsDouble < forwardExpiry)
		{
			return;
		}
		scratch.Clear();
		foreach (ZDOID key in forwarded.Keys)
		{
			scratch.Add(key);
		}
		forwardExpiry = double.MaxValue;
		foreach (ZDOID item in scratch)
		{
			Forward forward = forwarded[item];
			if (realtimeSinceStartupAsDouble >= forward.Until || !object.ReferenceEquals(world.GetZDO(item), forward.Data) || forward.Data.GetOwner() != forward.Owner)
			{
				forwarded.Remove(item);
			}
			else
			{
				forwardExpiry = Math.Min(forwardExpiry, forward.Until);
			}
		}
		scratch.Clear();
	}

	private static bool TryPrefix(ZDO __0, double __1, ref bool __result)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		if (!Active)
		{
			return true;
		}
		try
		{
			if (__0 == null)
			{
				return true;
			}
			if (collecting && __0 != null && __0.IsValid())
			{
				MarkWatch(__0);
			}
			if (watching.TryGetValue(__0.m_uid, out var value) && Watched(__0) && !value.Acknowledged)
			{
				__result = false;
				return false;
			}
			if (passMoves >= 16)
			{
				__result = false;
				return false;
			}
			return Handle(__0, __1, ref __result);
		}
		catch (Exception error)
		{
			Fail(error);
			return true;
		}
	}

	private static void TryPostfix(bool __result)
	{
		if (Enabled && __result)
		{
			passMoves++;
		}
	}

	private static bool Handle(ZDO data, double now, ref bool result)
	{
		//IL_0057: 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_0025: 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)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0206: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: 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_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0266: Unknown result type (might be due to invalid IL or missing references)
		result = false;
		if (!Eligible(data))
		{
			if (data != null)
			{
				managed.Remove(data.m_uid);
				if (!watching.ContainsKey(data.m_uid))
				{
					receivedTargets.Remove(data.m_uid);
				}
			}
			return false;
		}
		long owner = data.GetOwner();
		long sessionID = ZDOMan.GetSessionID();
		Lease value;
		bool flag = managed.TryGetValue(data.m_uid, out value) && object.ReferenceEquals(value.Data, data);
		if (flag)
		{
			value.Pass = pass;
		}
		if (owner == sessionID && !flag)
		{
			return true;
		}
		if (owner == sessionID && flag && !Watched(data))
		{
			CaptureLeaseTarget(data);
		}
		if (owner != sessionID && !Candidate(ZNet.instance.GetPeer(owner), data.GetPosition()))
		{
			return true;
		}
		if (!ShouldOverride(data.m_uid))
		{
			return true;
		}
		if (!TryTargetPeer(data.m_uid, out var peer))
		{
			return false;
		}
		ZNetPeer val = ((peer == 0) ? null : ZNet.instance.GetPeer(peer));
		if (val != null && !Candidate(val, data.GetPosition()))
		{
			if (owner == sessionID)
			{
				ZNetPeer val2 = BestCandidate(data.GetPosition());
				if (val2 != null)
				{
					result = Request(data, val2.m_uid, now);
				}
				else
				{
					managed.Remove(data.m_uid);
					receivedTargets.Remove(data.m_uid);
					data.SetOwner(0L);
					Fgn.ClearOwned(data.m_uid);
				}
			}
			return false;
		}
		if (val == null)
		{
			if (owner != sessionID)
			{
				return true;
			}
			ZNetPeer val3 = ZNet.instance.GetPeer(value.ReturnPeer);
			if (!Candidate(val3, data.GetPosition()))
			{
				val3 = BestCandidate(data.GetPosition());
			}
			if (val3 != null)
			{
				result = Request(data, val3.m_uid, now);
			}
			else
			{
				managed.Remove(data.m_uid);
				receivedTargets.Remove(data.m_uid);
				data.SetOwner(0L);
				Fgn.ClearOwned(data.m_uid);
			}
			return false;
		}
		bool flag2 = HighPing(val, data.GetPosition());
		bool flag3 = flag2 && OtherPlayerNearby(val, data);
		if (flag2 && flag3 && Fgn.Simulation())
		{
			if (owner == sessionID)
			{
				return false;
			}
			if (!ServerReady(data))
			{
				return false;
			}
			result = Request(data, sessionID, now);
			if (result)
			{
				managed[data.m_uid] = new Lease
				{
					Data = data,
					ReturnPeer = val.m_uid,
					SourceOwner = owner,
					SourceRevision = data.OwnerRevision,
					Requested = now,
					Pass = pass
				};
			}
			return false;
		}
		if (owner == sessionID || flag2)
		{
			if (owner != val.m_uid)
			{
				result = Request(data, val.m_uid, now);
			}
			return false;
		}
		return true;
	}

	private static bool HighPing(ZNetPeer target, Vector3 position)
	{
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		if (!Fgn.TryMeasuredRtt(target, out var value))
		{
			return false;
		}
		if (maximumPing.Value > 0)
		{
			return value > (float)maximumPing.Value;
		}
		float num = float.MaxValue;
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer != target && Candidate(peer, position) && Fgn.TryMeasuredRtt(peer, out var value2))
			{
				num = Math.Min(num, value2);
			}
		}
		if (num < float.MaxValue)
		{
			return value > num + 250f;
		}
		return false;
	}

	private static bool OtherPlayerNearby(ZNetPeer target, ZDO data)
	{
		//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_0008: 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_0034: 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_0055: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: 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)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: 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)
		Vector3 position = data.GetPosition();
		Vector2s sector = data.GetSector();
		foreach (ZNetPeer candidate in Fgn.Candidates)
		{
			if (candidate == target || !Candidate(candidate, position) || ((ZDOID)(ref candidate.m_characterID)).IsNone())
			{
				continue;
			}
			Vector2s zone = ZoneSystem.GetZone(candidate.GetRefPos());
			if (Math.Abs(sector.x - zone.x) > 1 || Math.Abs(sector.y - zone.y) > 1)
			{
				continue;
			}
			ZDO zDO = world.GetZDO(candidate.m_characterID);
			if (zDO == null || !zDO.IsValid() || zDO.GetOwner() != candidate.m_uid || zDO.GetFloat(ZDOVars.s_health, 1f) <= 0f)
			{
				continue;
			}
			Vector3 position2 = zDO.GetPosition();
			if (Finite(position2))
			{
				Vector3 val = position2 - position;
				if (((Vector3)(ref val)).sqrMagnitude <= nearbyRadius.Value * nearbyRadius.Value)
				{
					return true;
				}
			}
		}
		return false;
	}

	private static bool Candidate(ZNetPeer peer, Vector3 position)
	{
		//IL_000c: 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)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: 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)
		if (peer != null && peer.IsReady() && Finite(peer.GetRefPos()) && peer.GetRefPos() != Vector3.zero && Fgn.Matched(peer))
		{
			return ZNetScene.InActiveArea(position, peer.GetRefPos());
		}
		return false;
	}

	private static ZNetPeer BestCandidate(Vector3 position)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		ZNetPeer result = null;
		float num = float.MaxValue;
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (Candidate(peer, position) && Fgn.Rtt(peer) < num)
			{
				result = peer;
				num = Fgn.Rtt(peer);
			}
		}
		return result;
	}

	private static bool Eligible(ZDO data)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if (data != null && data.IsValid() && !((ZDOID)(ref data.m_uid)).IsNone() && data.GetOwner() != 0 && Finite(data.GetPosition()) && !data.GetBool(ZDOVars.s_tamed, false) && data.GetLong(ZDOVars.s_user, 0L) == 0 && !Fgn.Held(data))
		{
			return !Fgn.Possessed(data);
		}
		return false;
	}

	private static Character LoadedCharacter(ZDO data)
	{
		ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null);
		if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || val.m_distant || !((Component)val).gameObject.activeInHierarchy || (Object)(object)((Component)val).transform.parent != (Object)null || !object.ReferenceEquals(val.GetZDO(), data))
		{
			return null;
		}
		Character component = ((Component)val).GetComponent<Character>();
		if (!Object.op_Implicit((Object)(object)component) || component is Player || !((Behaviour)component).isActiveAndEnabled || component.IsDead() || component.IsBoss() || component.IsTamed() || component.IsAttached() || component.IsRiding())
		{
			return null;
		}
		BaseAI component2 = ((Component)val).GetComponent<BaseAI>();
		ZSyncTransform component3 = ((Component)val).GetComponent<ZSyncTransform>();
		if (!Object.op_Implicit((Object)(object)component2) || !((Behaviour)component2).isActiveAndEnabled || component2.HaveRider() || !Object.op_Implicit((Object)(object)component3) || !((Behaviour)component3).isActiveAndEnabled || !component3.m_syncPosition)
		{
			return null;
		}
		return component;
	}

	private static bool ServerReady(ZDO data)
	{
		//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_002c: 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_0048: 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_0050: 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_0079: 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)
		Character val = LoadedCharacter(data);
		NativePup native = Plugin.Native;
		Vector3 position = data.GetPosition();
		if (Object.op_Implicit((Object)(object)val) && native != null && native.Active && ZNetScene.instance.IsAreaReady(position) && native.ReadyPoint(position, 16f))
		{
			Vector3 position2 = ((Component)val).transform.position;
			if ((((Vector3)(ref position2)).Equals(position) || native.ReadyPoint(((Component)val).transform.position, 16f)) && data.IsValid() && data.GetPosition() == position)
			{
				return Eligible(data);
			}
		}
		return false;
	}

	private static bool Finite(Vector3 point)
	{
		if (!float.IsNaN(point.x) && !float.IsNaN(point.y) && !float.IsNaN(point.z) && !float.IsInfinity(point.x) && !float.IsInfinity(point.y))
		{
			return !float.IsInfinity(point.z);
		}
		return false;
	}

	private static bool Request(ZDO data, long target, double now)
	{
		//IL_002a: 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_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f1: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: 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)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		long owner = data.GetOwner();
		long sessionID = ZDOMan.GetSessionID();
		if (target == 0 || target == owner || passMoves >= 16 || Fgn.Pending(data.m_uid, now) || (Fgn.Cooldowns.TryGetValue(data.m_uid, out var value) && now < value))
		{
			return false;
		}
		Fgn.PerOwner.TryGetValue(owner, out var value2);
		if (value2 >= 6)
		{
			return false;
		}
		if (owner == sessionID)
		{
			Character val = LoadedCharacter(data);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			if (val.InAttack())
			{
				RememberAttack(data, val);
				return false;
			}
			if (val.IsStaggering() || !ServerReady(data) || !Candidate(ZNet.instance.GetPeer(target), data.GetPosition()))
			{
				return false;
			}
			world.ForceSendZDO(data.m_uid);
			data.SetOwner(target);
			managed.Remove(data.m_uid);
			receivedTargets.Remove(data.m_uid);
			Fgn.ClearOwned(data.m_uid);
			forwarded[data.m_uid] = new Forward
			{
				Data = data,
				Owner = target,
				Until = now + 5.0
			};
			if (forwarded.Count == 1 || forwardExpiry > now + 5.0)
			{
				forwardExpiry = now + 5.0;
			}
		}
		else
		{
			ZNetPeer peer = ZNet.instance.GetPeer(owner);
			if (!Candidate(peer, data.GetPosition()))
			{
				return false;
			}
			peer.m_rpc.Invoke("FGN_HandOff", new object[2] { data.m_uid, target });
		}
		Fgn.PerOwner[owner] = value2 + 1;
		Fgn.MarkPending(data.m_uid, target, now);
		Fgn.ClearStreak(data.m_uid);
		return true;
	}

	private static void NativeSetOwner(ZDO data, long owner)
	{
		//IL_0047: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled || managed.Count == 0 || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || !managed.TryGetValue(data.m_uid, out var value) || !object.ReferenceEquals(value.Data, data) || !Active || !Eligible(data) || (data.GetOwner() != ZDOMan.GetSessionID() && (data.GetOwner() != value.SourceOwner || data.OwnerRevision != value.SourceRevision || !(Time.realtimeSinceStartupAsDouble - value.Requested < 5.0) || !Fgn.Pending(data.m_uid, Time.realtimeSinceStartupAsDouble) || !Candidate(ZNet.instance.GetPeer(value.SourceOwner), data.GetPosition()) || !ShouldOverride(data.m_uid))))
		{
			data.SetOwner(owner);
		}
	}

	private static bool LocalHit(Dictionary<ZDOID, double> hits, ZDOID id, out double value)
	{
		//IL_0000: 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_000d: Unknown result type (might be due to invalid IL or missing references)
		if (ShouldOverride(id) && ReadTarget(ZDOMan.instance.GetZDO(id), out var _))
		{
			value = 0.0;
			return false;
		}
		return hits.TryGetValue(id, out value);
	}

	private static void ResultPrefix(ZRpc __0, ZDOID __1, bool __2, string __3, out double __state)
	{
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		__state = 0.0;
		if (__2 || __3 != "attacking" || __0 == null || !Enabled || Fgn == null || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance))
		{
			return;
		}
		try
		{
			if (!Active)
			{
				return;
			}
			ZDO zDO = world.GetZDO(__1);
			if (!Eligible(zDO) || !ShouldOverride(__1) || !ReadTarget(zDO, out var target) || ((ZDOID)(ref target)).IsNone())
			{
				return;
			}
			ZNetPeer peer = network.GetPeer(zDO.GetOwner());
			if (peer != null && object.ReferenceEquals(peer.m_rpc, __0))
			{
				double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
				if (Fgn.Pending(__1, realtimeSinceStartupAsDouble))
				{
					__state = realtimeSinceStartupAsDouble + 6.0;
				}
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static void ResultPostfix(ZDOID __1, double __state)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: 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_009d: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
		if (__state <= 0.0 || !Enabled || Fgn == null || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance))
		{
			return;
		}
		double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
		if (Fgn.Cooldowns.TryGetValue(__1, out var value) && value >= __state && value <= realtimeSinceStartupAsDouble + 6.0)
		{
			Fgn.Cooldowns.Remove(__1);
		}
		try
		{
			ZDO zDO = world.GetZDO(__1);
			if (Watched(zDO) && watching.TryGetValue(__1, out var value2) && ShouldOverride(__1) && ReadTarget(zDO, out var target) && !((ZDOID)(ref target)).IsNone())
			{
				value2.AttackRejected = true;
				if (value2.AttackReady)
				{
					value2.AttackReady = (value2.AttackRejected = false);
					ProcessTargetEvent(zDO, target);
				}
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static void VetoPostfix(ZDO __0, long __1, ref string __result)
	{
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		if (__result == "attacking")
		{
			try
			{
				RememberAttack(__0, null);
				return;
			}
			catch (Exception error)
			{
				Fail(error);
				return;
			}
		}
		if (__result != null || !Enabled || !Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsDedicated() || !serverConfirmed || !object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || __0 == null || !Watched(__0) || __0.GetOwner() != ZDOMan.GetSessionID())
		{
			return;
		}
		try
		{
			if (!Eligible(__0))
			{
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null && serverPeer.m_server && serverPeer.IsReady() && __1 == serverPeer.m_uid)
			{
				return;
			}
			ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(__0) : null);
			BaseAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<BaseAI>() : null);
			if (!Watching(val2))
			{
				return;
			}
			Character targetCreature = val2.GetTargetCreature();
			Player val3 = (Player)(object)((targetCreature is Player) ? targetCreature : null);
			if (!Object.op_Implicit((Object)(object)val3) || ((Character)val3).IsDead())
			{
				return;
			}
			ZNetView component = ((Component)val3).GetComponent<ZNetView>();
			ZDO val4 = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO() : null);
			if (val4 != null && val4.IsValid() && object.ReferenceEquals(world.GetZDO(val4.m_uid), val4))
			{
				long owner = val4.GetOwner();
				if (owner != 0 && __1 != owner)
				{
					__result = "AI target changed";
				}
			}
		}
		catch (Exception error2)
		{
			Fail(error2);
		}
	}

	private static bool DamagePrefix(Character __instance, HitData hit)
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		if (!Enabled || forwarded.Count == 0 || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || !Object.op_Implicit((Object)(object)__instance) || hit == null)
		{
			return true;
		}
		try
		{
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.