Decompiled source of DynamicNPCs v0.5.2

DynamicNPCs.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.ModCore.API;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using MagicaCloth2;
using Splatform;
using UnityEngine;
using UnityEngine.UI;
using Valheim.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("DynamicNPCs")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.2.0")]
[assembly: AssemblyInformationalVersion("0.5.2")]
[assembly: AssemblyProduct("DynamicNPCs")]
[assembly: AssemblyTitle("DynamicNPCs")]
[assembly: AssemblyVersion("0.5.2.0")]
namespace MercenaryCompanions;

internal static class AtomicFile
{
	private static readonly Encoding DefaultEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);

	internal static void WriteAllText(string path, string contents, Encoding encoding = null)
	{
		if (string.IsNullOrWhiteSpace(path))
		{
			throw new ArgumentException("A state-file path is required.", "path");
		}
		string directoryName = Path.GetDirectoryName(path);
		if (!string.IsNullOrEmpty(directoryName))
		{
			Directory.CreateDirectory(directoryName);
		}
		string text = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
		try
		{
			File.WriteAllText(text, contents ?? "", encoding ?? DefaultEncoding);
			if (File.Exists(path))
			{
				File.Replace(text, path, path + ".bak", ignoreMetadataErrors: true);
			}
			else
			{
				File.Move(text, path);
			}
		}
		finally
		{
			try
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
			catch
			{
			}
		}
	}
}
internal static class BannerAuthority
{
	private const string ClaimRequestRpc = "DynamicNPCs_BannerClaimRequestV4";

	private const string RemoveRequestRpc = "DynamicNPCs_BannerRemoveRequestV4";

	private const string MessageReplyRpc = "DynamicNPCs_BannerMessageReplyV4";

	private const string SummonRequestRpc = "DynamicNPCs_BannerSummonRequestV1";

	private const string UnsummonRequestRpc = "DynamicNPCs_BannerUnsummonRequestV1";

	private static readonly FieldInfo ZdoObjectsById = AccessTools.Field(typeof(ZDOMan), "m_objectsByID");

	private static readonly List<ZDO> BannerScan = new List<ZDO>();

	private static ZRoutedRpc _registeredRpc;

	private static ZDOMan _scanManager;

	private static int _scanIndex;

	private static float _nextScanAt;

	private static bool _reportedFirstScan;

	private static readonly Dictionary<ZDOID, List<ZDO>> MercsByBanner = new Dictionary<ZDOID, List<ZDO>>();

	private static readonly List<ZDO> OrphanedMercs = new List<ZDO>();

	private static float _loadedRosterTimer;

	private const float HomeSummonTeleportRange = 40f;

	private static readonly HashSet<long> LoginSpawnGranted = new HashSet<long>();

	private static bool _warnedRosterScanBlind;

	private static string _lastRosterSummary = "";

	private const float LoginRejoinWindowSeconds = 180f;

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

