Decompiled source of Follower Commands v1.0.1

FollowerAdvance.dll

Decompiled a day ago
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("1.0.1.0")]
namespace Fai.FollowerCommands;

[BepInPlugin("fai.FollowerAdvance", "Follower Advance", "1.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class FollowerAdvancePlugin : BaseUnityPlugin
{
	public const string PluginGuid = "fai.FollowerAdvance";

	private static FollowerAdvancePlugin instance;

	private ConfigEntry<KeyboardShortcut> advanceKey;

	private Harmony harmony;

	private static readonly MethodInfo MoveMethod = typeof(BaseAI).GetMethod("MoveTowards", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
	{
		typeof(Vector3),
		typeof(bool)
	}, null);

	private static readonly MethodInfo WakeMethod = typeof(MonsterAI).GetMethod("Wakeup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private void Awake()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Expected O, but got Unknown
		instance = this;
		CommandState.Reset();
		advanceKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "AdvanceKey", new KeyboardShortcut((KeyCode)103, Array.Empty<KeyCode>()), "Advance tamed creatures following you for 3 seconds.");
		if (MoveMethod == null || WakeMethod == null)
		{
			throw new MissingMethodException("Valheim advance movement API was not found.");
		}
		harmony = new Harmony("fai.followercommands.advance");
		harmony.PatchAll(typeof(FollowerAdvancePlugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Follower Advance 1.0.1 loaded (G or LT+X; 3 seconds).");
	}

	private void OnDestroy()
	{
		CommandState.Reset();
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if ((Object)(object)instance == (Object)(object)this)
		{
			instance = null;
		}
	}

	private void Update()
	{
		//IL_0054: 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)
		bool flag = NativeGamepad.ConsumeAdvance();
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting())
		{
			CommandState.Reset();
			return;
		}
		KeyboardShortcut value = advanceKey.Value;
		if ((!((KeyboardShortcut)(ref value)).IsDown() && !flag) || !NativeGamepad.CanTakeInput(localPlayer) || !CommandState.BeginAdvance(Time.frameCount, Time.unscaledTime))
		{
			return;
		}
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if ((Object)(object)allCharacter == (Object)null || allCharacter.IsDead() || !allCharacter.IsTamed())
			{
				continue;
			}
			MonsterAI component = ((Component)allCharacter).GetComponent<MonsterAI>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.GetFollowTarget() != (Object)(object)((Component)localPlayer).gameObject)
			{
				continue;
			}
			Tameable component2 = ((Component)allCharacter).GetComponent<Tameable>();
			if ((Object)(object)component2 != (Object)null && component2.HaveRider())
			{
				continue;
			}
			ZNetView component3 = ((Component)allCharacter).GetComponent<ZNetView>();
			if (!((Object)(object)component3 == (Object)null) && component3.IsValid())
			{
				try
				{
					component3.ClaimOwnership();
					WakeMethod.Invoke(component, null);
					ApplyAdvance(component, Time.unscaledTime);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("Advance command failed for " + ((Object)allCharacter).name + ": " + ex.GetBaseException()));
				}
			}
		}
	}

	internal static void ApplyAdvance(MonsterAI ai, float now)
	{
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		if (!CommandState.IsAdvanceActive(now) || (Object)(object)ai == (Object)null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || (Object)(object)ai.GetFollowTarget() != (Object)(object)((Component)localPlayer).gameObject)
		{
			return;
		}
		Character component = ((Component)ai).GetComponent<Character>();
		ZNetView component2 = ((Component)ai).GetComponent<ZNetView>();
		if (!((Object)(object)component == (Object)null) && !component.IsDead() && component.IsTamed() && !((Object)(object)component2 == (Object)null) && component2.IsValid() && component2.IsOwner())
		{
			Tameable component3 = ((Component)component).GetComponent<Tameable>();
			if ((!((Object)(object)component3 != (Object)null) || !component3.HaveRider()) && !((Object)(object)((BaseAI)ai).GetTargetCreature() != (Object)null) && !((Object)(object)ai.GetStaticTarget() != (Object)null))
			{
				Vector3 lookDir = ((Character)localPlayer).GetLookDir();
				MoveMethod.Invoke(ai, new object[2] { lookDir, true });
			}
		}
	}

	internal static void AfterAiUpdate(MonsterAI ai, bool updated)
	{
		if (!updated || !CommandState.IsAdvanceActive(Time.unscaledTime))
		{
			return;
		}
		try
		{
			ApplyAdvance(ai, Time.unscaledTime);
		}
		catch (Exception ex)
		{
			CommandState.Reset();
			if ((Object)(object)instance != (Object)null)
			{
				((BaseUnityPlugin)instance).Logger.LogError((object)("Advance AI update failed: " + ex.GetBaseException()));
			}
		}
	}
}
[HarmonyPatch(typeof(MonsterAI), "UpdateAI", new Type[] { typeof(float) })]
internal static class AdvanceAiPatch
{
	private static void Postfix(MonsterAI __instance, bool __result)
	{
		FollowerAdvancePlugin.AfterAiUpdate(__instance, __result);
	}
}