	internal static void Register()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null && instance != _registeredRpc)
		{
			instance.Register<ZDOID, int>("DynamicNPCs_BannerClaimRequestV4", (Action<long, ZDOID, int>)OnClaimRequest);
			instance.Register<ZDOID, int>("DynamicNPCs_BannerRemoveRequestV4", (Action<long, ZDOID, int>)OnRemoveRequest);
			instance.Register<string>("DynamicNPCs_BannerMessageReplyV4", (Action<long, string>)OnMessageReply);
			instance.Register<ZDOID>("DynamicNPCs_BannerSummonRequestV1", (Action<long, ZDOID>)OnSummonRequest);
			instance.Register<ZDOID>("DynamicNPCs_BannerUnsummonRequestV1", (Action<long, ZDOID>)OnUnsummonRequest);
			_registeredRpc = instance;
			MercPlugin.Log("Registered global dedicated-server banner authority RPCs.");
		}
	}

	internal static void ResetSceneMemory()
	{
		LoginSpawnGranted.Clear();
		BannerScan.Clear();
		_scanManager = null;
		_scanIndex = 0;
		_nextScanAt = 0f;
		_reportedFirstScan = false;
		MercsByBanner.Clear();
		OrphanedMercs.Clear();
		OwnerOnlineSince.Clear();
	}

	private static void EnsureLoadedBannerRosters()
	{
		_loadedRosterTimer -= Time.deltaTime;
		if (_loadedRosterTimer > 0f)
		{
			return;
		}
		_loadedRosterTimer = 1f;
		try
		{
			foreach (MercBannerSpawn instance in MercBannerSpawn.Instances)
			{
				if (!((Object)(object)instance == (Object)null))
				{
					ZNetView component = ((Component)instance).GetComponent<ZNetView>();
					ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
					if (IsBannerZdo(val))
					{
						ClaimServerOwnership(val);
						MigrateLegacyClaim(val);
						AutoClaimPlacedBanner(val);
						EnsureRoster(val);
					}
				}
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Loaded-banner roster pass failed: " + ex.Message);
		}
	}

	internal static void Update()
	{
		Register();
		if (!IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		try
		{
			EnsureLoadedBannerRosters();
			if (_scanManager != ZDOMan.instance)
			{
				_scanManager = ZDOMan.instance;
				BannerScan.Clear();
				_scanIndex = 0;
				_nextScanAt = 0f;
				_reportedFirstScan = false;
			}
			if ((_scanIndex == 0 && BannerScan.Count == 0 && Time.unscaledTime < _nextScanAt) || !_scanManager.GetAllZDOsWithPrefabIterative("MercBanner", BannerScan, ref _scanIndex))
			{
				return;
			}
			ZDO[] array = BannerScan.ToArray();
			BannerScan.Clear();
			_scanIndex = 0;
			_nextScanAt = Time.unscaledTime + 2f;
			if (!_reportedFirstScan)
			{
				_reportedFirstScan = true;
				MercPlugin.Log($"Global banner authority discovered {array.Length} persistent banner ZDO(s).");
			}
			CollectMercenariesBucketed();
			LogRosterSummary(array.Length);
			ZDO[] array2 = array;
			foreach (ZDO val in array2)
			{
				if (IsBannerZdo(val))
				{
					ClaimServerOwnership(val);
					MigrateLegacyClaim(val);
					AutoClaimPlacedBanner(val);
					EnsureRoster(val);
				}
			}
			TrackOwnerSessions();
			GrantLoginSpawns(array);
			EnforceOneBannerPerPlayer(array);
			RemoveOrphanedMercenaries();
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Global banner authority update failed: " + ex);
			BannerScan.Clear();
			_scanIndex = 0;
			_nextScanAt = Time.unscaledTime + 2f;
		}
	}

	internal static void RequestClaim(ZDOID bannerId, int progressionSeed)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		LogClientRequest("claim", bannerId);
		Register();
		if (ZRoutedRpc.instance == null)
		{
			MercPlugin.LogWarn($"Banner claim request not sent for {bannerId}: ZRoutedRpc.instance is null.");
			return;
		}
		if (((ZDOID)(ref bannerId)).IsNone())
		{
			MercPlugin.LogWarn("Banner claim request not sent: banner ZDOID is None.");
			return;
		}
		ZRoutedRpc.instance.InvokeRoutedRPC("DynamicNPCs_BannerClaimRequestV4", new object[2] { bannerId, progressionSeed });
	}

	internal static void RequestRemoval(ZDOID bannerId, int clientToolHash)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		LogClientRequest("removal", bannerId);
		Register();
		if (ZRoutedRpc.instance == null)
		{
			MercPlugin.LogWarn($"Banner removal request not sent for {bannerId}: ZRoutedRpc.instance is null.");
			return;
		}
		if (((ZDOID)(ref bannerId)).IsNone())
		{
			MercPlugin.LogWarn("Banner removal request not sent: banner ZDOID is None.");
			return;
		}
		ZRoutedRpc.instance.InvokeRoutedRPC("DynamicNPCs_BannerRemoveRequestV4", new object[2] { bannerId, clientToolHash });
	}

	private static void AutoClaimPlacedBanner(ZDO banner)
	{
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!IsServer() || !IsBannerZdo(banner))
			{
				return;
			}
			bool flag = MercConfig.ProgressionPerPlayer != null && MercConfig.ProgressionPerPlayer.Value;
			if ((flag && !WorldDataService.IsWorldStateReady) || banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) != 0L)
			{
				return;
			}
			long num = banner.GetLong(ZDOVars.s_creator, 0L);
			if (num == 0L)
			{
				return;
			}
			int num2 = ServerAuthority.EnsureAuthoritativePlayerStage(num, "banner placement server fallback");
			if (!flag || num2 >= 0)
			{
				string text = null;
				if (ServerAuthority.TryFindConnectedPeerByPlayerId(num, out var _, out var playerName) && !string.IsNullOrWhiteSpace(playerName))
				{
					text = playerName;
				}
				if (string.IsNullOrEmpty(text))
				{
					text = "player " + num;
				}
				banner.Set(MercBannerSpawn.ClaimOwnerIdKey, num);
				banner.Set(MercBannerSpawn.ClaimOwnerNameKey, text);
				for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
				{
					banner.Set(Mercenary.BannerEmployerIds[i], num);
					banner.Set(Mercenary.BannerEmployerNames[i], text);
					banner.Set(Mercenary.BannerFollowNames[i], "");
					banner.Set(MercBannerSpawn.RecruitedKeys[i], true);
				}
				MercPlugin.Log($"Banner {banner.m_uid} auto-claimed for {text} on placement.");
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Banner auto-claim failed: " + ex.Message);
		}
	}

	private static void OnClaimRequest(long sender, ZDOID bannerId, int progressionSeed)
	{
		//IL_000b: 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_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: 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_00fc: 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_01e1: Unknown result type (might be due to invalid IL or missing references)
		MercPlugin.Log($"Banner claim request received: sender={sender}, banner={bannerId}.");
		if (!RequireServerHandler("claim", sender, bannerId) || !TryValidateNearby("claim", sender, bannerId, 8f, out var requester, out var banner))
		{
			return;
		}
		bool flag = MercConfig.ProgressionPerPlayer != null && MercConfig.ProgressionPerPlayer.Value;
		if (flag && !WorldDataService.IsWorldStateReady)
		{
			Reject(sender, "claim", bannerId, "the current world's progression store is not ready", MercLocalization.Phrase("dnpc_banner_loading"));
			return;
		}
		ClaimServerOwnership(banner);
		MigrateLegacyClaim(banner);
		long num = banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
		if (num != 0L && num != requester.PlayerId)
		{
			Reject(sender, "claim", bannerId, $"banner belongs to player ID {num}, not requester {requester.PlayerId}", MercLocalization.Phrase("dnpc_message_banner_already_claimed"));
			return;
		}
		int num2 = ServerAuthority.EnsureAuthoritativeRequesterStage(requester, "banner claim server fallback");
		if (flag && num2 < 0)
		{
			Reject(sender, "claim", bannerId, "the authoritative progression stage could not be persisted", MercLocalization.Phrase("dnpc_banner_loading"));
			return;
		}
		if (num == 0L)
		{
			banner.Set(MercBannerSpawn.ClaimOwnerIdKey, requester.PlayerId);
			banner.Set(MercBannerSpawn.ClaimOwnerNameKey, requester.PlayerName);
			for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
			{
				if (string.IsNullOrWhiteSpace(banner.GetString(MercBannerSpawn.CustomNameKeys[i], "")))
				{
					banner.Set(MercBannerSpawn.CustomNameKeys[i], MercBannerSpawn.DefaultFirstName(MercBannerSpawn.Classes[i]));
				}
				banner.Set(Mercenary.BannerEmployerIds[i], 0L);
				banner.Set(Mercenary.BannerEmployerNames[i], "");
				banner.Set(Mercenary.BannerFollowNames[i], "");
				banner.Set(MercBannerSpawn.RecruitedKeys[i], false);
			}
			MercPlugin.Log($"{requester.PlayerName} claimed banner ZDO {bannerId} through global authority.");
		}
		for (int j = 0; j < MercBannerSpawn.Classes.Length; j++)
		{
			banner.Set(Mercenary.BannerEmployerIds[j], requester.PlayerId);
			banner.Set(Mercenary.BannerEmployerNames[j], requester.PlayerName);
			banner.Set(Mercenary.BannerFollowNames[j], "");
			banner.Set(MercBannerSpawn.RecruitedKeys[j], true);
		}
		EnsureRoster(banner);
		MercPlugin.Log($"Banner claim request accepted: sender={sender}, banner={bannerId}, " + $"owner={requester.PlayerName}, clientStageHint={progressionSeed}, " + $"authoritativeStage={num2}.");
		SendMessage(sender, MercLocalization.Phrase("dnpc_message_company_arrived"));
	}

	private static void OnRemoveRequest(long sender, ZDOID bannerId, int clientToolHash)
	{
		//IL_000b: 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_0030: 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_0120: 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)
		MercPlugin.Log($"Banner removal request received: sender={sender}, banner={bannerId}.");
		if (!RequireServerHandler("removal", sender, bannerId) || !TryValidateNearby("removal", sender, bannerId, 10f, out var requester, out var banner))
		{
			return;
		}
		if (banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) != requester.PlayerId && !ServerAuthority.IsSenderAdmin(sender))
		{
			Reject(sender, "removal", bannerId, "sender is neither the banner owner nor a server admin", MercLocalization.Phrase("dnpc_message_banner_remove_owner"));
			return;
		}
		int stableHashCode = StringExtensionMethods.GetStableHashCode("Hammer");
		int stableHashCode2 = StringExtensionMethods.GetStableHashCode("MercHammer");
		int stableHashCode3 = StringExtensionMethods.GetStableHashCode("Mercenary Hammer");
		if (clientToolHash != stableHashCode && clientToolHash != stableHashCode2 && clientToolHash != stableHashCode3)
		{
			Reject(sender, "removal", bannerId, $"remove-mode tool marker {clientToolHash} did not match a build hammer ({stableHashCode})", MercLocalization.Phrase("dnpc_message_banner_remove_hammer"));
			return;
		}
		string playerName = requester.PlayerName;
		RemoveBannerAndRoster(banner, "dismantled by " + playerName);
		SendMessage(sender, MercLocalization.Phrase("dnpc_message_banner_dismantled"));
		MercPlugin.Log($"Banner removal request accepted: sender={sender}, banner={bannerId}, admin={playerName}.");
	}

	internal static void RequestSummon(ZDOID bannerId)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		Register();
		if (ZRoutedRpc.instance != null && !((ZDOID)(ref bannerId)).IsNone())
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(ServerAuthority.GetServerPeerUid(), "DynamicNPCs_BannerSummonRequestV1", new object[1] { bannerId });
		}
	}

	internal static void RequestUnsummon(ZDOID bannerId)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		Register();
		if (ZRoutedRpc.instance != null && !((ZDOID)(ref bannerId)).IsNone())
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(ServerAuthority.GetServerPeerUid(), "DynamicNPCs_BannerUnsummonRequestV1", new object[1] { bannerId });
		}
	}

	private static void OnUnsummonRequest(long sender, ZDOID bannerId)
	{
		//IL_0006: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		if (!RequireServerHandler("unsummon", sender, bannerId) || !TryValidateNearby("unsummon", sender, bannerId, 10f, out var requester, out var banner))
		{
			return;
		}
		long num = banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
		if (num == 0L || num != requester.PlayerId)
		{
			Reject(sender, "unsummon", bannerId, "banner is not owned by the requester", MercLocalization.Phrase("dnpc_message_banner_not_yours"));
			return;
		}
		ClaimServerOwnership(banner);
		banner.Set(MercBannerSpawn.CompanyStabledKey, 1, false);
		int num2 = 0;
		for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
		{
			ZDOID zDOID = banner.GetZDOID(MercBannerSpawn.MemberIdKeys[i]);
			ZDO val = ((!((ZDOID)(ref zDOID)).IsNone() && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(zDOID) : null);
			if (IsLivingMercZdo(val))
			{
				ServerAuthority.MarkDeliberateRemoval(val.m_uid);
				DestroyWorldZdo(val, "company dismissed at banner");
				num2++;
			}
			banner.RemoveZDOID(MercBannerSpawn.MemberIdKeys[i]);
		}
		MercPlugin.Log($"Company dismissed at banner {bannerId} by {requester.PlayerName}: " + $"{num2} member(s) removed.");
	}

	private static void OnSummonRequest(long sender, ZDOID bannerId)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c7: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_033a: 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_009a: 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_01e7: 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_01ec: 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_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: 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_0282: Unknown result type (might be due to invalid IL or missing references)
		//IL_028b: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0292: 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)
		if (!RequireServerHandler("summon", sender, bannerId) || !TryValidateNearby("summon", sender, bannerId, 10f, out var requester, out var banner))
		{
			return;
		}
		long num = banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
		if (num == 0L || num != requester.PlayerId)
		{
			Reject(sender, "summon", bannerId, "banner is not claimed by the requester", MercLocalization.Phrase("dnpc_message_banner_claim_before_gather"));
			return;
		}
		ClaimServerOwnership(banner);
		if (banner.GetInt(MercBannerSpawn.CompanyStabledKey, 0) != 0)
		{
			banner.Set(MercBannerSpawn.CompanyStabledKey, 0, false);
			for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
			{
				ZDOID zDOID = banner.GetZDOID(MercBannerSpawn.MemberIdKeys[i]);
				ZDO val = ((!((ZDOID)(ref zDOID)).IsNone() && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(zDOID) : null);
				if (IsLivingMercZdo(val))
				{
					ServerAuthority.MarkDeliberateRemoval(val.m_uid);
					DestroyWorldZdo(val, "company re-summoned while stale");
				}
				banner.RemoveZDOID(MercBannerSpawn.MemberIdKeys[i]);
				banner.Set(MercBannerSpawn.LastSeenKeys[i], 0L);
			}
			MercPlugin.Log($"Company summoned back at banner {bannerId} for {requester.PlayerName}.");
			return;
		}
		int num2 = 0;
		for (int j = 0; j < MercBannerSpawn.Classes.Length; j++)
		{
			MercClass mercClass = MercBannerSpawn.Classes[j];
			ZDOID zDOID2 = banner.GetZDOID(MercBannerSpawn.MemberIdKeys[j]);
			ZDO val2 = ((!((ZDOID)(ref zDOID2)).IsNone() && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(zDOID2) : null);
			if (!IsMercenaryZdo(val2) || !string.IsNullOrEmpty(Mercenary.FollowNameOf(val2)))
			{
				continue;
			}
			ClaimServerOwnership(val2);
			val2.Set(Mercenary.ZdoStayDay, 0, false);
			Vector3 val3 = (Vector3)(mercClass switch
			{
				MercClass.Healer => new Vector3(-2f, 0f, 1f), 
				MercClass.Tank => new Vector3(2f, 0f, 1f), 
				_ => new Vector3(0f, 0f, -2f), 
			});
			Vector3 val4 = banner.GetPosition() + val3;
			if (Vector3.Distance(val2.GetPosition(), val4) <= 40f)
			{
				continue;
			}
			GameObject val5 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(zDOID2) : null);
			Mercenary mercenary = (((Object)(object)val5 != (Object)null) ? val5.GetComponent<Mercenary>() : null);
			if (((Object)(object)mercenary != (Object)null && (Object)(object)mercenary.Ai != (Object)null && mercenary.Ai.EngagedInCombat) || val2.GetInt(ZDOVars.s_haveTargetHash, 0) != 0)
			{
				continue;
			}
			try
			{
				val2.SetPosition(val4);
				val2.SetRotation(Quaternion.LookRotation(-val3));
				ZDOMan instance = ZDOMan.instance;
				if (instance != null)
				{
					instance.ForceSendZDO(val2.m_uid);
				}
				num2++;
			}
			catch (Exception ex)
			{
				MercPlugin.LogWarn($"Could not summon {mercClass} {zDOID2} home: {ex.Message}");
			}
		}
		EnsureRoster(banner);
		SendMessage(sender, (num2 > 0) ? MercLocalization.Phrase("dnpc_message_company_gathering_far", num2) : MercLocalization.Phrase("dnpc_message_company_gathering"));
		MercPlugin.Log($"Banner summon accepted: sender={sender}, banner={bannerId}, teleported={num2}.");
	}

	private static void OnMessageReply(long sender, string message)
	{
		if (ServerAuthority.IsAuthoritativeServerSender(sender))
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, MercLocalization.Resolve(message), 0, (Sprite)null, false);
			}
		}
	}

	private static void SendMessage(long target, string message)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "DynamicNPCs_BannerMessageReplyV4", new object[1] { message ?? "" });
		}
	}

	private static bool IsServer()
	{
		if ((Object)(object)ZNet.instance != (Object)null)
		{
			return ZNet.instance.IsServer();
		}
		return false;
	}

	private static void LogClientRequest(string operation, ZDOID bannerId)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		long num = 0L;
		try
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				num = ZNet.GetUID();
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Could not read local routed peer ID for banner " + operation + ": " + ex.Message);
		}
		MercPlugin.Log($"Banner client request: operation={operation}, banner={bannerId}, localPeer={num}, routedRpcExists={ZRoutedRpc.instance != null}.");
	}

	private static bool RequireServerHandler(string operation, long sender, ZDOID bannerId)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		if (IsServer())
		{
			return true;
		}
		MercPlugin.LogWarn($"Banner {operation} request rejected on a non-server peer: sender={sender}, banner={bannerId}.");
		return false;
	}

	private static bool TryValidateNearby(string operation, long sender, ZDOID bannerId, float range, out ServerAuthority.SenderPlayerState requester, out ZDO banner)
	{
		//IL_0015: 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_0045: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: 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_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_015d: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: 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_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: 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)
		bool num = ServerAuthority.TryResolveSender(sender, out requester);
		banner = null;
		if (!num || requester == null)
		{
			return Reject(sender, operation, bannerId, "sender player was not found on the server", MercLocalization.Phrase("dnpc_message_banner_player_missing"));
		}
		if (requester.PlayerId == 0L)
		{
			return Reject(sender, operation, bannerId, $"character ZDO {requester.CharacterZdoId} has no persistent player ID", MercLocalization.Phrase("dnpc_message_banner_identity_not_ready"));
		}
		if (ZDOMan.instance == null || ((ZDOID)(ref bannerId)).IsNone())
		{
			return Reject(sender, operation, bannerId, (ZDOMan.instance == null) ? "ZDOMan is unavailable" : "banner ZDOID is None", MercLocalization.Phrase("dnpc_message_banner_record_missing"));
		}
		banner = ZDOMan.instance.GetZDO(bannerId);
		if (banner == null || !banner.IsValid())
		{
			return Reject(sender, operation, bannerId, "banner ZDO was not found or is invalid", MercLocalization.Phrase("dnpc_message_banner_record_missing"));
		}
		int stableHashCode = StringExtensionMethods.GetStableHashCode("MercBanner");
		int prefab = banner.GetPrefab();
		if (prefab != stableHashCode)
		{
			return Reject(sender, operation, bannerId, $"wrong prefab hash {prefab}; expected {stableHashCode}", MercLocalization.Phrase("dnpc_message_banner_wrong_object"));
		}
		if (requester.IsDead)
		{
			return Reject(sender, operation, bannerId, "requesting player is dead", MercLocalization.Phrase("dnpc_message_banner_while_dead"));
		}
		Vector3 position = requester.Position;
		Vector3 position2 = banner.GetPosition();
		float num2 = Vector3.Distance(position, position2);
		if (num2 > range)
		{
			return Reject(sender, operation, bannerId, $"distance {num2:F2}m exceeds {range:F2}m; player={position}, banner={position2}", MercLocalization.Phrase("dnpc_message_banner_move_within", range.ToString("F0")));
		}
		return true;
	}

	private static bool Reject(long sender, string operation, ZDOID bannerId, string reason, string playerMessage)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		MercPlugin.LogWarn($"Banner {operation} request rejected: sender={sender}, banner={bannerId}, reason={reason}.");
		SendMessage(sender, playerMessage);
		return false;
	}

	private static void LogHammerDiagnostics(ServerAuthority.SenderPlayerState requester, long sender, ZDOID bannerId, int mercHammerHash, int displayNameHash)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = null;
		try
		{
			object obj;
			if (requester == null)
			{
				obj = null;
			}
			else
			{
				Player livePlayer = requester.LivePlayer;
				obj = ((livePlayer != null) ? ((Humanoid)livePlayer).GetCurrentWeapon() : null);
			}
			val = ((ItemData)(obj?)).m_dropPrefab;
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn($"Banner removal hammer diagnostics failed for sender={sender}, banner={bannerId}: {ex.Message}");
		}
		MercPlugin.Log($"Banner removal hammer diagnostics: sender={sender}, banner={bannerId}, " + "currentWeaponDropPrefab=" + (((Object)(object)val != (Object)null) ? ((Object)val).name : "<null>") + ", " + $"playerRightItemHash={requester?.RightItemHash ?? 0}, " + $"MercHammerHash={mercHammerHash}, " + $"MercenaryHammerHash={displayNameHash}.");
	}

	private static bool IsBannerZdo(ZDO zdo)
	{
		if (zdo != null && zdo.IsValid())
		{
			return zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("MercBanner");
		}
		return false;
	}

	private static void ClaimServerOwnership(ZDO zdo)
	{
		if (zdo != null && IsServer() && zdo.GetOwner() != ZNet.GetUID())
		{
			zdo.SetOwner(ZNet.GetUID());
		}
	}

	private static void MigrateLegacyClaim(ZDO banner)
	{
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		if (!IsBannerZdo(banner) || banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) != 0L)
		{
			return;
		}
		for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
		{
			long num = banner.GetLong(Mercenary.BannerEmployerIds[i], 0L);
			string text = banner.GetString(Mercenary.BannerEmployerNames[i], "");
			if (num == 0L || string.IsNullOrWhiteSpace(text))
			{
				continue;
			}
			banner.Set(MercBannerSpawn.ClaimOwnerIdKey, num);
			banner.Set(MercBannerSpawn.ClaimOwnerNameKey, text);
			for (int j = 0; j < MercBannerSpawn.Classes.Length; j++)
			{
				if (string.IsNullOrWhiteSpace(banner.GetString(MercBannerSpawn.CustomNameKeys[j], "")))
				{
					banner.Set(MercBannerSpawn.CustomNameKeys[j], MercBannerSpawn.DefaultFirstName(MercBannerSpawn.Classes[j]));
				}
			}
			MercPlugin.Log($"Global banner authority migrated legacy claim for {text} at {banner.m_uid}.");
			break;
		}
	}

	private static void GrantLoginSpawns(ZDO[] banners)
	{
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			foreach (ZDO val in banners)
			{
				if (!IsBannerZdo(val))
				{
					continue;
				}
				long num = val.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
				if (num != 0L && !LoginSpawnGranted.Contains(num) && val.GetInt(MercBannerSpawn.CompanyStabledKey, 0) == 0 && IsOwnerOnline(num))
				{
					LoginSpawnGranted.Add(num);
					for (int j = 0; j < MercBannerSpawn.Classes.Length; j++)
					{
						val.Set(MercBannerSpawn.LastSeenKeys[j], 0L);
					}
					MercPlugin.Log($"Login spawn granted for player {num}'s banner {val.m_uid}: missing mercenaries return immediately.");
				}
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Login spawn grant failed: " + ex.Message);
		}
	}

	private static bool IsOwnerOnline(long playerId)
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && localPlayer.GetPlayerID() == playerId)
			{
				return true;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && !((ZDOID)(ref peer.m_characterID)).IsNone())
				{
					ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(peer.m_characterID) : null);
					if (val != null && val.GetLong(ZDOVars.s_playerID, 0L) == playerId)
					{
						return true;
					}
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static ZDO ChooseSurvivingBanner(ZDO a, ZDO b)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: 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_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: 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_00fa: 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)
		try
		{
			List<ZDO> list = AllZdosFromTable();
			int num = 0;
			int num2 = 0;
			foreach (ZDO item in list)
			{
				if (item == null || !item.IsValid() || !IsMercenaryZdo(item))
				{
					continue;
				}
				ZDOID val = Mercenary.BannerIdOf(item);
				if (IsLivingMercZdo(item))
				{
					if (val == a.m_uid)
					{
						num++;
					}
					else if (val == b.m_uid)
					{
						num2++;
					}
				}
			}
			if (num != num2)
			{
				return (num > num2) ? a : b;
			}
			Vector3 val2 = Vector3.zero;
			bool flag = false;
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null))
				{
					val2 = ((Component)allPlayer).transform.position;
					flag = true;
					break;
				}
			}
			if (flag)
			{
				return (Vector3.Distance(a.GetPosition(), val2) <= Vector3.Distance(b.GetPosition(), val2)) ? a : b;
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Duplicate-banner choice failed: " + ex.Message);
		}
		return a;
	}

	private static void NotifyOwnerOfDuplicate(long owner, ZDO removed)
	{
		try
		{
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null) && allPlayer.GetPlayerID() == owner)
				{
					((Character)allPlayer).Message((MessageType)2, MercLocalization.Text("dnpc_message_duplicate_banner"), 0, (Sprite)null, false);
					break;
				}
			}
		}
		catch
		{
		}
	}

	private static void EnforceOneBannerPerPlayer(ZDO[] banners)
	{
		try
		{
			Dictionary<long, ZDO> dictionary = new Dictionary<long, ZDO>();
			foreach (ZDO val in banners)
			{
				if (!IsBannerZdo(val))
				{
					continue;
				}
				long num = val.GetLong(ZDOVars.s_creator, 0L);
				if (num == 0L)
				{
					num = val.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
				}
				if (num != 0L)
				{
					if (!dictionary.TryGetValue(num, out var value))
					{
						dictionary[num] = val;
						continue;
					}
					ZDO val2 = ChooseSurvivingBanner(value, val);
					ZDO val3 = ((val2 == value) ? val : value);
					dictionary[num] = val2;
					RemoveBannerAndRoster(val3, $"duplicate Mercenary Banner for player {num}");
					NotifyOwnerOfDuplicate(num, val3);
				}
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("One-banner-per-player enforcement failed: " + ex.Message);
		}
	}

	private static void EnsureRoster(ZDO banner)
	{
		//IL_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: 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_013c: 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_0103: 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_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_025d: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_0375: Unknown result type (might be due to invalid IL or missing references)
		//IL_0399: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
		if (!IsBannerZdo(banner) || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		bool flag = MercConfig.ProgressionPerPlayer != null && MercConfig.ProgressionPerPlayer.Value;
		if ((flag && !MercProgressionRules.CanMutatePersonalProgression(flag, WorldDataService.IsWorldStateReady)) || banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) == 0L)
		{
			return;
		}
		AdoptBeforeRosterCreation(banner);
		int stage;
		bool flag2 = TryResolveAuthoritativeCompanyStage(banner, out stage);
		if (flag2)
		{
			SynchronizeProgressionStage(banner, stage);
		}
		double timeSeconds = ZNet.instance.GetTimeSeconds();
		for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
		{
			MercClass mercClass = MercBannerSpawn.Classes[i];
			if (!string.IsNullOrWhiteSpace(banner.GetString(Mercenary.BannerEmployerNames[i], "")))
			{
				banner.Set(MercBannerSpawn.RecruitedKeys[i], true);
			}
			List<ZDO> list = FindPersistentMercZdos(mercClass, banner.m_uid);
			ZDOID zDOID = banner.GetZDOID(MercBannerSpawn.MemberIdKeys[i]);
			ZDO val = ChooseCanonicalMerc(list, zDOID);
			if (val == null && !((ZDOID)(ref zDOID)).IsNone() && ZDOMan.instance != null)
			{
				ZDO zDO = ZDOMan.instance.GetZDO(zDOID);
				if (IsMercenaryZdo(zDO) && Mercenary.BannerIdOf(zDO) == banner.m_uid)
				{
					val = zDO;
					if (!list.Contains(zDO))
					{
						list.Add(zDO);
					}
				}
			}
			if (val != null)
			{
				banner.Set(MercBannerSpawn.MemberIdKeys[i], val.m_uid);
				foreach (ZDO item in list)
				{
					if (item != null && item.m_uid != val.m_uid)
					{
						DestroyWorldZdo(item, $"duplicate {mercClass} for banner {banner.m_uid}");
					}
				}
			}
			else
			{
				banner.RemoveZDOID(MercBannerSpawn.MemberIdKeys[i]);
			}
			if (IsLivingMercZdo(val))
			{
				if (flag2)
				{
					SynchronizeProgressionStage(val, stage);
				}
				RecoverLostMember(banner, mercClass, val);
				RejoinFollowedOwner(banner, mercClass, val);
				long num = Mercenary.EmployerIdOf(val);
				string text = Mercenary.EmployerNameOf(val);
				string text2 = Mercenary.FollowNameOf(val);
				if (val.GetOwner() == ZNet.GetUID())
				{
					Mercenary.RestoreEmploymentFromBanner(val, banner, mercClass);
				}
				long num2 = Mercenary.EmployerIdOf(val);
				string text3 = Mercenary.EmployerNameOf(val);
				string text4 = Mercenary.FollowNameOf(val);
				if (num != num2 || text != text3 || text2 != text4)
				{
					MercPlugin.Log($"Roster employment repaired: banner={banner.m_uid}, class={mercClass}, " + $"member={val.m_uid}, ownerId={num2}, ownerName={text3}, " + "follow=" + text4 + ".");
				}
				banner.Set(MercBannerSpawn.LastSeenKeys[i], (long)timeSeconds);
				continue;
			}
			if (val != null)
			{
				AnnounceFallIfUnreported(banner, mercClass, i, val);
				DestroyWorldZdo(val, $"dead {mercClass} ready for banner respawn");
			}
			banner.RemoveZDOID(MercBannerSpawn.MemberIdKeys[i]);
			double num3 = banner.GetLong(MercBannerSpawn.LastSeenKeys[i], 0L);
			if (num3 <= 0.0)
			{
				num3 = banner.GetFloat(MercBannerSpawn.LastSeenKeys[i], 0f);
			}
			if (banner.GetInt(MercBannerSpawn.CompanyStabledKey, 0) != 0)
			{
				continue;
			}
			if (val == null && !((ZDOID)(ref zDOID)).IsNone() && num3 > 0.0 && timeSeconds - num3 < 120.0 && ServerAuthority.AnnouncedFallen.Add(zDOID))
			{
				ServerAuthority.AnnounceRosterFall(RosterMemberName(banner, mercClass, i));
				MercPlugin.Log($"Roster lost track of {mercClass} {zDOID} without a recorded death; presumed fallen.");
			}
			if ((!(num3 > 0.0) || !(timeSeconds - num3 < (double)MercConfig.RespawnDelaySeconds.Value)) && SpawnMerc(banner, mercClass, i, flag2 ? stage : (-1)))
			{
				banner.Set(MercBannerSpawn.LastSeenKeys[i], (long)timeSeconds);
				if (!((ZDOID)(ref zDOID)).IsNone())
				{
					ServerAuthority.AnnouncedFallen.Remove(zDOID);
				}
			}
		}
	}

	internal static List<ZDO> AllZdosFromTable()
	{
		List<ZDO> list = new List<ZDO>();
		if (ZDOMan.instance == null || ZdoObjectsById == null)
		{
			return list;
		}
		try
		{
			if (ZdoObjectsById.GetValue(ZDOMan.instance) is IDictionary<ZDOID, ZDO> dictionary)
			{
				list.AddRange(dictionary.Values);
			}
		}
		catch
		{
		}
		return list;
	}

	internal static void PublishCompanyStage(long playerId, int stage)
	{
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		bool personalModeEnabled = MercConfig.ProgressionPerPlayer != null && MercConfig.ProgressionPerPlayer.Value;
		if (!IsServer() || ZDOMan.instance == null || playerId == 0L || !MercProgressionRules.CanMutatePersonalProgression(personalModeEnabled, WorldDataService.IsWorldStateReady))
		{
			return;
		}
		try
		{
			int num = MercProgressionRules.ResolveStage(stage, -1);
			List<ZDO> list = AllZdosFromTable();
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			int num2 = 0;
			foreach (ZDO item in list)
			{
				if (IsBannerZdo(item) && item.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) == playerId)
				{
					hashSet.Add(item.m_uid);
					if (SynchronizeProgressionStage(item, num))
					{
						num2++;
					}
				}
			}
			if (hashSet.Count > 0)
			{
				foreach (ZDO item2 in list)
				{
					if (IsMercenaryZdo(item2) && hashSet.Contains(Mercenary.BannerIdOf(item2)) && SynchronizeProgressionStage(item2, num))
					{
						num2++;
					}
				}
			}
			if (num2 > 0)
			{
				MercPlugin.Log($"Published company progression stage {num} for player " + $"{playerId} to {num2} persistent ZDO(s).");
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Could not publish company progression stage: " + ex.Message);
		}
	}

	private static bool TryResolveAuthoritativeCompanyStage(ZDO banner, out int stage)
	{
		stage = -1;
		if (MercConfig.ProgressionPerPlayer == null || !MercConfig.ProgressionPerPlayer.Value)
		{
			return false;
		}
		long num = banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
		if (num == 0L)
		{
			return false;
		}
		int bannerStage = Mercenary.ProgressionStageOf(banner);
		if (!WorldDataService.IsWorldStateReady)
		{
			return false;
		}
		int stage2 = PlayerProgression.GetStage(num);
		int num2 = MercProgressionRules.SelectDurableStage(stage2, bannerStage, PlayerProgression.WasRecoveredFromBackup);
		if (num2 >= 0)
		{
			if (stage2 < 0 || num2 > stage2)
			{
				int num3 = PlayerProgression.EnsureSeeded(num, num2, PlayerProgression.WasRecoveredFromBackup ? "newer banner mirror after atomic-backup recovery" : "recovered banner progression mirror");
				if (num3 < num2)
				{
					return false;
				}
				stage = num3;
				return true;
			}
			stage = num2;
			return true;
		}
		int num4 = ServerAuthority.SeedStageFromLiveGear(num);
		if (num4 >= 0)
		{
			stage = MercProgressionRules.ResolveStage(num4, -1);
			return true;
		}
		stage = PlayerProgression.EnsureSeeded(num, 0, "unobserved legacy company fallback");
		return stage >= 0;
	}

	private static bool SynchronizeProgressionStage(ZDO zdo, int stage)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (zdo == null || Mercenary.ProgressionStageOf(zdo) == stage)
			{
				return false;
			}
			ClaimServerOwnership(zdo);
			if (!Mercenary.SetProgressionStage(zdo, stage))
			{
				return false;
			}
			ZDOMan instance = ZDOMan.instance;
			if (instance != null)
			{
				instance.ForceSendZDO(zdo.m_uid);
			}
			return true;
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Could not synchronize a company progression mirror: " + ex.Message);
			return false;
		}
	}

	private static void WarnOnceRosterScanBlind(string reason)
	{
		if (!_warnedRosterScanBlind)
		{
			_warnedRosterScanBlind = true;
			MercPlugin.LogWarn("Persistent roster scan is blind (" + reason + "); banner adoption and orphan cleanup cannot work this session.");
		}
	}

	private static void LogRosterSummary(int bannerCount)
	{
		int num = 0;
		foreach (KeyValuePair<ZDOID, List<ZDO>> item in MercsByBanner)
		{
			num += item.Value.Count;
		}
		string text = $"{num} mercenary ZDO(s) under {MercsByBanner.Count} banner id(s), " + $"{OrphanedMercs.Count} orphan(s), {bannerCount} banner(s)";
		if (!(text == _lastRosterSummary))
		{
			_lastRosterSummary = text;
			MercPlugin.Log("Persistent roster census: " + text + ".");
		}
	}

	private static void CollectMercenariesBucketed()
	{
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: 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_0105: Unknown result type (might be due to invalid IL or missing references)
		MercsByBanner.Clear();
		OrphanedMercs.Clear();
		if (ZDOMan.instance == null)
		{
			return;
		}
		if (ZdoObjectsById == null)
		{
			WarnOnceRosterScanBlind("the world ZDO table field was not found");
			return;
		}
		try
		{
			if (!(ZdoObjectsById.GetValue(ZDOMan.instance) is IDictionary<ZDOID, ZDO> dictionary))
			{
				WarnOnceRosterScanBlind("the world ZDO table could not be read");
				return;
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode("Merc_Tank");
			int stableHashCode2 = StringExtensionMethods.GetStableHashCode("Merc_Healer");
			int stableHashCode3 = StringExtensionMethods.GetStableHashCode("Merc_Archer");
			foreach (KeyValuePair<ZDOID, ZDO> item in dictionary)
			{
				ZDO value = item.Value;
				if (value == null || !value.IsValid())
				{
					continue;
				}
				int prefab = value.GetPrefab();
				if (prefab != stableHashCode && prefab != stableHashCode2 && prefab != stableHashCode3)
				{
					continue;
				}
				ZDOID key = Mercenary.BannerIdOf(value);
				if (((ZDOID)(ref key)).IsNone())
				{
					if (!value.GetBool(Mercenary.ZdoStandalone, false))
					{
						OrphanedMercs.Add(value);
					}
					continue;
				}
				if (!MercsByBanner.TryGetValue(key, out var value2))
				{
					value2 = (MercsByBanner[key] = new List<ZDO>());
				}
				value2.Add(value);
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Persistent roster scan failed: " + ex.Message);
		}
	}

	private static List<ZDO> FindPersistentMercZdos(MercClass mercClass, ZDOID bannerId)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		List<ZDO> list = new List<ZDO>();
		if (MercsByBanner.TryGetValue(bannerId, out var value))
		{
			int stableHashCode = StringExtensionMethods.GetStableHashCode(PrefabName(mercClass));
			foreach (ZDO item in value)
			{
				if (item != null && item.IsValid() && item.GetPrefab() == stableHashCode)
				{
					list.Add(item);
				}
			}
		}
		return list;
	}

	private static ZDO ChooseCanonicalMerc(List<ZDO> candidates, ZDOID trackedId)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		ZDO val = null;
		int num = int.MinValue;
		foreach (ZDO candidate in candidates)
		{
			if (candidate != null)
			{
				int num2 = (IsLivingMercZdo(candidate) ? 10000 : 0);
				if (!((ZDOID)(ref trackedId)).IsNone() && candidate.m_uid == trackedId)
				{
					num2 += 5000;
				}
				if (!string.IsNullOrEmpty(Mercenary.EmployerNameOf(candidate)))
				{
					num2 += 500;
				}
				if (!string.IsNullOrEmpty(Mercenary.FollowNameOf(candidate)))
				{
					num2 += 250;
				}
				if ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ZNetScene.instance.FindInstance(candidate.m_uid) != (Object)null)
				{
					num2 += 100;
				}
				if (val == null || num2 > num || (num2 == num && ((ZDOID)(ref candidate.m_uid)).CompareTo(val.m_uid) < 0))
				{
					val = candidate;
					num = num2;
				}
			}
		}
		return val;
	}

	private static void TrackOwnerSessions()
	{
		try
		{
			bool flag = MercProgressionRules.CanMutatePersonalProgression(MercConfig.ProgressionPerPlayer != null && MercConfig.ProgressionPerPlayer.Value, WorldDataService.IsWorldStateReady);
			HashSet<long> hashSet = new HashSet<long>();
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null))
				{
					long playerID = allPlayer.GetPlayerID();
					if (playerID != 0L)
					{
						hashSet.Add(playerID);
					}
				}
			}
			foreach (long item in hashSet)
			{
				if (!OwnerOnlineSince.ContainsKey(item))
				{
					OwnerOnlineSince[item] = Time.time;
				}
				if (flag)
				{
					ServerAuthority.SeedStageFromLiveGear(item);
				}
			}
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, float> item2 in OwnerOnlineSince)
			{
				if (!hashSet.Contains(item2.Key))
				{
					list.Add(item2.Key);
				}
			}
			foreach (long item3 in list)
			{
				OwnerOnlineSince.Remove(item3);
			}
		}
		catch
		{
		}
	}

	internal static void HoldFollowingCompanyOnOwnerDeath(Player owner)
	{
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: 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)
		if ((Object)(object)owner == (Object)null || !IsServer() || ZDOMan.instance == null)
		{
			return;
		}
		long playerID = owner.GetPlayerID();
		if (playerID == 0L)
		{
			return;
		}
		int num = Mathf.Max(1, WorldDataService.CurrentDay);
		int num2 = 0;
		foreach (ZDO item in AllZdosFromTable())
		{
			if (!IsBannerZdo(item) || item.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) != playerID)
			{
				continue;
			}
			ClaimServerOwnership(item);
			for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
			{
				if (!string.IsNullOrEmpty(item.GetString(Mercenary.BannerFollowNames[i], "")))
				{
					item.Set(Mercenary.BannerFollowNames[i], "");
					ZDOID zDOID = item.GetZDOID(MercBannerSpawn.MemberIdKeys[i]);
					ZDO val = ((!((ZDOID)(ref zDOID)).IsNone()) ? ZDOMan.instance.GetZDO(zDOID) : null);
					if (IsLivingMercZdo(val))
					{
						ClaimServerOwnership(val);
						Vector3 position = val.GetPosition();
						Mercenary.SetFollowName(val, "");
						val.Set(Mercenary.ZdoStayDay, num, false);
						val.Set(Mercenary.ZdoStayAnchor, position);
						val.Set(MercAI.ZdoGuideTask, "");
						val.Set("merc_guideFind", "");
						val.Set("merc_guideRoute", "");
						ZDOMan.instance.ForceSendZDO(val.m_uid);
						GameObject val2 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(val.m_uid) : null);
						(((Object)(object)val2 != (Object)null) ? val2.GetComponent<Mercenary>() : null)?.NotifySimulationOwnerOfStateChange(refillFood: false);
						num2++;
					}
				}
			}
		}
		if (num2 > 0)
		{
			MercPlugin.Log($"Owner death hold: {num2} following mercenary(s) kept their " + "current positions and stopped guiding for the respawn flow.");
		}
	}

	private static void RejoinFollowedOwner(ZDO banner, MercClass mercClass, ZDO tracked)
	{
		//IL_01a6: 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_00b8: 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_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: 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_0126: 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_0136: 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)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			int num = Mathf.Clamp((int)mercClass, 0, Mercenary.BannerFollowNames.Length - 1);
			string text = banner.GetString(Mercenary.BannerFollowNames[num], "");
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			long num2 = banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
			if (num2 != 0L && OwnerOnlineSince.TryGetValue(num2, out var value) && !(Time.time - value > 180f) && TryGetOnlineOwnerPosition(num2, out var position, out var rotation) && (!((Object)(object)ZNetScene.instance != (Object)null) || !((Object)(object)ZNetScene.instance.FindInstance(tracked.m_uid) != (Object)null)) && tracked.GetInt(ZDOVars.s_haveTargetHash, 0) == 0 && !(Vector3.Distance(tracked.GetPosition(), position) <= 60f))
			{
				Vector3 val = (Vector3)(mercClass switch
				{
					MercClass.Healer => new Vector3(-2f, 0f, 1f), 
					MercClass.Tank => new Vector3(2f, 0f, 1f), 
					_ => new Vector3(0f, 0f, -2f), 
				});
				ClaimServerOwnership(tracked);
				tracked.SetPosition(position + rotation * val);
				tracked.SetRotation(rotation);
				ZDOMan instance = ZDOMan.instance;
				if (instance != null)
				{
					instance.ForceSendZDO(tracked.m_uid);
				}
				MercPlugin.Log($"Rejoined owner: {mercClass} {tracked.m_uid}" + (string.IsNullOrEmpty(text) ? "" : (" following '" + text + "'")) + " was stranded far from the online owner; moved to their side.");
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn($"Followed-owner rejoin failed for {tracked.m_uid}: {ex.Message}");
		}
	}

	private static bool TryGetOnlineOwnerPosition(long playerId, out Vector3 position, out Quaternion rotation)
	{
		//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_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: 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_00d4: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
		position = Vector3.zero;
		rotation = Quaternion.identity;
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && localPlayer.GetPlayerID() == playerId)
			{
				position = ((Component)localPlayer).transform.position;
				rotation = ((Component)localPlayer).transform.rotation;
				return true;
			}
			if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null)
			{
				return false;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && !((ZDOID)(ref peer.m_characterID)).IsNone())
				{
					ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
					if (zDO != null && zDO.IsValid() && zDO.GetLong(ZDOVars.s_playerID, 0L) == playerId)
					{
						position = zDO.GetPosition();
						rotation = zDO.GetRotation();
						return true;
					}
				}
			}
		}
		catch
		{
		}
		return false;
	}

	private static string RosterMemberName(ZDO banner, MercClass mercClass, int index)
	{
		return banner.GetString(MercBannerSpawn.CustomNameKeys[index], MercBannerSpawn.DefaultFirstName(mercClass)) + " " + ServerAuthority.RoleTitle(mercClass);
	}

	private static void AnnounceFallIfUnreported(ZDO banner, MercClass mercClass, int index, ZDO tracked)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (tracked != null && !IsLivingMercZdo(tracked) && ServerAuthority.AnnouncedFallen.Add(tracked.m_uid))
			{
				ServerAuthority.AnnounceRosterFall(RosterMemberName(banner, mercClass, index));
			}
		}
		catch
		{
		}
	}

	private static void RecoverLostMember(ZDO banner, MercClass mercClass, ZDO tracked)
	{
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: 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)
		//IL_00b0: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: 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_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: 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_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!string.IsNullOrEmpty(Mercenary.FollowNameOf(tracked)))
			{
				return;
			}
			int num = tracked.GetInt(Mercenary.ZdoStayDay, 0);
			int currentDay = WorldDataService.CurrentDay;
			if ((num > 0 && currentDay > 0 && currentDay - num < 1) || ((Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ZNetScene.instance.FindInstance(tracked.m_uid) != (Object)null))
			{
				return;
			}
			Vector3 val = (Vector3)(mercClass switch
			{
				MercClass.Healer => new Vector3(-2f, 0f, 1f), 
				MercClass.Tank => new Vector3(2f, 0f, 1f), 
				_ => new Vector3(0f, 0f, -2f), 
			});
			Vector3 val2 = banner.GetPosition() + val;
			if (!(Vector3.Distance(tracked.GetPosition(), val2) <= 40f))
			{
				ClaimServerOwnership(tracked);
				tracked.SetPosition(val2);
				tracked.SetRotation(Quaternion.LookRotation(-val));
				tracked.Set(Mercenary.ZdoStayDay, 0, false);
				ZDOMan instance = ZDOMan.instance;
				if (instance != null)
				{
					instance.ForceSendZDO(tracked.m_uid);
				}
				MercPlugin.Log($"Recovered lost {mercClass} {tracked.m_uid}: it was not following, " + $"not instantiated, and far from banner {banner.m_uid}; moved home.");
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn($"Lost-member recovery failed for {tracked.m_uid}: {ex.Message}");
		}
	}

	private static bool IsLivingMercZdo(ZDO zdo)
	{
		if (zdo != null && zdo.IsValid() && !zdo.GetBool(ZDOVars.s_dead, false))
		{
			return zdo.GetFloat(ZDOVars.s_health, 1f) > 0f;
		}
		return false;
	}

	private static bool SpawnMerc(ZDO banner, MercClass mercClass, int classIndex, int progressionStage)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//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)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: 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_0083: 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_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: 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_0181: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		GameObject mercPrefab = MercPrefabs.GetMercPrefab(mercClass);
		if ((Object)(object)mercPrefab == (Object)null)
		{
			MercPlugin.LogWarn($"Global banner authority has no prefab for {mercClass}; it will retry.");
			return false;
		}
		Vector3 val = (Vector3)(mercClass switch
		{
			MercClass.Healer => new Vector3(-2f, 0f, 1f), 
			MercClass.Tank => new Vector3(2f, 0f, 1f), 
			_ => new Vector3(0f, 0f, -2f), 
		});
		Vector3 val2 = banner.GetRotation() * val;
		Vector3 val3 = banner.GetPosition() + val2;
		float num = (((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.GetSolidHeight(val3) : (-10000f));
		if (num > -10000f)
		{
			val3.y = num + 0.1f;
		}
		Quaternion val4 = ((((Vector3)(ref val2)).sqrMagnitude > 0.001f) ? Quaternion.LookRotation(-val2) : Quaternion.identity);
		GameObject val5 = Object.Instantiate<GameObject>(mercPrefab, val3, val4);
		ZNetView component = val5.GetComponent<ZNetView>();
		if ((Object)(object)component != (Object)null && component.IsValid())
		{
			ZDO zDO = component.GetZDO();
			ClaimServerOwnership(zDO);
			zDO.Set(ZDOVars.s_modelIndex, (mercClass == MercClass.Healer) ? 1 : 0, false);
			Mercenary.SetBannerId(zDO, banner.m_uid);
			Mercenary.RestoreEmploymentFromBanner(zDO, banner, mercClass);
			if (progressionStage >= 0)
			{
				Mercenary.SetProgressionStage(zDO, progressionStage);
			}
			banner.Set(MercBannerSpawn.MemberIdKeys[classIndex], zDO.m_uid);
			MercPlugin.Log($"Global banner authority spawned {mercClass} {zDO.m_uid} for banner {banner.m_uid}.");
			MercPlugin.Log($"Employment synchronized: banner={banner.m_uid}, class={mercClass}, " + $"member={zDO.m_uid}, ownerId={Mercenary.EmployerIdOf(zDO)}, " + "ownerName=" + Mercenary.EmployerNameOf(zDO) + ", follow=" + Mercenary.FollowNameOf(zDO) + ".");
			return true;
		}
		Object.Destroy((Object)(object)val5);
		MercPlugin.LogWarn($"Global banner authority could not create a network ZDO for {mercClass}; it will retry.");
		return false;
	}

	private static void RemoveBannerAndRoster(ZDO banner, string reason)
	{
		//IL_0011: 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_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_0043: 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_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		if (!IsBannerZdo(banner) || ZDOMan.instance == null)
		{
			return;
		}
		ZDOID uid = banner.m_uid;
		Dictionary<ZDOID, ZDO> dictionary = new Dictionary<ZDOID, ZDO>();
		for (int i = 0; i < MercBannerSpawn.Classes.Length; i++)
		{
			ZDOID zDOID = banner.GetZDOID(MercBannerSpawn.MemberIdKeys[i]);
			ZDO val = ((!((ZDOID)(ref zDOID)).IsNone()) ? ZDOMan.instance.GetZDO(zDOID) : null);
			if (IsMercenaryZdo(val))
			{
				dictionary[val.m_uid] = val;
			}
			foreach (ZDO item in FindPersistentMercZdos(MercBannerSpawn.Classes[i], uid))
			{
				dictionary[item.m_uid] = item;
			}
		}
		try
		{
			foreach (ZDO item2 in AllZdosFromTable())
			{
				if (item2 != null && item2.IsValid() && IsMercenaryZdo(item2) && Mercenary.BannerIdOf(item2) == uid)
				{
					dictionary[item2.m_uid] = item2;
				}
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Banner removal safety sweep failed: " + ex.Message);
		}
		foreach (ZDO value in dictionary.Values)
		{
			DestroyWorldZdo(value, "its banner was " + reason);
		}
		try
		{
			MercBannerSpawn.ServerRemovalInProgress = true;
			DestroyWorldZdo(banner, "banner " + reason);
		}
		finally
		{
			MercBannerSpawn.ServerRemovalInProgress = false;
		}
		MercPlugin.Log($"Global banner authority removed banner {uid} and {dictionary.Count} mercenary ZDO(s): {reason}.");
	}

	private static void RemoveOrphanedMercenaries()
	{
		//IL_0052: 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)
		try
		{
			List<ZDO> list = new List<ZDO>(OrphanedMercs);
			if (ZDOMan.instance != null && ZdoObjectsById != null && ZdoObjectsById.GetValue(ZDOMan.instance) is IDictionary<ZDOID, ZDO>)
			{
				foreach (KeyValuePair<ZDOID, List<ZDO>> item in MercsByBanner)
				{
					if (!IsBannerZdo(ZDOMan.instance.GetZDO(item.Key)))
					{
						list.AddRange(item.Value);
					}
				}
			}
			int num = 0;
			int num2 = 0;
			foreach (ZDO item2 in list)
			{
				if (item2 != null && item2.IsValid())
				{
					ZDO val = FindOwnerBannerFor(item2);
					if (val != null && AdoptIntoBanner(val, item2))
					{
						num++;
						continue;
					}
					DestroyWorldZdo(item2, $"orphaned from missing banner {Mercenary.BannerIdOf(item2)}");
					num2++;
				}
			}
			if (num > 0 || num2 > 0)
			{
				MercPlugin.Log($"Orphan sweep: adopted {num} living mercenary ZDO(s) into their " + $"employer's current banner; removed {num2} with no home banner.");
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Orphaned mercenary cleanup failed: " + ex.Message);
		}
	}

	private static ZDO FindOwnerBannerFor(ZDO orphan)
	{
		try
		{
			long num = Mercenary.EmployerIdOf(orphan);
			if (num == 0L)
			{
				return null;
			}
			foreach (ZDO item in AllZdosFromTable())
			{
				if (IsBannerZdo(item) && item.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) == num)
				{
					return item;
				}
			}
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn("Owner banner lookup failed: " + ex.Message);
		}
		return null;
	}

	private static bool AdoptIntoBanner(ZDO banner, ZDO orphan)
	{
		return TryAdoptLivingOrphan(banner, orphan);
	}

	private static void AdoptBeforeRosterCreation(ZDO banner)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		bool flag = false;
		string[] memberIdKeys = MercBannerSpawn.MemberIdKeys;
		foreach (string text in memberIdKeys)
		{
			if (!IsLivingMercZdo(ZDOMan.instance.GetZDO(banner.GetZDOID(text))))
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		long num = banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L);
		foreach (ZDO item in AllZdosFromTable())
		{
			if (IsMercenaryZdo(item) && IsLivingMercZdo(item) && Mercenary.EmployerIdOf(item) == num && !item.GetBool(Mercenary.ZdoStandalone, false))
			{
				ZDOID val = Mercenary.BannerIdOf(item);
				if (!IsBannerZdo(ZDOMan.instance.GetZDO(val)))
				{
					TryAdoptLivingOrphan(banner, item);
				}
			}
		}
	}

	internal static bool TryAdoptLivingOrphan(ZDO banner, ZDO orphan)
	{
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: 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)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: 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_01b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!IsServer() || !IsBannerZdo(banner) || !IsLivingMercZdo(orphan) || Mercenary.EmployerIdOf(orphan) == 0L || Mercenary.EmployerIdOf(orphan) != banner.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L))
			{
				return false;
			}
			int prefab = orphan.GetPrefab();
			MercClass mercClass = ((prefab != StringExtensionMethods.GetStableHashCode("Merc_Tank")) ? ((prefab == StringExtensionMethods.GetStableHashCode("Merc_Healer")) ? MercClass.Healer : ((prefab == StringExtensionMethods.GetStableHashCode("Merc_Archer")) ? MercClass.Archer : ((MercClass)(-1)))) : MercClass.Tank);
			if (mercClass < MercClass.Tank)
			{
				return false;
			}
			int num = Mathf.Clamp((int)mercClass, 0, MercBannerSpawn.MemberIdKeys.Length - 1);
			ZDOID zDOID = banner.GetZDOID(MercBannerSpawn.MemberIdKeys[num]);
			ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(zDOID) : null);
			if (val != null && val.IsValid() && val.m_uid != orphan.m_uid && IsLivingMercZdo(val))
			{
				return false;
			}
			ZDOID val2 = Mercenary.BannerIdOf(orphan);
			ClaimServerOwnership(banner);
			ClaimServerOwnership(orphan);
			banner.Set(MercBannerSpawn.CustomNameKeys[num], orphan.GetString(Mercenary.ZdoCustomName, MercBannerSpawn.DefaultFirstName(mercClass)));
			banner.Set(Mercenary.BannerEmployerIds[num], Mercenary.EmployerIdOf(orphan));
			banner.Set(Mercenary.BannerEmployerNames[num], Mercenary.EmployerNameOf(orphan));
			banner.Set(Mercenary.BannerFollowNames[num], Mercenary.FollowNameOf(orphan));
			banner.Set(MercBannerSpawn.RecruitedKeys[num], true);
			Mercenary.SetBannerId(orphan, banner.m_uid);
			Mercenary.RestoreEmploymentFromBanner(orphan, banner, mercClass);
			banner.Set(MercBannerSpawn.MemberIdKeys[num], orphan.m_uid);
			MercPlugin.Log($"Adopted mercenary {orphan.m_uid} ({mercClass}) into banner {banner.m_uid}; " + $"its previous banner {val2} no longer exists.");
			return true;
		}
		catch (Exception ex)
		{
			MercPlugin.LogWarn($"Mercenary adoption failed for {orphan.m_uid}: {ex.Message}");
			return false;
		}
	}

	private static bool IsMercenaryZdo(ZDO zdo)
	{
		if (zdo == null || !zdo.IsValid())
		{
			return false;
		}
		int prefab = zdo.GetPrefab();
		if (prefab != StringExtensionMethods.GetStableHashCode("Merc_Tank") && prefab != StringExtensionMethods.GetStableHashCode("Merc_Healer"))
		{
			return prefab == StringExtensionMethods.GetStableHashCode("Merc_Archer");
		}
		return true;
	}

	internal static void DestroyWorldZdo(ZDO zdo, string reason)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: 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)
		if (zdo == null || ZDOMan.instance == null)
		{
			return;
		}
		ZDOID uid = zdo.m_uid;
		ClaimServerOwnership(zdo);
		GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(uid) : null);
		if ((Object)(object)val != (Object)null)
		{
			ZNetView component = val.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner())
			{
				component.ClaimOwnership();
			}
			ZNetScene.instance.Destroy(val);
		}
		else
		{
			ClaimServerOwnership(zdo);
			ZDOMan.instance.DestroyZDO(zdo);
		}
		MercPlugin.Log($"Removed ZDO {uid}: {reason}");
	}

	private static string PrefabName(MercClass mercClass)
	{
		return mercClass switch
		{
			MercClass.Archer => "Merc_Archer", 
			MercClass.Healer => "Merc_Healer", 
			_ => "Merc_Tank", 
		};
	}
}
internal static class ServerAuthority
{
	internal sealed class SenderPlayerState
	{
		internal long SenderUid;