FollowerRecall.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("1.0.1.0")]
namespace Fai.FollowerCommands;

public static class CommandState
{
	private static float advanceUntil;

	private static int recallFrame = -1;

	public static bool BeginAdvance(int frame, float now)
	{
		if (frame == recallFrame)
		{
			return false;
		}
		advanceUntil = now + 3f;
		return true;
	}

	public static bool IsAdvanceActive(float now)
	{
		return advanceUntil > 0f && now < advanceUntil;
	}

	public static void Recall(int frame)
	{
		advanceUntil = 0f;
		recallFrame = frame;
	}

	public static void Reset()
	{
		advanceUntil = 0f;
		recallFrame = -1;
	}
}
[BepInPlugin("fai.FollowerRecall", "Follower Recall", "1.0.1")]
public sealed class FollowerRecallPlugin : BaseUnityPlugin
{
	private sealed class Recall
	{
		internal ZDOID Id;

		internal ZDOID Player;

		internal Vector3 Destination;

		internal float Expires;

		internal bool Prepared;

		internal bool Applied;
	}

	internal sealed class RecallRequest
	{
		internal ZDOID Player;

		internal Vector3 Origin;

		internal List<Vector3> Points;

		internal HashSet<ZDOID> Loaded;
	}

	public const string PluginGuid = "fai.FollowerRecall";

	internal const string RequestRpc = "Fai_FollowerCommands_RecallRequest_v100";

	internal const string ApplyRpc = "Fai_FollowerCommands_RecallApply_v100";

	internal const string ResultRpc = "Fai_FollowerCommands_RecallResult_v100";

	internal const float Radius = 3f;

	internal static FollowerRecallPlugin Instance;

	private ConfigEntry<KeyboardShortcut> shortcut;

	private Harmony harmony;

	private ZRoutedRpc registeredRpc;

	private readonly Dictionary<ZDOID, Recall> recalls = new Dictionary<ZDOID, Recall>();

	private readonly Dictionary<long, float> lastRequest = new Dictionary<long, float>();

	private float awaitingSince = -1f;

	private float nextPress;

	private static readonly FieldInfo ObjectsField = FindField(typeof(ZDOMan), "m_objectsByID");

	private static readonly FieldInfo TargetCreatureField = FindField(typeof(MonsterAI), "m_targetCreature");

	private static readonly MethodInfo AlertMethod = FindMethod(typeof(MonsterAI), "SetAlerted");

	private static readonly MethodInfo GroundMethod = FindMethod(typeof(Character), "ResetGroundContact");

	private static readonly FieldInfo TargetStaticField = FindField(typeof(MonsterAI), "m_targetStatic");

	private static readonly Dictionary<string, FieldInfo> CharacterFields = new string[6] { "m_maxAirAltitude", "m_lastGroundTouch", "m_fallTimer", "m_pushForce", "m_rootMotion", "m_moveDir" }.ToDictionary((string n) => n, (string n) => FindField(typeof(Character), n));

	private static readonly Dictionary<string, FieldInfo> SyncFields = new string[5] { "m_targetPosTimer", "m_haveTempRelPos", "m_positionCached", "m_velocityCached", "m_lastLocalVelocity" }.ToDictionary((string n) => n, (string n) => FindField(typeof(ZSyncTransform), n));

	private static FieldInfo FindField(Type type, string name)
	{
		Type type2 = type;
		while (type != null)
		{
			FieldInfo field = type.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field;
			}
			type = type.BaseType;
		}
		throw new MissingFieldException(type2.FullName, name);
	}

	private static MethodInfo FindMethod(Type type, string name)
	{
		Type type2 = type;
		while (type != null)
		{
			MethodInfo method = type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (method != null)
			{
				return method;
			}
			type = type.BaseType;
		}
		throw new MissingMethodException(type2.FullName, name);
	}

	internal static ZPackage EncodeRequest(ZDOID player, Vector3 origin, List<Vector3> points, List<ZDOID> loaded)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: 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_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		ZPackage val = new ZPackage();
		val.Write(player);
		val.Write(origin);
		val.Write(points.Count);
		foreach (Vector3 point in points)
		{
			val.Write(point);
		}
		val.Write(loaded.Count);
		foreach (ZDOID item in loaded)
		{
			val.Write(item);
		}
		return val;
	}

	internal static RecallRequest DecodeRequest(ZPackage package)
	{
		//IL_0020: 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_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
		if (package == null)
		{
			throw new ArgumentNullException("package");
		}
		package.SetPos(0);
		RecallRequest recallRequest = new RecallRequest();
		recallRequest.Player = package.ReadZDOID();
		recallRequest.Origin = package.ReadVector3();
		int num = package.ReadInt();
		if (num < 1 || num > 64)
		{
			throw new ArgumentException("Invalid destination count.");
		}
		recallRequest.Points = new List<Vector3>(num);
		for (int i = 0; i < num; i++)
		{
			Vector3 val = package.ReadVector3();
			if (!WithinRadius(recallRequest.Origin, val))
			{
				throw new ArgumentException("Destination exceeds 3 metres.");
			}
			recallRequest.Points.Add(val);
		}
		int num2 = package.ReadInt();
		if (num2 < 0 || num2 > 100000 || num2 > (package.Size() - package.GetPos()) / 12)
		{
			throw new ArgumentException("Invalid loaded follower count.");
		}
		recallRequest.Loaded = new HashSet<ZDOID>();
		for (int j = 0; j < num2; j++)
		{
			recallRequest.Loaded.Add(package.ReadZDOID());
		}
		return recallRequest;
	}

	private void Awake()
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Expected O, but got Unknown
		Instance = this;
		shortcut = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "RecallShortcut", new KeyboardShortcut((KeyCode)104, Array.Empty<KeyCode>()), "Teleport every tamed creature following you into a 3 metre radius. No source-distance limit. Host/server must also install this version.");
		harmony = new Harmony("fai.followercommands.recall");
		harmony.PatchAll(typeof(FollowerRecallPlugin).Assembly);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Follower Recall 1.0.1 loaded (H or LT+A; 3m; no on-screen recall notifications).");
	}

	private void OnDestroy()
	{
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		recalls.Clear();
		NativeGamepad.Reset();
		CommandState.Reset();
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void Update()
	{
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: 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_0243: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
		bool flag = NativeGamepad.ConsumeRecall();
		RegisterRpcs();
		if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			recalls.Clear();
			lastRequest.Clear();
			awaitingSince = -1f;
			CommandState.Reset();
			return;
		}
		Recall[] array = recalls.Values.ToArray();
		foreach (Recall recall in array)
		{
			if (Time.unscaledTime >= recall.Expires)
			{
				recalls.Remove(recall.Id);
			}
			else
			{
				if (recall.Applied)
				{
					continue;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(recall.Id);
				if (zDO == null)
				{
					continue;
				}
				try
				{
					PrepareRecall(zDO, recall);
					GameObject val = ZNetScene.instance.FindInstance(recall.Id);
					if (!((Object)(object)val == (Object)null))
					{
						Character component = val.GetComponent<Character>();
						if ((Object)(object)component == (Object)null || component.IsDead())
						{
							recall.Applied = true;
						}
						else
						{
							ApplyLoadedRecall(component, recall);
						}
					}
				}
				catch (Exception ex)
				{
					recall.Applied = true;
					((BaseUnityPlugin)this).Logger.LogError((object)string.Concat("Recall synchronization failed for ", recall.Id, ": ", ex.GetBaseException()));
				}
			}
		}
		Player localPlayer = Player.m_localPlayer;
		if (awaitingSince >= 0f && Time.unscaledTime - awaitingSince > 8f)
		{
			awaitingSince = -1f;
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Recall request timed out. Verify that the host has Follower Commands installed.");
		}
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		KeyboardShortcut value = shortcut.Value;
		if ((!((KeyboardShortcut)(ref value)).IsDown() && !flag) || Time.unscaledTime < nextPress || !NativeGamepad.CanTakeInput(localPlayer))
		{
			return;
		}
		nextPress = Time.unscaledTime + 0.2f;
		CommandState.Recall(Time.frameCount);
		List<Vector3> points = BuildLandingPoints(((Component)localPlayer).transform.position);
		List<ZDOID> list = new List<ZDOID>();
		foreach (Character allCharacter in Character.GetAllCharacters())
		{
			if (!((Object)(object)allCharacter == (Object)null) && allCharacter.IsTamed() && !allCharacter.IsDead())
			{
				MonsterAI component2 = ((Component)allCharacter).GetComponent<MonsterAI>();
				if ((Object)(object)component2 != (Object)null && (Object)(object)component2.GetFollowTarget() == (Object)(object)((Component)localPlayer).gameObject)
				{
					list.Add(allCharacter.GetZDOID());
				}
			}
		}
		ZPackage val2 = EncodeRequest(((Character)localPlayer).GetZDOID(), ((Component)localPlayer).transform.position, points, list);
		if (ZNet.instance.IsServer())
		{
			HandleRequest(ZNet.GetUID(), val2);
			return;
		}
		awaitingSince = Time.unscaledTime;
		ZRoutedRpc.instance.InvokeRoutedRPC(GetServerId(), "Fai_FollowerCommands_RecallRequest_v100", new object[1] { val2 });
	}

	private void RegisterRpcs()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null && !object.ReferenceEquals(instance, registeredRpc))
		{
			registeredRpc = instance;
			instance.Register<ZPackage>("Fai_FollowerCommands_RecallRequest_v100", (Action<long, ZPackage>)HandleRequest);
			instance.Register<ZPackage>("Fai_FollowerCommands_RecallApply_v100", (Action<long, ZPackage>)HandleApply);
			instance.Register<int>("Fai_FollowerCommands_RecallResult_v100", (Action<long, int>)HandleResult);
		}
	}

	internal static bool IsFinite(Vector3 point)
	{
		return !float.IsNaN(point.x) && !float.IsNaN(point.y) && !float.IsNaN(point.z) && !float.IsInfinity(point.x) && !float.IsInfinity(point.y) && !float.IsInfinity(point.z);
	}

	internal static bool IsSavedFollower(ZDO zdo, string playerName)
	{
		return zdo != null && zdo.GetBool(ZDOVars.s_tamed, false) && !zdo.GetBool(ZDOVars.s_dead, false) && zdo.GetFloat(ZDOVars.s_health, 1f) > 0f && !string.IsNullOrEmpty(playerName) && string.Equals(zdo.GetString(ZDOVars.s_follow, string.Empty), playerName, StringComparison.Ordinal);
	}

	internal static bool WithinRadius(Vector3 origin, Vector3 point)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		int result;
		if (IsFinite(origin) && IsFinite(point))
		{
			Vector3 val = point - origin;
			result = ((((Vector3)(ref val)).sqrMagnitude <= 9f) ? 1 : 0);
		}
		else
		{
			result = 0;
		}
		return (byte)result != 0;
	}

	private void HandleRequest(long sender, ZPackage package)
	{
		//IL_0415: Unknown result type (might be due to invalid IL or missing references)
		//IL_045e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: 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_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_031a: Unknown result type (might be due to invalid IL or missing references)
		//IL_031f: Unknown result type (might be due to invalid IL or missing references)
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0331: 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_0339: Unknown result type (might be due to invalid IL or missing references)
		//IL_0340: Unknown result type (might be due to invalid IL or missing references)
		//IL_0342: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b7: 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_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: 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)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_026d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0272: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null)
		{
			return;
		}
		try
		{
			RecallRequest recallRequest = DecodeRequest(package);
			ZDOID player = recallRequest.Player;
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			if (sender == ZNet.GetUID())
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null || ((Character)Player.m_localPlayer).GetZDOID() != player)
				{
					return;
				}
			}
			else if (peer == null || peer.m_characterID != player)
			{
				return;
			}
			if (lastRequest.TryGetValue(sender, out var value) && Time.unscaledTime - value < 0.15f)
			{
				return;
			}
			lastRequest[sender] = Time.unscaledTime;
			ZDO zDO = ZDOMan.instance.GetZDO(player);
			if (zDO == null || zDO.GetBool(ZDOVars.s_dead, false))
			{
				return;
			}
			string text = zDO.GetString(ZDOVars.s_playerName, string.Empty);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			Vector3 origin = recallRequest.Origin;
			if (!IsFinite(origin))
			{
				return;
			}
			Vector3 val = origin - zDO.GetPosition();
			if (((Vector3)(ref val)).sqrMagnitude > 100f)
			{
				return;
			}
			List<Vector3> points = recallRequest.Points;
			HashSet<ZDOID> loaded = recallRequest.Loaded;
			if (!(ObjectsField.GetValue(ZDOMan.instance) is Dictionary<ZDOID, ZDO> dictionary))
			{
				throw new InvalidOperationException("Cannot enumerate world ZDOs.");
			}
			List<ZDO> list = new List<ZDO>();
			foreach (ZDO value2 in dictionary.Values)
			{
				if (value2.GetBool(ZDOVars.s_tamed, false) && !value2.GetBool(ZDOVars.s_dead, false) && !(value2.GetFloat(ZDOVars.s_health, 1f) <= 0f))
				{
					GameObject val2 = ZNetScene.instance.FindInstance(value2.m_uid);
					MonsterAI val3 = ((!((Object)(object)val2 == (Object)null)) ? val2.GetComponent<MonsterAI>() : null);
					GameObject val4 = ((!((Object)(object)val3 == (Object)null)) ? val3.GetFollowTarget() : null);
					bool flag = ((!((Object)(object)val4 != (Object)null)) ? IsSavedFollower(value2, text) : ((Object)(object)val4.GetComponent<Player>() != (Object)null && ((Character)val4.GetComponent<Player>()).GetZDOID() == player));
					if (!flag && (Object)(object)val4 == (Object)null && value2.GetOwner() == sender && loaded.Contains(value2.m_uid))
					{
						flag = true;
					}
					if (flag)
					{
						list.Add(value2);
					}
				}
			}
			List<Recall> list2 = new List<Recall>();
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				ZDO val5 = list[i];
				Vector3 destination = points[i % points.Count];
				Recall recall = new Recall();
				recall.Id = val5.m_uid;
				recall.Player = player;
				recall.Destination = destination;
				recall.Expires = Time.unscaledTime + 5f;
				Recall recall2 = recall;
				long owner = val5.GetOwner();
				bool flag2 = false;
				try
				{
					val5.SetOwner(ZNet.GetUID());
					PrepareRecall(val5, recall2);
					val5.Set(ZDOVars.s_follow, text);
					val5.Set(ZDOVars.s_patrol, false);
					recalls[recall2.Id] = recall2;
					flag2 = true;
					GameObject val6 = ZNetScene.instance.FindInstance(val5.m_uid);
					Character val7 = ((!((Object)(object)val6 == (Object)null)) ? val6.GetComponent<Character>() : null);
					if ((Object)(object)val7 != (Object)null)
					{
						ApplyLoadedRecall(val7, recall2);
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)string.Concat("Recall failed for ", val5.m_uid, ": ", ex.GetBaseException()));
				}
				finally
				{
					val5.SetOwner((!flag2) ? owner : sender);
					ZDOMan.instance.ForceSendZDO(val5.m_uid);
				}
				if (flag2)
				{
					num++;
					list2.Add(recall2);
					if (list2.Count == 128)
					{
						Broadcast(list2);
						list2.Clear();
					}
				}
			}
			if (list2.Count > 0)
			{
				Broadcast(list2);
			}
			if (sender == ZNet.GetUID())
			{
				HandleResult(sender, num);
			}
			else
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Fai_FollowerCommands_RecallResult_v100", new object[1] { num });
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Recalled " + num + " tamed followers for " + text + " (world-wide, <=3m)."));
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("Recall failed: " + ex2));
			if (sender != ZNet.GetUID() && ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "Fai_FollowerCommands_RecallResult_v100", new object[1] { -1 });
			}
		}
	}

	private void Broadcast(List<Recall> batch)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//IL_0028: 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_0040: Unknown result type (might be due to invalid IL or missing references)
		ZPackage val = new ZPackage();
		val.Write(batch.Count);
		foreach (Recall item in batch)
		{
			val.Write(item.Id);
			val.Write(item.Player);
			val.Write(item.Destination);
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "Fai_FollowerCommands_RecallApply_v100", new object[1] { val });
	}

	private static long GetServerId()
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return 0L;
		}
		if (ZNet.instance.IsServer())
		{
			return ZNet.GetUID();
		}
		return ZNet.instance.GetServerPeer()?.m_uid ?? 0;
	}

	private bool FromServer(long sender)
	{
		return (Object)(object)ZNet.instance != (Object)null && ZRoutedRpc.instance != null && sender == GetServerId();
	}

	private void HandleApply(long sender, ZPackage packet)
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		if (!FromServer(sender) || ZDOMan.instance == null)
		{
			return;
		}
		try
		{
			foreach (Recall item in DecodeApply(packet, Time.unscaledTime))
			{
				if (!ZNet.instance.IsServer() || !recalls.TryGetValue(item.Id, out var _))
				{
					recalls[item.Id] = item;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Invalid recall synchronization: " + ex.Message));
		}
	}

	private static List<Recall> DecodeApply(ZPackage packet, float now)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: 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)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		if (packet == null)
		{
			throw new ArgumentNullException("packet");
		}
		packet.SetPos(0);
		int num = packet.ReadInt();
		if (num < 0 || num > 128 || num > (packet.Size() - packet.GetPos()) / 36)
		{
			throw new ArgumentException("Invalid recall synchronization count.");
		}
		List<Recall> list = new List<Recall>(num);
		for (int i = 0; i < num; i++)
		{
			ZDOID id = packet.ReadZDOID();
			ZDOID player = packet.ReadZDOID();
			Vector3 val = packet.ReadVector3();
			if (!IsFinite(val))
			{
				throw new ArgumentException("Invalid recall destination.");
			}
			list.Add(new Recall
			{
				Id = id,
				Player = player,
				Destination = val,
				Expires = now + 5f
			});
		}
		return list;
	}

	private void HandleResult(long sender, int count)
	{
		if (FromServer(sender))
		{
			awaitingSince = -1f;
			if (count < 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Host reported a recall failure. See the host's BepInEx log.");
			}
		}
	}

	private static void PrepareRecall(ZDO zdo, Recall recall)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: 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_0069: 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)
		if (!recall.Prepared)
		{
			zdo.SetPosition(recall.Destination);
			if (zdo.IsOwner())
			{
				zdo.Set(ZDOVars.s_velHash, Vector3.zero);
				zdo.Set(ZDOVars.s_velRelHash, Vector3.zero);
				zdo.Set(ZDOVars.s_bodyVelHash, Vector3.zero);
				zdo.Set(ZDOVars.s_bodyAVelHash, Vector3.zero);
				zdo.Set(ZDOVars.s_bodyVelocity, Vector3.zero);
				zdo.SetConnection((ConnectionType)2, ZDOID.None);
			}
			recall.Prepared = true;
		}
	}

	private void ApplyLoadedRecall(Character creature, Recall recall)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		recall.Applied = true;
		try
		{
			ApplyToInstance(creature, recall);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)string.Concat("Recall instance update failed for ", recall.Id, ": ", ex.GetBaseException()));
		}
	}

	private static List<Vector3> BuildLandingPoints(Vector3 origin)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: 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)
		//IL_0109: 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_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		List<Vector3> list = new List<Vector3>();
		int mask = LayerMask.GetMask(new string[6] { "Default", "static_solid", "Default_small", "piece", "terrain", "vehicle" });
		RaycastHit val2 = default(RaycastHit);
		for (int i = 0; i < 48; i++)
		{
			float num = (float)i * 2.3999631f;
			float num2 = 1.35f + (float)(i % 4) * 0.4f;
			Vector3 val = origin + new Vector3(Mathf.Cos(num) * num2, 0f, Mathf.Sin(num) * num2);
			if (Physics.Raycast(val + Vector3.up * 1.4f, Vector3.down, ref val2, 3.8f, mask, (QueryTriggerInteraction)1) && !(((RaycastHit)(ref val2)).normal.y < 0.5f))
			{
				val.y = ((RaycastHit)(ref val2)).point.y + 0.1f;
				if (WithinRadius(origin, val) && !Physics.CheckCapsule(val + Vector3.up * 0.3f, val + Vector3.up * 0.9f, 0.25f, mask, (QueryTriggerInteraction)1))
				{
					list.Add(val);
				}
			}
		}
		if (list.Count == 0)
		{
			list.Add(origin + Vector3.up * 0.15f);
		}
		return list;
	}

	private static void ApplyToInstance(Character creature, Recall recall)
	{
		//IL_0007: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_020e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		((Component)creature).transform.position = recall.Destination;
		Rigidbody component = ((Component)creature).GetComponent<Rigidbody>();
		if ((Object)(object)component != (Object)null)
		{
			component.position = recall.Destination;
			if (!component.isKinematic)
			{
				component.linearVelocity = Vector3.zero;
				component.angularVelocity = Vector3.zero;
			}
			component.WakeUp();
		}
		CharacterFields["m_maxAirAltitude"].SetValue(creature, recall.Destination.y);
		CharacterFields["m_lastGroundTouch"].SetValue(creature, 0f);
		CharacterFields["m_fallTimer"].SetValue(creature, 0f);
		CharacterFields["m_pushForce"].SetValue(creature, Vector3.zero);
		CharacterFields["m_rootMotion"].SetValue(creature, Vector3.zero);
		CharacterFields["m_moveDir"].SetValue(creature, Vector3.zero);
		GroundMethod.Invoke(creature, null);
		MonsterAI component2 = ((Component)creature).GetComponent<MonsterAI>();
		if ((Object)(object)component2 != (Object)null)
		{
			if (creature.IsOwner())
			{
				ClearTarget(component2);
				((BaseAI)component2).ResetPatrolPoint();
			}
			GameObject val = ZNetScene.instance.FindInstance(recall.Player);
			if ((Object)(object)val != (Object)null)
			{
				component2.SetFollowTarget(val);
			}
		}
		ZSyncTransform component3 = ((Component)creature).GetComponent<ZSyncTransform>();
		if ((Object)(object)component3 != (Object)null)
		{
			SyncFields["m_targetPosTimer"].SetValue(component3, 0f);
			SyncFields["m_haveTempRelPos"].SetValue(component3, false);
			SyncFields["m_positionCached"].SetValue(component3, recall.Destination);
			SyncFields["m_velocityCached"].SetValue(component3, Vector3.zero);
			SyncFields["m_lastLocalVelocity"].SetValue(component3, Vector3.zero);
			if (creature.IsOwner())
			{
				component3.SyncNow();
			}
		}
	}

	internal static bool SuppressTarget(MonsterAI ai)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Instance == (Object)null || (Object)(object)ai == (Object)null)
		{
			return false;
		}
		ZNetView component = ((Component)ai).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
		{
			return false;
		}
		if (!Instance.recalls.TryGetValue(component.GetZDO().m_uid, out var value) || Time.unscaledTime >= value.Expires)
		{
			return false;
		}
		Character component2 = ((Component)ai).GetComponent<Character>();
		if ((Object)(object)component2 == (Object)null || !component2.IsTamed())
		{
			return false;
		}
		ClearTarget(ai);
		return true;
	}

	private static void ClearTarget(MonsterAI ai)
	{
		TargetCreatureField.SetValue(ai, null);
		TargetStaticField.SetValue(ai, null);
		AlertMethod.Invoke(ai, new object[1] { false });
	}
}
[HarmonyPatch(typeof(MonsterAI), "UpdateTarget")]
internal static class RecallCombatPatch
{
	private static bool Prefix(MonsterAI __instance)
	{
		return !FollowerRecallPlugin.SuppressTarget(__instance);
	}
}
public static class NativeGamepad
{
	private static readonly ComboState State = new ComboState();