		internal long PeerUid;

		internal ZDOID CharacterZdoId;

		internal Player LivePlayer;

		internal ZDO PlayerZdo;

		internal long PlayerId;

		internal string PlayerName;

		internal Vector3 Position;

		internal bool IsDead;

		internal int RightItemHash;
	}

	internal sealed class MercenaryState
	{
		internal ZDOID MercId;

		internal ZDO MercZdo;

		internal ZDOID BannerId;

		internal ZDO BannerZdo;

		internal MercClass Class;

		internal string Name;

		internal Vector3 Position;

		internal long EmployerId;

		internal string EmployerName;

		internal string FollowName;
	}

	private sealed class ResourceJob
	{
		internal long Requester;

		internal long PlayerId;

		internal long Owner;

		internal int Token;

		internal float Expires;

		internal float NextHit;

		internal MercResourceWork.Target Target;

		internal int Stage;
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static ConsoleEvent <>9__6_0;

		public static Func<long, bool> <>9__9_0;

		public static Func<CompanyPreset, string> <>9__9_2;

		public static Comparison<MercenaryState> <>9__66_0;

		internal void <RegisterCompanyCommand>b__6_0(ConsoleEventArgs args)
		{
			_companyConsole = args.Context;
			string text = string.Join(" ", args.Args.Skip(1).ToArray());
			if (string.IsNullOrWhiteSpace(text))
			{
				text = "status";
			}
			if (text.Length > 160)
			{
				args.Context.AddString("Company command is too long.");
				return;
			}
			Register();
			if (IsServer())
			{
				OnCompanyCommand(ZNet.GetUID(), text);
				return;
			}
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				instance.InvokeRoutedRPC("DynamicNPCs_CompanyV1", new object[1] { text });
			}
		}