	private static readonly MethodInfo TakeInput = typeof(Player).GetMethod("TakeInput", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	public static bool ConsumeAdvance()
	{
		SampleNative();
		return State.TakeAdvance();
	}

	public static bool ConsumeRecall()
	{
		SampleNative();
		return State.TakeRecall();
	}

	public static void Reset()
	{
		State.Reset();
	}

	public static bool CanTakeInput(Player player)
	{
		if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || (Object)(object)Hud.instance == (Object)null || TakeInput == null)
		{
			return false;
		}
		try
		{
			return (bool)TakeInput.Invoke(player, null);
		}
		catch (TargetInvocationException)
		{
			return false;
		}
	}

	internal static void SampleNative()
	{
		ReadPhysical(out var enabled, out var allowed, out var triggerHeld, out var advanceHeld, out var recallHeld);
		State.Sample(Time.frameCount, enabled, allowed, triggerHeld, advanceHeld, recallHeld);
	}

	private static void ReadPhysical(out bool enabled, out bool allowed, out bool triggerHeld, out bool advanceHeld, out bool recallHeld)
	{
		enabled = ZInput.instance != null && ZInput.IsGamepadEnabled();
		allowed = false;
		triggerHeld = false;
		advanceHeld = false;
		recallHeld = false;
		if (enabled)
		{
			triggerHeld = ReadHeld("JoyLTrigger");
			advanceHeld = ReadHeld("JoyButtonX");
			recallHeld = ReadHeld("JoyButtonA");
			allowed = CanTakeInput(Player.m_localPlayer);
		}
	}