		internal bool <OnCompanyCommand>b__9_0(long id)
		{
			if (id != ZNet.GetUID())
			{
				return ZNet.instance.GetPeer(id) == null;
			}
			return false;
		}

		internal string <OnCompanyCommand>b__9_2(CompanyPreset p)
		{
			return p.Name + ": " + p.Formation.ToString() + " / " + p.Duty;
		}

		internal int <ListOwnedCompany>b__66_0(MercenaryState left, MercenaryState right)
		{
			int num = (int)left.Class;
			return num.CompareTo((int)right.Class);
		}
	}

	private const string CompanyCommandRpc = "DynamicNPCs_CompanyV1";

	private const string CompanyReplyRpc = "DynamicNPCs_CompanyReplyV1";

	internal const string CompanyFormationKey = "dnpc.company.formation.v1";

	private const string CompanyPresetsKey = "dnpc.company.presets.v1";

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

	private static Terminal _companyConsole;

	private const string AgentRequestRpc = "MercAgentRequestV1";

	private const string AgentReplyRpc = "MercAgentReplyV1";

	private const string CleanupRequestRpc = "DynamicNPCs_AdminClearRadiusRequestV1";

	private const string CleanupReplyRpc = "DynamicNPCs_AdminClearRadiusReplyV1";

	private const string PurgeRequestRpc = "DynamicNPCs_AdminPurgeMercsRequestV1";

	private const string PlayerMessageRpc = "DynamicNPCs_PlayerMessageV1";

	private const string MercRehireRequestRpc = "DynamicNPCs_MercRehireRequestV1";

	private const string MercCommandRequestRpc = "DynamicNPCs_MercCommandRequestV1";

	private const string MercRenameRequestRpc = "DynamicNPCs_MercRenameRequestV1";

	private const string MercDismissRequestRpc = "DynamicNPCs_MercDismissRequestV1";

	private const string MercConversationRequestRpc = "DynamicNPCs_MercConversationRequestV1";

	private const string MercResurrectionRequestRpc = "DynamicNPCs_MercResurrectionRequestV1";

	private const string MercSpeechReplyRpc = "DynamicNPCs_MercSpeechReplyV1";

	private const string MercGuideApplyRpc = "DynamicNPCs_MercGuideApplyV1";

	private const string MercResurrectionApplyRpc = "DynamicNPCs_MercResurrectionApplyV1";

	private const string ContextCompanyOrderRequestRpc = "DynamicNPCs_ContextCompanyOrderRequestV1";

	private const string ContextTargetApplyRpc = "DynamicNPCs_ContextTargetApplyV1";

	private static readonly object RequestLock = new object();

	private static readonly Dictionary<long, int> PendingAi = new Dictionary<long, int>();

	private static readonly Dictionary<long, int> LastAiRequest = new Dictionary<long, int>();

	private static readonly Queue<int> AiRequestTimes = new Queue<int>();

	private static readonly object ContextOrderLock = new object();

	private static readonly Dictionary<long, int> LastContextOrderAt = new Dictionary<long, int>();

	private static readonly Dictionary<long, int> LastContextOrderSequence = new Dictionary<long, int>();

	private static readonly object IdentityLock = new object();

	private static readonly Dictionary<long, long> PinnedPeerPlayerIds = new Dictionary<long, long>();

	private static readonly Dictionary<long, long> PinnedPlayerPeers = new Dictionary<long, long>();

	private static readonly FieldInfo PeerSocketField = AccessTools.Field(typeof(ZNetPeer), "m_socket");

	private static readonly FieldInfo ZdoObjectsById = AccessTools.Field(typeof(ZDOMan), "m_objectsByID");

	private static ZRoutedRpc _registeredRpc;

	internal static readonly HashSet<ZDOID> AnnouncedFallen = new HashSet<ZDOID>();

	private const string ResourceRequestRpc = "DynamicNPCs_ResourceWorkRequestV1";

	private const string ResourceApplyRpc = "DynamicNPCs_ResourceWorkApplyV1";

	private const string ResourceSwingRpc = "DynamicNPCs_ResourceWorkSwingV1";

	private const string ResourceStopRpc = "DynamicNPCs_ResourceWorkStopV1";

	private static ZRoutedRpc _resourceRpc;

	private static int _resourceToken;

	private static readonly Dictionary<ZDOID, ResourceJob> ResourceJobs = new Dictionary<ZDOID, ResourceJob>();

	internal static void RegisterCompanyCommand()
	{
		//IL_0033: 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_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Expected O, but got Unknown
		object obj = <>c.<>9__6_0;
		if (obj == null)
		{
			ConsoleEvent val = delegate(ConsoleEventArgs args)
			{
				_companyConsole = args.Context;
				string text = string.Join(" ", args.Args.Skip(1).ToArray());
				if (string.IsNullOrWhiteSpace(text))
				{
					text = "status";
				}
				if (text.Length > 160)
				{
					args.Context.AddString("Company command is too long.");
				}
				else
				{
					Register();
					if (IsServer())
					{
						OnCompanyCommand(ZNet.GetUID(), text);
					}
					else
					{
						ZRoutedRpc instance = ZRoutedRpc.instance;
						if (instance != null)
						{
							instance.InvokeRoutedRPC("DynamicNPCs_CompanyV1", new object[1] { text });
						}
					}
				}
			};
			<>c.<>9__6_0 = val;
			obj = (object)val;
		}
		new ConsoleCommand("dnpc_company", "company status | preset list | preset save <name> <roles|tight|line> <follow|hold> | preset apply/delete <name>", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static void OnCompanyReply(long sender, string message)
	{
		if (!IsAuthoritativeServerSender(sender) || message == null || message.Length > 1800)
		{
			return;
		}
		if ((Object)(object)_companyConsole != (Object)null)
		{
			_companyConsole.AddString(message);
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if (localPlayer != null)
		{
			((Character)localPlayer).Message((MessageType)1, message, 0, (Sprite)null, false);
		}
	}

	private static void ReplyCompany(long sender, string message)
	{
		message = Bound(message, 1800);
		if (sender == ZNet.GetUID())
		{
			OnCompanyReply(sender, message);
			return;
		}
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC(sender, "DynamicNPCs_CompanyReplyV1", new object[1] { message });
		}
	}

	private static void OnCompanyCommand(long sender, string payload)
	{
		//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0249: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0650: Unknown result type (might be due to invalid IL or missing references)
		//IL_0684: Unknown result type (might be due to invalid IL or missing references)
		//IL_066d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0689: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_06bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_06c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_06cc: Unknown result type (might be due to invalid IL or missing references)
		if (!IsServer() || payload == null || payload.Length > 160 || !TryResolveSender(sender, out var requester))
		{
			return;
		}
		float unscaledTime = Time.unscaledTime;
		if (LastCompanyCommand.TryGetValue(sender, out var value) && unscaledTime - value < 1f)
		{
			return;
		}
		long[] array = LastCompanyCommand.Keys.Where((long id) => id != ZNet.GetUID() && ZNet.instance.GetPeer(id) == null).ToArray();
		foreach (long key in array)
		{
			LastCompanyCommand.Remove(key);
		}
		LastCompanyCommand[sender] = unscaledTime;
		try
		{
			string[] parts = payload.Trim().ToLowerInvariant().Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			ZDO val = ((IEnumerable<ZDO>)BannerAuthority.AllZdosFromTable()).FirstOrDefault((Func<ZDO, bool>)((ZDO zdo) => zdo != null && zdo.IsValid() && zdo.GetPrefab() == StringExtensionMethods.GetStableHashCode("MercBanner") && zdo.GetLong(MercBannerSpawn.ClaimOwnerIdKey, 0L) == requester.PlayerId));
			if (parts.Length == 1 && parts[0] == "status")
			{
				StringBuilder stringBuilder = new StringBuilder("Your company: player=" + requester.PlayerId + ", banner=" + ((val != null) ? ((object)Unsafe.As<ZDOID, ZDOID>(ref val.m_uid)/*cast due to .constrained prefix*/).ToString() : "missing") + "\n");
				foreach (ZDO item in BannerAuthority.AllZdosFromTable())
				{
					if (item != null && item.IsValid() && Mercenary.EmployerIdOf(item) == requester.PlayerId && TryResolveMercenary(item.m_uid, out var state, out var _, allowMissingBanner: true))
					{
						stringBuilder.Append(state.Name).Append(": id=").Append(state.MercId)
							.Append(", owner=")
							.Append(item.GetOwner())
							.Append(", duty=")
							.Append(string.IsNullOrEmpty(state.FollowName) ? "hold" : "follow")
							.Append(", distance=")
							.Append(Vector3.Distance(requester.Position, state.Position).ToString("F0"))
							.Append("m, ")
							.Append((state.BannerZdo == null) ? "orphan: awaiting owned banner recovery" : "roster linked")
							.AppendLine();
					}
				}
				ReplyCompany(sender, stringBuilder.ToString());
				return;
			}
			if (val == null)
			{
				throw new ArgumentException("Claim a company banner first.");
			}
			if (requester.IsDead)
			{
				throw new ArgumentException("Company presets cannot change while you are dead.");
			}
			if (parts.Length < 2 || parts[0] != "preset")
			{
				throw new ArgumentException("Use status or preset list/save/apply/delete.");
			}
			List<CompanyPreset> list = CompanyPresetRules.Decode(val.GetString("dnpc.company.presets.v1", ""));
			if (parts.Length == 2 && parts[1] == "list")
			{
				ReplyCompany(sender, (list.Count == 0) ? "No presets. Save one with: dnpc_company preset save travel roles follow" : string.Join("\n", list.Select((CompanyPreset p) => p.Name + ": " + p.Formation.ToString() + " / " + p.Duty)));
				return;
			}
			if (parts.Length < 3 || !CompanyPresetRules.ValidName(parts[2]))
			{
				throw new ArgumentException("Use a 1–24 character name containing a-z, 0-9, - or _.");
			}
			CompanyPreset companyPreset = list.FirstOrDefault((CompanyPreset p) => p.Name == parts[2]);
			if (parts[1] == "save" && parts.Length == 5)
			{
				if (!Enum.TryParse<CompanyFormation>(parts[3], ignoreCase: true, out var result) || !Enum.IsDefined(typeof(CompanyFormation), result) || !Enum.TryParse<CompanyDuty>(parts[4], ignoreCase: true, out var result2) || !Enum.IsDefined(typeof(CompanyDuty), result2))
				{
					throw new ArgumentException("Choose roles/tight/line and follow/hold.");
				}
				if (companyPreset == null)
				{
					if (list.Count >= 8)
					{
						throw new ArgumentException("Delete a preset before adding a ninth.");
					}
					list.Add(companyPreset = new CompanyPreset
					{
						Name = parts[2]
					});
				}
				companyPreset.Formation = result;
				companyPreset.Duty = result2;
				EnsureZdoServerOwnership(val);
				val.Set("dnpc.company.presets.v1", CompanyPresetRules.Encode(list));
				ReplyCompany(sender, "Saved preset " + companyPreset.Name + ".");
				return;
			}
			if (parts[1] == "delete" && parts.Length == 3 && companyPreset != null)
			{
				list.Remove(companyPreset);
				EnsureZdoServerOwnership(val);
				val.Set("dnpc.company.presets.v1", CompanyPresetRules.Encode(list));
				ReplyCompany(sender, "Deleted preset " + companyPreset.Name + ".");
				return;
			}
			if (parts[1] == "apply" && parts.Length == 3 && companyPreset != null)
			{
				ConfigEntry<bool> contextCompanyOrdersEnabled = MercConfig.ContextCompanyOrdersEnabled;
				if (contextCompanyOrdersEnabled == null || !contextCompanyOrdersEnabled.Value)
				{
					throw new ArgumentException("The server disabled company orders.");
				}
				List<MercenaryState> list2 = ListOwnedCompany(requester);
				if (list2.Count == 0)
				{
					throw new ArgumentException("No employed company is ready.");
				}
				EnsureZdoServerOwnership(val);
				val.Set("dnpc.company.formation.v1", (int)companyPreset.Formation);
				foreach (MercenaryState item2 in list2)
				{
					EnsureZdoServerOwnership(item2.MercZdo);
					item2.MercZdo.Set("dnpc.company.formation.v1", (int)companyPreset.Formation);
				}
				if (companyPreset.Duty == CompanyDuty.Follow)
				{
					ApplyCompanyRecall(sender, requester, list2);
				}
				else
				{
					ApplyCompanyHold(sender, requester, list2, requester.Position);
					Vector3 forward = (((Object)(object)requester.LivePlayer != (Object)null) ? ((Component)requester.LivePlayer).transform.forward : Vector3.forward);
					foreach (MercenaryState item3 in list2)
					{
						item3.MercZdo.Set(Mercenary.ZdoStayAnchor, requester.Position + MercFormation.Offset(item3.Class, forward, MercMovementMode.Travel, companyPreset.Formation));
					}
				}
				ReplyCompany(sender, "Applied " + companyPreset.Name + ": " + companyPreset.Formation.ToString() + " / " + companyPreset.Duty.ToString() + ".");
				return;
			}
			throw new ArgumentException("Unknown preset or syntax. Use preset list, save <name> <formation> <duty>, apply <name>, or delete <name>.");
		}
		catch (Exception ex)
		{
			ReplyCompany(sender, "Company: " + ex.Message);
		}
	}

	internal static void Register()
	{
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null && instance != _registeredRpc)
		{
			instance.Register<string>("MercAgentRequestV1", (Action<long, string>)OnAgentRequest);
			instance.Register<string>("DynamicNPCs_CompanyV1", (Action<long, string>)OnCompanyCommand);
			instance.Register<string>("DynamicNPCs_CompanyReplyV1", (Action<long, string>)OnCompanyReply);
			instance.Register<string, string>("MercAgentReplyV1", (Action<long, string, string>)OnAgentReply);
			instance.Register<float>("DynamicNPCs_AdminClearRadiusRequestV1", (Action<long, float>)OnCleanupRequest);
			instance.Register<string>("DynamicNPCs_AdminClearRadiusReplyV1", (Action<long, string>)OnCleanupReply);
			instance.Register("DynamicNPCs_AdminPurgeMercsRequestV1", (Action<long>)OnPurgeRequest);
			instance.Register<string>("DynamicNPCs_PlayerMessageV1", (Action<long, string>)OnPlayerMessage);
			instance.Register<ZDOID, int>("DynamicNPCs_MercRehireRequestV1", (Action<long, ZDOID, int>)OnMercRehireRequest);
			instance.Register<ZDOID, bool>("DynamicNPCs_MercCommandRequestV1", (Action<long, ZDOID, bool>)OnMercCommandRequest);
			instance.Register<ZDOID, string>("DynamicNPCs_MercRenameRequestV1", (Action<long, ZDOID, string>)OnMercRenameRequest);
			instance.Register<ZDOID>("DynamicNPCs_MercDismissRequestV1", (Action<long, ZDOID>)OnMercDismissRequest);
			instance.Register<ZDOID, int, int, string, string>("DynamicNPCs_GuideMenuRequestV1", (Method<ZDOID, int, int, string, string>)OnGuideMenuRequest);
			instance.Register<ZDOID, string>("DynamicNPCs_MercConversationRequestV1", (Action<long, ZDOID, string>)OnMercConversationRequest);
			instance.Register<ZDOID, ZDOID>("DynamicNPCs_MercResurrectionRequestV1", (Action<long, ZDOID, ZDOID>)OnMercResurrectionRequest);
			instance.Register<ZDOID, string, string>("DynamicNPCs_MercSpeechReplyV1", (Action<long, ZDOID, string, string>)OnMercSpeechReply);
			instance.Register<ZDOID, ZDOID, string>("DynamicNPCs_MercGuideApplyV1", (Action<long, ZDOID, ZDOID, string>)OnMercGuideApply);
			instance.Register<ZDOID, ZDOID>("DynamicNPCs_MercResurrectionApplyV1", (Action<long, ZDOID, ZDOID>)OnMercResurrectionApply);
			instance.Register<int, ZDOID, Vector3, int>("DynamicNPCs_ContextCompanyOrderRequestV1", (Method<int, ZDOID, Vector3, int>)OnContextCompanyOrderRequest);
			instance.Register<ZDOID, ZDOID, int, ZDOID>("DynamicNPCs_ContextTargetApplyV1", (Method<ZDOID, ZDOID, int, ZDOID>)OnContextTargetApply);
			MercResourceWork.Register();
			_registeredRpc = instance;
			MercPlugin.Log("Registered server-authoritative mercenary RPC protocol.");
		}
	}

	internal static void Reset()
	{
		MercResourceWork.Reset();
		lock (RequestLock)
		{
			PendingAi.Clear();
			LastAiRequest.Clear();
			AiRequestTimes.Clear();
		}
		lock (ContextOrderLock)
		{
			LastContextOrderAt.Clear();
			LastContextOrderSequence.Clear();
		}
		lock (IdentityLock)
		{
			PinnedPeerPlayerIds.Clear();
			PinnedPlayerPeers.Clear();
		}
		AnnouncedFallen.Clear();
	}

	internal static void SendAgentQuestion(string question)
	{
		if (ZRoutedRpc.instance != null)
		{
			string text = Bound(question, 1000);
			if (!string.IsNullOrEmpty(text))
			{
				string text2 = "{\"q\":\"" + MiniJson.Escape(text) + "\"}";
				ZRoutedRpc.instance.InvokeRoutedRPC("MercAgentRequestV1", new object[1] { text2 });
			}
		}
	}

	internal static void RequestNpcCleanup(float radius)
	{
		Register();
		float num = Mathf.Clamp(radius, 1f, 200f);
		MercPlugin.Log($"Admin NPC cleanup client request: radius={num:F1}m, " + $"localPeer={(((Object)(object)ZNet.instance != (Object)null) ? ZNet.GetUID() : 0)}, " + $"routedRpcExists={ZRoutedRpc.instance != null}.");
		if (ZRoutedRpc.instance == null)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, MercLocalization.Text("dnpc_message_cleanup_network_failed"), 0, (Sprite)null, false);
			}
		}
		else
		{
			ZRoutedRpc.instance.InvokeRoutedRPC("DynamicNPCs_AdminClearRadiusRequestV1", new object[1] { num });
		}
	}

	internal static void RequestMercPurge()
	{
		Register();
		if (ZRoutedRpc.instance == null)
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, MercLocalization.Text("dnpc_message_purge_network_failed"), 0, (Sprite)null, false);
			}
		}
		else
		{
			ZRoutedRpc.instance.InvokeRoutedRPC("DynamicNPCs_AdminPurgeMercsRequestV1", Array.Empty<object>());
		}
	}

	internal static void RequestMercRehire(ZDOID mercId, int progressionSeed)
	{
		//IL_000a: 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)
		Register();
		MercPlugin.Log($"Mercenary rehire client request: mercZdo={mercId}, " + $"localPeer={(((Object)(object)ZNet.instance != (Object)null) ? ZNet.GetUID() : 0)}, " + $"routedRpcExists={ZRoutedRpc.instance != null}.");
		if (ZRoutedRpc.instance == null || ((ZDOID)(ref mercId)).IsNone())
		{
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, MercLocalization.Text("dnpc_message_rehire_network_failed"), 0, (Sprite)null, false);
			}
		}
		else
		{
			ZRoutedRpc.instance.InvokeRoutedRPC("DynamicNPCs_MercRehireRequestV1", new object[2] { mercId, progressionSeed });
		}
	}

	internal static void RequestMercCommand(ZDOID mercId, bool message)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		Register();
		MercPlugin.Log($"Mercenary command client request: mercZdo={mercId}, localPeer={(((Object)(object)ZNet.instance != (Object)null) ? ZNet.GetUID() : 0)}.");
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC("DynamicNPCs_MercCommandRequestV1", new object[2] { mercId, message });
		}
	}

	internal static void RequestMercRename(ZDOID mercId, string name)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		Register();
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC("DynamicNPCs_MercRenameRequestV1", new object[2]
			{
				mercId,
				Bound(name, 24)
			});
		}
	}

	internal static void RequestMercDismiss(ZDOID mercId)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		Register();
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC("DynamicNPCs_MercDismissRequestV1", new object[1] { mercId });
		}
	}

	internal static void RequestMercConversation(ZDOID mercId, string payload)
	{
		//IL_000a: 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)
		Register();
		MercPlugin.Log(string.Format("Merc conversation client request: mercZdo={0}, payloadLength={1}.", mercId, (payload ?? "").Length));
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC("DynamicNPCs_MercConversationRequestV1", new object[2]
			{
				mercId,
				Bound(payload, 10000)
			});
		}
	}

	internal static void RequestMercResurrection(ZDOID mercId, ZDOID playerId)
	{
		//IL_001c: 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)
		Register();
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC("DynamicNPCs_MercResurrectionRequestV1", new object[2] { mercId, playerId });
		}
	}

	internal static void RequestContextCompanyOrder(MercContextOrderType order, ZDOID targetId, Vector3 point, int sequence)
	{
		//IL_0025: 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)
		Register();
		ZRoutedRpc instance = ZRoutedRpc.instance;
		if (instance != null)
		{
			instance.InvokeRoutedRPC("DynamicNPCs_ContextCompanyOrderRequestV1", new object[4]
			{
				(int)order,
				targetId,
				point,
				sequence
			});
		}
	}

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

	private static void OnAgentRequest(long sender, string payload)
	{
		if (string.IsNullOrEmpty(payload) || payload.Length > 10000)
		{
			return;
		}
		MercPlugin.Log($"Private agent request received: sender={sender}.");
		if (!IsServer())
		{
			MercPlugin.LogWarn($"Private agent request rejected: sender={sender}, reason=handler is not the authoritative server.");
			return;
		}
		if (!TryResolveSender(sender, out var state))
		{
			MercPlugin.LogWarn($"Private agent request rejected: sender={sender}, reason=sender player/character ZDO could not be resolved.");
			SendAgentReply(sender, "I cannot identify your character on the server yet. Try again in a moment.");
			return;
		}
		if (!MercConfig.AgentChatEnabled.Value)
		{
			MercPlugin.LogWarn($"Private agent request rejected: sender={sender}, reason=AgentChatEnabled is false.");
			SendAgentReply(sender, "Private ! agent chat is disabled by the server.");
			return;
		}
		if (!TryBeginAi(sender, state, null, agentMode: true))
		{
			MercPlugin.LogWarn($"Private agent request rejected: sender={sender}, reason=AI request is already pending or rate-limited.");
			return;
		}
		string text = Bound(ReadString(MiniJson.Parse(payload) as Dictionary<string, object>, "q"), 1000);
		if (string.IsNullOrWhiteSpace(