	private static bool ReadHeld(string name)
	{
		ButtonDef buttonDef = ZInput.instance.GetButtonDef(name);
		return buttonDef != null && buttonDef.Held;
	}

	internal static bool SuppressNativeButton(string name)
	{
		if (string.IsNullOrEmpty(name))
		{
			return false;
		}
		if (ZInput.instance == null || !ZInput.IsGamepadEnabled())
		{
			return false;
		}
		ButtonDef buttonDef = ZInput.instance.GetButtonDef(name);
		string path;
		try
		{
			path = ((buttonDef != null) ? buttonDef.GetActionPath(true) : null);
		}
		catch (ArgumentOutOfRangeException)
		{
			return false;
		}
		catch (IndexOutOfRangeException)
		{
			return false;
		}
		if (!ComboState.BlocksPath(path, blockAdvance: true, blockRecall: true, triggerHeld: true))
		{
			return false;
		}
		ReadPhysical(out var enabled, out var allowed, out var triggerHeld, out var advanceHeld, out var recallHeld);
		if (!enabled || !allowed)
		{
			return false;
		}
		bool blockAdvance = (triggerHeld && advanceHeld) || State.BlockAdvance(Time.frameCount);
		bool blockRecall = (triggerHeld && recallHeld) || State.BlockRecall(Time.frameCount);
		return ComboState.BlocksPath(path, blockAdvance, blockRecall, triggerHeld);
	}
}
internal sealed class ComboState
{
	private bool previousAdvance;

	private bool previousRecall;

	private bool pendingAdvance;

	private bool pendingRecall;

	private bool consumedAdvance;

	private bool consumedRecall;

	private int advanceReleased = -1;

	private int recallReleased = -1;

	internal void Sample(int frame, bool enabled, bool allowed, bool triggerHeld, bool advanceHeld, bool recallHeld)
	{
		if (!enabled)
		{
			Reset();
			return;
		}
		bool flag = triggerHeld && advanceHeld;
		bool flag2 = triggerHeld && recallHeld;
		if (!allowed)
		{
			pendingAdvance = false;
			pendingRecall = false;
			consumedAdvance = false;
			consumedRecall = false;
			advanceReleased = -1;
			recallReleased = -1;
		}
		else
		{
			if (flag && !recallHeld && !previousAdvance)
			{
				pendingAdvance = true;
			}
			if (flag2 && !advanceHeld && !previousRecall)
			{
				pendingRecall = true;
			}
			if (consumedAdvance && !advanceHeld)
			{
				advanceReleased = frame;
			}
			if (consumedRecall && !recallHeld)
			{
				recallReleased = frame;
			}
			consumedAdvance = advanceHeld && (consumedAdvance || triggerHeld);
			consumedRecall = recallHeld && (consumedRecall || triggerHeld);
		}
		previousAdvance = flag;
		previousRecall = flag2;
	}

	internal bool TakeAdvance()
	{
		bool result = pendingAdvance;
		pendingAdvance = false;
		return result;
	}

	internal bool TakeRecall()
	{
		bool result = pendingRecall;
		pendingRecall = false;
		return result;
	}

	internal bool BlockAdvance(int frame)
	{
		return consumedAdvance || advanceReleased == frame;
	}

	internal bool BlockRecall(int frame)
	{
		return consumedRecall || recallReleased == frame;
	}

	internal static bool BlocksPath(string path, bool blockAdvance, bool blockRecall, bool triggerHeld)
	{
		if (string.IsNullOrEmpty(path))
		{
			return false;
		}
		return (blockAdvance && path.EndsWith("/buttonWest", StringComparison.OrdinalIgnoreCase)) || (blockRecall && path.EndsWith("/buttonSouth", StringComparison.OrdinalIgnoreCase)) || (triggerHeld && (blockAdvance || blockRecall) && path.EndsWith("/leftTrigger", StringComparison.OrdinalIgnoreCase));
	}

	internal void Reset()
	{
		previousAdvance = false;
		previousRecall = false;
		pendingAdvance = false;
		pendingRecall = false;
		consumedAdvance = false;
		consumedRecall = false;
		advanceReleased = -1;
		recallReleased = -1;
	}
}
[HarmonyPatch(typeof(ZInput), "InternalUpdate")]
internal static class NativeGamepadCapturePatch
{
	private static void Postfix()
	{
		NativeGamepad.SampleNative();
	}
}
[HarmonyPatch]
internal static class NativeGamepadConflictPatch
{
	private static IEnumerable<MethodBase> TargetMethods()
	{
		string[] array = new string[3] { "GetButton", "GetButtonDown", "GetButtonUp" };
		foreach (string name in array)
		{
			yield return typeof(ZInput).GetMethod(name, BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null);
		}
	}

	private static bool Prefix(string name, ref bool __result)
	{
		if (!NativeGamepad.SuppressNativeButton(name))
		{
			return true;
		}
		__result = false;
		return false;
	}
}