Decompiled source of ValheimBattleRoyale v1.1.1

ValheimBattleRoyale.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimBattleRoyale")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimBattleRoyale")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("75C3A508-4CBA-494A-B957-1B0C9E976E0B")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.1.1.0")]
namespace ValheimBattleRoyale;

[BepInPlugin("com.xtasy.ValheimBattleRoyale", "Valheim Battle Royale", "1.1.1")]
public sealed class ValheimBattleRoyalePlugin : BaseUnityPlugin
{
	internal sealed class ZonePresence
	{
		internal bool Known;

		internal bool Outside;

		internal double LastLog = double.NegativeInfinity;

		internal bool Observe(bool outside, bool diagnostics, double now, out bool entered)
		{
			entered = Known && Outside && !outside;
			bool num = !Known || Outside != outside;
			Known = true;
			Outside = outside;
			double num2 = ((!diagnostics) ? 30 : (outside ? 5 : 10));
			if (!num && now - LastLog < num2)
			{
				return false;
			}
			LastLog = now;
			return true;
		}
	}

	private sealed class PeerDiagnostics
	{
		internal long AckRevision = -1L;

		internal long LastDamageSent;

		internal long LastDamageAck;

		internal double FirstUnackedAt = -1.0;

		internal double LastStateAckAt;

		internal double FirstSeenAt = LocalClock();

		internal ZDOID Character;
	}

	public const string GUID = "com.xtasy.ValheimBattleRoyale";

	public const string NAME = "Valheim Battle Royale";

	public const string VERSION = "1.1.1";

	internal static ValheimBattleRoyalePlugin Instance;

	internal static ManualLogSource Log;

	internal ConfigEntry<KeyboardShortcut> StartHotkey;

	internal ConfigEntry<KeyboardShortcut> PlaceZoneHotkey;

	internal ConfigEntry<KeyboardShortcut> CancelZoneHotkey;

	internal ConfigEntry<float> StartCountdown;

	internal ConfigEntry<float> InitialRadius;

	internal ConfigEntry<float> NextRadiusMultiplier;

	internal ConfigEntry<float> MinimumRadius;

	internal ConfigEntry<float> WaitBeforeShrink;

	internal ConfigEntry<float> ShrinkDuration;

	internal ConfigEntry<float> OutsideDamage;

	internal ConfigEntry<float> DamageInterval;

	internal ConfigEntry<bool> RequireNextInsideCurrent;

	internal ConfigEntry<bool> ShowHud;

	internal ConfigEntry<float> OutsideWarningTopPercent;

	internal ConfigEntry<float> OutsideWarningFontSize;

	private Harmony _harmony;

	private FileSystemWatcher _watcher;

	private int _reloadPending;

	private double _reloadAt = -1.0;

	private double _nextServerDamageAt;

	private double _nextVisualAt;

	private double _nextSyncAt;

	private double _nextSnapshotAt;

	private double _lastStateAt;

	private double _clockAtReceipt;

	private double _serverClockAtReceipt;

	private double _nextHudAttempt;

	private double _nextFontProbe;

	private ZNet _network;

	private ZRoutedRpc _registeredRouter;

	private long _serverPeerId;

	private long _revision;

	private long _snapshotSerial;

	private long _lastSnapshotSerial;

	private long _matchId;

	private long _damageSequence;

	private long _lastDamageSequence;

	private string _session;

	private bool _hasSnapshot;

	private bool _snapshotNeeded;

	private GameObject _hudRoot;

	private TextMeshProUGUI _hudTitle;

	private TextMeshProUGUI _hudTimer;

	private TextMeshProUGUI _hudDanger;

	private Player _pvpPlayer;

	private bool _pvpCaptured;

	private bool _previousPvp;

	private bool _pvpNoticeShown;

	private ConfigEntry<string> ExemptSteamIds;

	private string _exemptConfig;

	private readonly HashSet<string> _exemptIds = new HashSet<string>(StringComparer.Ordinal);

	private bool _localExempt;

	private string _lastHudStatus;

	private double _nextPresenceCheck;

	private readonly Dictionary<long, ZonePresence> _presence = new Dictionary<long, ZonePresence>();

	private ConfigEntry<bool> DiagnosticLogging;

	private ConfigEntry<float> DiagnosticInterval;

	private readonly Dictionary<string, double> _logTimes = new Dictionary<string, double>();

	private readonly Dictionary<string, double> _rpcTimes = new Dictionary<string, double>();

	private readonly Dictionary<long, PeerDiagnostics> _peers = new Dictionary<long, PeerDiagnostics>();

	private static TMP_FontAsset _valheimNorseBold;

	private static string _selectedLanguage;

	private static float _nextLanguageProbeAt;

	private static readonly FieldInfo AdminListField = AccessTools.Field(typeof(ZNet), "m_adminList");

	private static readonly MethodInfo AdminContains = AccessTools.Method(typeof(ZNet), "ListContainsId", new Type[2]
	{
		typeof(SyncedList),
		typeof(string)
	}, (Type[])null);

	private static readonly MethodInfo CheckDeath = AccessTools.Method(typeof(Character), "CheckDeath", Type.EmptyTypes, (Type[])null);

	private static readonly FieldInfo LastHit = AccessTools.Field(typeof(Character), "m_lastHit");

	private static readonly Type LocalizationType = Type.GetType("Localization, assembly_guiutils", throwOnError: false);

	private static readonly MethodInfo SelectedLanguage = ((LocalizationType == null) ? null : AccessTools.Method(LocalizationType, "GetSelectedLanguage", Type.EmptyTypes, (Type[])null));

	private static readonly PropertyInfo LocalizationInstance = ((LocalizationType == null) ? null : AccessTools.Property(LocalizationType, "instance"));

	private const int Protocol = 111;

	private const string RpcRequest = "XBR111_Request";

	private const string RpcState = "XBR111_State";

	private const string RpcSnapshotRequest = "XBR111_SnapshotRequest";

	private const string RpcStateAck = "XBR111_StateAck";

	private const string RpcZoneDamage = "XBR111_ZoneDamage";

	private const string RpcDamageAck = "XBR111_DamageAck";

	internal bool _restoringPvp;

	private static StatusEffect _zoneFireVisual;

	private double DiagnosticSeconds => SafeNumber(DiagnosticInterval.Value, 30f, 5f, 300f);

	private static double LocalClock()
	{
		return Time.realtimeSinceStartupAsDouble;
	}

	internal void Diagnose(string key, string message, bool warning = false, double interval = -1.0)
	{
		bool flag = key.StartsWith("damage-tx-", StringComparison.Ordinal) || key.StartsWith("damage-rx-", StringComparison.Ordinal) || key.StartsWith("damage-ack-", StringComparison.Ordinal) || key == "damage-local";
		if (flag && !warning)
		{
			interval = (DiagnosticLogging.Value ? 5 : 30);
		}
		if (!warning && !DiagnosticLogging.Value && !flag)
		{
			return;
		}
		if (interval < 0.0)
		{
			interval = (warning ? 10.0 : DiagnosticSeconds);
		}
		if (Allow(_logTimes, key, interval))
		{
			string text = (((Object)(object)ZNet.instance == (Object)null) ? "offline" : (ZNet.instance.IsServer() ? "server" : "client"));
			string text2 = "[BR111 " + text + "] " + message;
			if (warning)
			{
				Log.LogWarning((object)text2);
			}
			else
			{
				Log.LogInfo((object)text2);
			}
		}
	}

	private static bool Allow(Dictionary<string, double> times, string key, double interval)
	{
		double num = LocalClock();
		if (times.TryGetValue(key, out var value) && num < value)
		{
			return false;
		}
		if (times.Count >= 512 && !times.ContainsKey(key))
		{
			return false;
		}
		times[key] = num + interval;
		return true;
	}

	internal static bool Finite(float v)
	{
		if (!float.IsNaN(v))
		{
			return !float.IsInfinity(v);
		}
		return false;
	}

	internal static bool Finite(double v)
	{
		if (!double.IsNaN(v))
		{
			return !double.IsInfinity(v);
		}
		return false;
	}

	internal static bool Finite(Vector3 v)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		if (Finite(v.x) && Finite(v.y))
		{
			return Finite(v.z);
		}
		return false;
	}

	private static float SafeNumber(float value, float fallback, float min, float max)
	{
		return Mathf.Clamp(Finite(value) ? value : fallback, min, max);
	}

	internal static bool SkipDamageForHealth(bool dead, bool hasHealth, float health)
	{
		if (!dead)
		{
			if (hasHealth)
			{
				if (Finite(health))
				{
					return health <= 0f;
				}
				return true;
			}
			return false;
		}
		return true;
	}

	internal static bool IsBattleRoyaleMethod(int hash)
	{
		if (hash != StringExtensionMethods.GetStableHashCode("XBR111_Request") && hash != StringExtensionMethods.GetStableHashCode("XBR111_State") && hash != StringExtensionMethods.GetStableHashCode("XBR111_SnapshotRequest") && hash != StringExtensionMethods.GetStableHashCode("XBR111_StateAck") && hash != StringExtensionMethods.GetStableHashCode("XBR111_ZoneDamage"))
		{
			return hash == StringExtensionMethods.GetStableHashCode("XBR111_DamageAck");
		}
		return true;
	}

	internal static bool AcceptIngress(bool server, bool ready, bool serverConnection, long actualPeer, long claimedPeer, long target, long local, bool globalTarget, int method)
	{
		if (!ready || actualPeer != claimedPeer || target != local || !globalTarget)
		{
			return false;
		}
		if (server)
		{
			if (method != StringExtensionMethods.GetStableHashCode("XBR111_Request") && method != StringExtensionMethods.GetStableHashCode("XBR111_SnapshotRequest") && method != StringExtensionMethods.GetStableHashCode("XBR111_StateAck"))
			{
				return method == StringExtensionMethods.GetStableHashCode("XBR111_DamageAck");
			}
			return true;
		}
		if (serverConnection)
		{
			if (method != StringExtensionMethods.GetStableHashCode("XBR111_State"))
			{
				return method == StringExtensionMethods.GetStableHashCode("XBR111_ZoneDamage");
			}
			return true;
		}
		return false;
	}

	internal bool ValidateIngress(ZRpc transport, ZPackage package)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		if (package == null)
		{
			return false;
		}
		int pos = package.GetPos();
		try
		{
			package.ReadLong();
			long claimedPeer = package.ReadLong();
			long target = package.ReadLong();
			ZDOID val = package.ReadZDOID();
			int num = package.ReadInt();
			if (!IsBattleRoyaleMethod(num))
			{
				return true;
			}
			ZNet instance = ZNet.instance;
			ZNetPeer val2 = null;
			if ((Object)(object)instance != (Object)null && transport != null)
			{
				foreach (ZNetPeer peer in instance.GetPeers())
				{
					if (peer.m_rpc == transport)
					{
						val2 = peer;
						break;
					}
				}
			}
			int num2;
			if ((Object)(object)instance != (Object)null && val2 != null)
			{
				num2 = (AcceptIngress(instance.IsServer(), val2.IsReady(), !instance.IsServer() && val2 == instance.GetServerPeer(), val2.m_uid, claimedPeer, target, ZNet.GetUID(), ((ZDOID)(ref val)).IsNone(), num) ? 1 : 0);
				if (num2 != 0)
				{
					goto IL_0121;
				}
			}
			else
			{
				num2 = 0;
			}
			Diagnose("rpc-ingress-rejected", "Rejected BR RPC: transport identity, direction or target mismatch; claimed=" + claimedPeer + " method=" + num, warning: true, 10.0);
			goto IL_0121;
			IL_0121:
			return (byte)num2 != 0;
		}
		catch (Exception ex)
		{
			Diagnose("rpc-header-invalid", "Rejected malformed routed RPC header: " + ex.GetType().Name, warning: true, 10.0);
			return false;
		}
		finally
		{
			package.SetPos(pos);
		}
	}

	private bool IsServerSender(long sender)
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return false;
		}
		if (ZNet.instance.IsServer())
		{
			return sender == ZNet.GetUID();
		}
		ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
		if (serverPeer != null && serverPeer.IsReady())
		{
			return sender == serverPeer.m_uid;
		}
		return false;
	}

	private bool IsConnectedPeer(long sender)
	{
		ZNetPeer val = (((Object)(object)ZNet.instance == (Object)null) ? null : ZNet.instance.GetPeer(sender));
		if (val != null)
		{
			return val.IsReady();
		}
		return false;
	}

	private void Awake()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		BindConfig();
		SetupConfigWatcher();
		_harmony = new Harmony("com.xtasy.ValheimBattleRoyale");
		_harmony.PatchAll();
		Log.LogInfo((object)"Valheim Battle Royale 1.1.1 loaded.");
	}

	private void OnDestroy()
	{
		if ((Object)(object)_hudRoot != (Object)null)
		{
			Object.Destroy((Object)(object)_hudRoot);
			_hudRoot = null;
		}
		RestorePvp(discardIfUnavailable: true);
		MinimapUpdatePatch.Cleanup();
		if ((Object)(object)_zoneFireVisual != (Object)null)
		{
			Object.Destroy((Object)(object)_zoneFireVisual);
		}
		_zoneFireVisual = null;
		if (_watcher != null)
		{
			_watcher.Dispose();
			_watcher = null;
		}
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
		Instance = null;
	}

	private void BindConfig()
	{
		//IL_001b: 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_008b: 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_0267: Expected O, but got Unknown
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Expected O, but got Unknown
		StartHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "StartHotkey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Start/stop match request from an admin client.");
		PlaceZoneHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "PlaceZoneHotkey", new KeyboardShortcut((KeyCode)323, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Place or move a zone on the large map. Mouse0 = left mouse button; Mouse1 = right mouse button; Mouse2 = middle mouse button (wheel click). Example: LeftShift + Mouse0.");
		CancelZoneHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "CancelZoneHotkey", new KeyboardShortcut((KeyCode)323, (KeyCode[])(object)new KeyCode[1] { (KeyCode)306 }), "Cancel the next/current zone on the large map. Mouse0 = left mouse button; Mouse1 = right mouse button; Mouse2 = middle mouse button (wheel click). Example: LeftControl + Mouse0.");
		StartCountdown = ((BaseUnityPlugin)this).Config.Bind<float>("Match", "StartCountdown", 10f, "Seconds between admin start and actual match start. Damage is disabled during this countdown.");
		InitialRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "InitialRadius", 3000f, "Initial safe-zone radius used by Shift+LeftClick / br zone when radius is omitted.");
		NextRadiusMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "NextRadiusMultiplier", 0.5f, "Next-zone radius as a multiplier of the current radius. 0.5 = 50%. Used when an explicit radius is not supplied.");
		MinimumRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "MinimumRadius", 25f, "Minimum automatically calculated next-zone radius.");
		WaitBeforeShrink = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "WaitBeforeShrink", 300f, "Seconds from assigning the next safe zone until shrinking starts.");
		ShrinkDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "ShrinkDuration", 180f, "Seconds used to move/shrink from current zone to next zone.");
		RequireNextInsideCurrent = ((BaseUnityPlugin)this).Config.Bind<bool>("Zone", "RequireNextZoneInsideCurrent", true, "Reject a next circle that is not fully inside the current circle.");
		OutsideDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "OutsideDamage", 10f, "Direct health removed per tick outside the current safe zone. Bypasses armor, blocking and elemental resistances.");
		DamageInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "DamageInterval", 2f, "Seconds between damage ticks.");
		ExemptSteamIds = ((BaseUnityPlugin)this).Config.Bind<string>("Administration", "ExemptSteamIds", "", "Server-only comma-separated SteamID64 list. Listed players are exempt from zone damage, forced PvP and personal zone effects. Does not grant admin permissions.");
		ShowHud = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowHud", true, "Show match/zone/timer information on screen.");
		OutsideWarningTopPercent = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "OutsideWarningTopPercent", 20f, new ConfigDescription("Outside-zone warning center measured from the top as a percentage of screen height (0-100). Clamped to keep the text on screen. Client-local setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		OutsideWarningFontSize = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "OutsideWarningFontSize", 25f, new ConfigDescription("Outside-zone warning font size (12-100). Client-local setting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(12f, 100f), Array.Empty<object>()));
		DiagnosticLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Enabled", true, "Log RPC, zone, HUD and damage summaries. Warnings remain enabled.");
		DiagnosticInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Diagnostics", "SummaryInterval", 30f, "Seconds between repeated diagnostic summaries (5..300). Warnings are limited separately.");
	}

	private void SetupConfigWatcher()
	{
		try
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
			_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath) ?? Paths.ConfigPath, Path.GetFileName(configFilePath));
			_watcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime;
			_watcher.Changed += delegate
			{
				Interlocked.Exchange(ref _reloadPending, 1);
			};
			_watcher.Created += delegate
			{
				Interlocked.Exchange(ref _reloadPending, 1);
			};
			_watcher.Renamed += delegate
			{
				Interlocked.Exchange(ref _reloadPending, 1);
			};
			_watcher.EnableRaisingEvents = true;
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Config watcher unavailable: " + ex));
		}
	}

	private void Update()
	{
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		if (Interlocked.Exchange(ref _reloadPending, 0) != 0)
		{
			_reloadAt = LocalClock() + 0.35;
		}
		if (_reloadAt > 0.0 && LocalClock() >= _reloadAt)
		{
			_reloadAt = -1.0;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					SyncServerSettings();
					BroadcastState();
				}
				Diagnose("config", "Configuration reloaded.");
			}
			catch (Exception ex)
			{
				Diagnose("config-error", "Config reload failed: " + ex, warning: true);
			}
		}
		try
		{
			MaintainNetwork();
		}
		catch (Exception ex2)
		{
			Diagnose("network-error", "Network update failed: " + ex2, warning: true);
		}
		if ((Object)(object)ZNet.instance != (Object)null && ZRoutedRpc.instance != null)
		{
			try
			{
				if (ZNet.instance.IsServer() && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					SetLocalExemption(IsExempt(ZNet.GetUID()));
				}
				UpdatePvp();
				UpdateZonePresence();
				if ((Object)(object)Player.m_localPlayer != (Object)null && !IsTextInputActive())
				{
					KeyboardShortcut value = StartHotkey.Value;
					if (((KeyboardShortcut)(ref value)).IsDown())
					{
						SendRequest("toggle");
					}
				}
				if (ZNet.instance.IsServer())
				{
					ApplyServerDamage();
				}
				ApplyLocalZoneVisual();
			}
			catch (Exception ex3)
			{
				Diagnose("gameplay-error", "Gameplay update failed: " + ex3, warning: true);
			}
		}
		try
		{
			UpdateTmpHud();
		}
		catch (Exception ex4)
		{
			Diagnose("hud-error", "HUD update failed; recreation scheduled: " + ex4, warning: true);
			DestroyHud();
			_nextHudAttempt = LocalClock() + 1.0;
		}
	}

	private void ResetSession(bool resetRouter = true)
	{
		RestorePvp(discardIfUnavailable: true);
		ZoneState.Reset();
		if (resetRouter)
		{
			_registeredRouter = null;
		}
		_serverPeerId = (_revision = (_snapshotSerial = (_lastSnapshotSerial = (_matchId = 0L))));
		_damageSequence = (_lastDamageSequence = 0L);
		_localExempt = false;
		_hasSnapshot = false;
		_snapshotNeeded = true;
		_session = null;
		_nextSyncAt = (_nextSnapshotAt = (_nextServerDamageAt = (_nextVisualAt = (_lastStateAt = 0.0))));
		_peers.Clear();
		_rpcTimes.Clear();
		_logTimes.Clear();
		_presence.Clear();
		_nextPresenceCheck = 0.0;
		_valheimNorseBold = null;
		_nextFontProbe = 0.0;
		_lastHudStatus = null;
		DestroyHud();
		if ((Object)(object)_zoneFireVisual != (Object)null)
		{
			Object.Destroy((Object)(object)_zoneFireVisual);
		}
		_zoneFireVisual = null;
	}

	private unsafe void MaintainNetwork()
	{
		//IL_0242: Unknown result type (might be due to invalid IL or missing references)
		//IL_0247: Unknown result type (might be due to invalid IL or missing references)
		ZNet instance = ZNet.instance;
		ZRoutedRpc instance2 = ZRoutedRpc.instance;
		if (_network != instance || (_registeredRouter != null && _registeredRouter != instance2))
		{
			ResetSession(_registeredRouter != instance2);
			_network = instance;
			if ((Object)(object)instance != (Object)null && instance.IsServer())
			{
				_session = Guid.NewGuid().ToString("N");
				_hasSnapshot = true;
				SyncServerSettings();
			}
		}
		if ((Object)(object)instance == (Object)null || instance2 == null)
		{
			return;
		}
		if (_registeredRouter != instance2)
		{
			instance2.Register<ZPackage>("XBR111_Request", (Action<long, ZPackage>)RPC_Request);
			instance2.Register<ZPackage>("XBR111_State", (Action<long, ZPackage>)RPC_State);
			instance2.Register("XBR111_SnapshotRequest", (Action<long>)RPC_SnapshotRequest);
			instance2.Register<ZPackage>("XBR111_StateAck", (Action<long, ZPackage>)RPC_StateAck);
			instance2.Register<ZPackage>("XBR111_ZoneDamage", (Action<long, ZPackage>)RPC_ZoneDamage);
			instance2.Register<ZPackage>("XBR111_DamageAck", (Action<long, ZPackage>)RPC_DamageAck);
			_registeredRouter = instance2;
			Diagnose("registered", "RPC registered; protocol=111 uid=" + ZNet.GetUID(), warning: false, 0.0);
		}
		double num = LocalClock();
		if (instance.IsServer())
		{
			if (_session == null)
			{
				_session = Guid.NewGuid().ToString("N");
				_hasSnapshot = true;
				SyncServerSettings();
			}
			if (num < _nextSyncAt)
			{
				return;
			}
			_nextSyncAt = num + 5.0;
			HashSet<long> hashSet = new HashSet<long>();
			foreach (ZNetPeer peer in instance.GetPeers())
			{
				if (peer.IsReady())
				{
					hashSet.Add(peer.m_uid);
					if (!_peers.TryGetValue(peer.m_uid, out var value))
					{
						value = new PeerDiagnostics();
						_peers[peer.m_uid] = value;
						string key = "join-" + peer.m_uid;
						string text = peer.m_uid.ToString();
						ZDOID characterID = peer.m_characterID;
						Diagnose(key, "Ready peer=" + text + " character=" + ((object)(*(ZDOID*)(&characterID))/*cast due to .constrained prefix*/).ToString(), warning: false, 0.0);
					}
					SendState(peer.m_uid);
					if ((value.AckRevision < 0 && num - value.FirstSeenAt > 15.0) || (value.AckRevision >= 0 && num - value.LastStateAckAt > 15.0))
					{
						Diagnose("sync-peer-" + peer.m_uid, "No recent state ACK peer=" + peer.m_uid + "; check that both sides run 1.1.1.", warning: true, 30.0);
					}
				}
			}
			foreach (long item in new List<long>(_peers.Keys))
			{
				if (hashSet.Contains(item))
				{
					continue;
				}
				_peers.Remove(item);
				foreach (string item2 in new List<string>(_rpcTimes.Keys))
				{
					if (item2.EndsWith("-" + item, StringComparison.Ordinal))
					{
						_rpcTimes.Remove(item2);
					}
				}
				foreach (string item3 in new List<string>(_logTimes.Keys))
				{
					if (item3.EndsWith("-" + item, StringComparison.Ordinal))
					{
						_logTimes.Remove(item3);
					}
				}
			}
			Diagnose("server-summary", "Zone rev=" + _revision + " peers=" + hashSet.Count + " " + ZoneState.Describe());
			return;
		}
		ZNetPeer serverPeer = instance.GetServerPeer();
		long num2 = ((serverPeer != null && serverPeer.IsReady()) ? serverPeer.m_uid : 0);
		if (_serverPeerId != num2)
		{
			ResetSession(resetRouter: false);
			_serverPeerId = num2;
			return;
		}
		if (num2 == 0L)
		{
			Diagnose("waiting-server", "Waiting for ready server peer.");
			return;
		}
		if (num >= _nextSnapshotAt && (!_hasSnapshot || _snapshotNeeded || num - _lastStateAt > 12.0))
		{
			_nextSnapshotAt = num + 3.0;
			instance2.InvokeRoutedRPC(num2, "XBR111_SnapshotRequest", Array.Empty<object>());
			Diagnose("snapshot-request", "Snapshot requested from=" + num2 + " received=" + _hasSnapshot);
		}
		if (_hasSnapshot && num - _lastStateAt > 15.0)
		{
			Diagnose("stale-state", "State heartbeat stale age=" + (num - _lastStateAt).ToString("F1") + "s; requesting resync.", warning: true, 15.0);
		}
	}

	private void SyncServerSettings()
	{
		ZoneState.DamageAmount = SafeNumber(OutsideDamage.Value, 10f, 0f, 100000f);
		ZoneState.DamageTick = SafeNumber(DamageInterval.Value, 2f, 0.1f, 3600f);
		if (ZoneState.Phase == ZonePhase.Waiting)
		{
			ZoneState.WaitSecondsAtAssignment = SafeNumber(WaitBeforeShrink.Value, 300f, 0f, 86400f);
		}
	}

	internal bool ForcePvp(Player player)
	{
		if ((Object)(object)player == (Object)(object)Player.m_localPlayer && ZoneState.Running && _hasSnapshot)
		{
			return !_localExempt;
		}
		return false;
	}

	private void CapturePvp(Player player)
	{
		if (!_pvpCaptured || !((Object)(object)_pvpPlayer == (Object)(object)player))
		{
			if (!_pvpCaptured)
			{
				_previousPvp = ((Character)player).IsPVPEnabled();
				_pvpCaptured = true;
				_pvpNoticeShown = false;
			}
			_pvpPlayer = player;
		}
	}

	internal void GuardPvp(Player player, ref bool pvp)
	{
		if (ForcePvp(player) && ((Character)player).IsOwner())
		{
			CapturePvp(player);
			if (!pvp)
			{
				Diagnose("pvp-block", "Prevented disabling PvP during the match.");
			}
			pvp = true;
		}
	}

	private void UpdatePvp()
	{
		Player localPlayer = Player.m_localPlayer;
		if (_pvpCaptured && (!ZoneState.Running || _localExempt))
		{
			RestorePvp();
		}
		if (!((Object)(object)localPlayer == (Object)null) && ((Character)localPlayer).IsOwner() && ForcePvp(localPlayer))
		{
			CapturePvp(localPlayer);
			if (!((Character)localPlayer).IsPVPEnabled())
			{
				localPlayer.SetPVP(true);
				Diagnose("pvp-on", "PvP enabled for local player.");
			}
		}
	}

	private void RestorePvp(bool discardIfUnavailable = false)
	{
		if (!_pvpCaptured)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).IsOwner() || ((Character)localPlayer).IsDead())
		{
			if (discardIfUnavailable)
			{
				_pvpCaptured = false;
				_pvpPlayer = null;
			}
			return;
		}
		_restoringPvp = true;
		try
		{
			if (((Character)localPlayer).IsPVPEnabled() != _previousPvp)
			{
				localPlayer.SetPVP(_previousPvp);
			}
			_pvpCaptured = false;
			_pvpPlayer = null;
		}
		finally
		{
			_restoringPvp = false;
		}
	}

	internal bool AllowPvpNotice(Character character, string message)
	{
		if (_localExempt || _restoringPvp || (Object)(object)character != (Object)(object)Player.m_localPlayer || !ZoneState.Running || !_hasSnapshot)
		{
			return true;
		}
		if (message == "$msg_pvpoff")
		{
			return false;
		}
		if (message != "$msg_pvpon")
		{
			return true;
		}
		if (_pvpNoticeShown)
		{
			Diagnose("pvp-notice", "Suppressed repeated PvP notification.");
			return false;
		}
		_pvpNoticeShown = true;
		return true;
	}

	internal static bool IsTextInputActive()
	{
		if ((Object)(object)Console.instance != (Object)null && Console.IsVisible())
		{
			return true;
		}
		if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
		{
			return true;
		}
		if (!TextInput.IsVisible() && !Minimap.InTextInput() && !Menu.IsActive() && !InventoryGui.IsVisible())
		{
			return ZInput.VirtualKeyboardOpen;
		}
		return true;
	}

	internal void SendRequest(string action, Vector3 pos = default(Vector3), float radius = 0f)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Expected O, but got Unknown
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
		{
			return;
		}
		if (!Finite(pos) || !Finite(radius))
		{
			Diagnose("bad-request", "Invalid control coordinates/radius.", warning: true);
			return;
		}
		ZPackage val = new ZPackage();
		val.Write(action);
		val.Write(pos);
		val.Write(radius);
		if (ZNet.instance.IsServer())
		{
			val.SetPos(0);
			RPC_Request(ZNet.GetUID(), val);
			return;
		}
		ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
		if (serverPeer == null || !serverPeer.IsReady())
		{
			Diagnose("request-no-server", "Control request skipped: server not ready.", warning: true);
			return;
		}
		Diagnose("request-send", "Control TX action=" + action + " to=" + serverPeer.m_uid, warning: false, 0.25);
		ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "XBR111_Request", new object[1] { val });
	}

	private unsafe void RPC_Request(long sender, ZPackage pkg)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		if (!IsAuthorized(sender))
		{
			Diagnose("unauthorized", "Rejected non-admin control RPC sender=" + sender, warning: true);
		}
		else
		{
			if (!Allow(_rpcTimes, "control-" + sender, 0.25))
			{
				return;
			}
			try
			{
				if (pkg == null || pkg.Size() > 256)
				{
					throw new InvalidDataException("control size");
				}
				string text = pkg.ReadString();
				Vector3 val = pkg.ReadVector3();
				float num = pkg.ReadSingle();
				if (!Finite(val) || !Finite(num) || Mathf.Abs(val.x) > 100000f || Mathf.Abs(val.z) > 100000f || num > 100000f)
				{
					throw new InvalidDataException("invalid coordinates/radius");
				}
				string[] obj = new string[8]
				{
					"Control RX sender=",
					sender.ToString(),
					" action=",
					text,
					" pos=",
					null,
					null,
					null
				};
				Vector3 val2 = val;
				obj[5] = ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString();
				obj[6] = " radius=";
				obj[7] = num.ToString();
				Diagnose("control", string.Concat(obj), warning: false, 0.25);
				switch (text)
				{
				case "toggle":
					if (ZoneState.Running || ZoneState.Starting)
					{
						StopMatch();
					}
					else
					{
						StartMatch();
					}
					break;
				case "start":
					StartMatch();
					break;
				case "stop":
					StopMatch();
					break;
				case "zone":
					SetCurrentZone(val, num);
					break;
				case "next":
					SetNextZone(val, num);
					break;
				case "cancelnext":
					CancelNextZone();
					break;
				case "cancelzone":
					CancelAssignedZone();
					break;
				default:
					Diagnose("unknown-control", "Unknown control action.", warning: true);
					return;
				}
				BroadcastState();
			}
			catch (Exception ex)
			{
				Diagnose("control-error", "Control RPC failed: " + ex, warning: true);
			}
		}
	}

	private void CancelNextZone()
	{
		if (ZoneState.Phase == ZonePhase.Shrinking)
		{
			Diagnose("cancel-shrinking", "Cannot cancel during shrinking.", warning: true);
			return;
		}
		ZoneState.HasNext = false;
		ZoneState.Phase = ZonePhase.Idle;
	}

	private void StartMatch()
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		if (ZoneState.Running || ZoneState.Starting)
		{
			return;
		}
		if (!ZoneState.HasCurrent)
		{
			Log.LogWarning((object)"Cannot start: current safe zone is not set.");
			return;
		}
		_matchId++;
		_damageSequence = 0L;
		foreach (PeerDiagnostics value in _peers.Values)
		{
			value.LastDamageSent = (value.LastDamageAck = 0L);
			value.FirstUnackedAt = -1.0;
			value.Character = ZDOID.None;
		}
		ZoneState.Starting = true;
		ZoneState.MatchStartAt = ServerTime() + (double)SafeNumber(StartCountdown.Value, 10f, 0f, 86400f);
		SyncServerSettings();
		Log.LogInfo((object)("Battle Royale countdown started; match=" + _matchId));
	}

	private void StopMatch()
	{
		//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)
		ZoneState.CurrentCenter = GetEffectiveCurrentCenter();
		ZoneState.CurrentRadius = GetEffectiveCurrentRadius();
		ZoneState.Running = (ZoneState.Starting = false);
		ZoneState.Phase = ZonePhase.Idle;
		_matchId++;
		RestorePvp();
		Log.LogInfo((object)"Battle Royale stopped.");
	}

	internal static string NormalizeSteamId(string value)
	{
		if (value == null)
		{
			return null;
		}
		value = value.Trim();
		if (value.StartsWith("Steam_", StringComparison.OrdinalIgnoreCase))
		{
			value = value.Substring(6);
		}
		if (value.Length != 17)
		{
			return null;
		}
		string text = value;
		foreach (char c in text)
		{
			if (c < '0' || c > '9')
			{
				return null;
			}
		}
		if (!ulong.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result == 0L)
		{
			return null;
		}
		return value;
	}

	private bool IsExempt(long peerId)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return false;
		}
		string text = ExemptSteamIds.Value ?? "";
		if (text != _exemptConfig)
		{
			_exemptIds.Clear();
			_exemptConfig = text;
			string[] array = text.Split(new char[1] { ',' });
			foreach (string value in array)
			{
				string text2 = NormalizeSteamId(value);
				if (text2 != null)
				{
					_exemptIds.Add(text2);
				}
				else if (!string.IsNullOrWhiteSpace(value))
				{
					Diagnose("exempt-invalid", "Ignored invalid ExemptSteamIds entry; use comma-separated SteamID64 values.", warning: true, 30.0);
				}
			}
		}
		if (_exemptIds.Count == 0)
		{
			return false;
		}
		try
		{
			string value4;
			if (peerId == ZNet.GetUID())
			{
				object value2 = Type.GetType("Splatform.PlatformManager, Splatform", throwOnError: true).GetProperty("DistributionPlatform").GetValue(null, null);
				object value3 = Type.GetType("Splatform.IDistributionPlatform, Splatform", throwOnError: true).GetProperty("LocalUser").GetValue(value2, null);
				value4 = Type.GetType("Splatform.IUser, Splatform", throwOnError: true).GetProperty("PlatformUserID").GetValue(value3, null)?.ToString();
			}
			else
			{
				ZNetPeer peer = ZNet.instance.GetPeer(peerId);
				object obj;
				if (peer == null)
				{
					obj = null;
				}
				else
				{
					ZRpc rpc = peer.m_rpc;
					if (rpc == null)
					{
						obj = null;
					}
					else
					{
						ISocket socket = rpc.GetSocket();
						obj = ((socket != null) ? socket.GetHostName() : null);
					}
				}
				value4 = (string)obj;
			}
			string text3 = NormalizeSteamId(value4);
			return text3 != null && _exemptIds.Contains(text3);
		}
		catch (Exception ex)
		{
			Diagnose("exempt-identity", "Could not verify exemption identity: " + ex.GetType().Name, warning: true, 30.0);
			return false;
		}
	}

	private void SetLocalExemption(bool exempt)
	{
		if (_localExempt == exempt)
		{
			return;
		}
		_localExempt = exempt;
		Log.LogInfo((object)("[BR111] Server-authorized local exemption=" + exempt));
		if (exempt)
		{
			RestorePvp();
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && ((Character)localPlayer).GetSEMan() != null)
			{
				((Character)localPlayer).GetSEMan().RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("BR_ZoneFireVisual"), false);
			}
		}
	}

	private bool IsAuthorized(long sender)
	{
		if (sender == ZNet.GetUID())
		{
			return true;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(sender);
		if (peer == null || !peer.IsReady() || peer.m_rpc == null || peer.m_rpc.GetSocket() == null)
		{
			return false;
		}
		try
		{
			object obj = ((AdminListField == null) ? null : AdminListField.GetValue(ZNet.instance));
			if (obj == null || AdminContains == null)
			{
				return false;
			}
			return (bool)AdminContains.Invoke(ZNet.instance, new object[2]
			{
				obj,
				peer.m_rpc.GetSocket().GetHostName()
			});
		}
		catch (Exception ex)
		{
			Diagnose("admin-api", "Admin check failed: " + ex, warning: true);
			return false;
		}
	}

	private void CancelAssignedZone()
	{
		if (ZoneState.Phase == ZonePhase.Shrinking)
		{
			Log.LogWarning((object)"Cannot cancel a zone while shrinking is in progress.");
		}
		else if (ZoneState.HasNext)
		{
			ZoneState.HasNext = false;
			ZoneState.Phase = ZonePhase.Idle;
			Log.LogInfo((object)"Next zone cancelled.");
		}
		else if (ZoneState.HasCurrent)
		{
			ZoneState.HasCurrent = false;
			ZoneState.HasNext = false;
			ZoneState.Phase = ZonePhase.Idle;
			ZoneState.Running = false;
			ZoneState.Starting = false;
			Log.LogInfo((object)"Current/start zone cancelled.");
		}
	}

	private void SetCurrentZone(Vector3 pos, float radius)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if (radius <= 0f)
		{
			radius = SafeNumber(InitialRadius.Value, 3000f, 1f, 100000f);
		}
		ZoneState.CurrentCenter = Flat(pos);
		ZoneState.CurrentRadius = Mathf.Max(1f, radius);
		ZoneState.HasCurrent = true;
		ZoneState.HasNext = false;
		ZoneState.Phase = ZonePhase.Idle;
		Log.LogInfo((object)$"Current zone: X={pos.x:F0} Z={pos.z:F0} R={radius:F0}");
	}

	private void SetNextZone(Vector3 pos, float radius)
	{
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: 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_00fb: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		if (!ZoneState.HasCurrent)
		{
			Log.LogWarning((object)"Set current zone first.");
			return;
		}
		if (ZoneState.Phase == ZonePhase.Shrinking)
		{
			Log.LogWarning((object)"Cannot assign or move the next zone while shrinking is in progress.");
			return;
		}
		bool hasNext = ZoneState.HasNext;
		if (radius <= 0f)
		{
			if (hasNext)
			{
				radius = ZoneState.NextRadius;
			}
			else
			{
				float num = SafeNumber(NextRadiusMultiplier.Value, 0.5f, 0.01f, 1f);
				radius = Mathf.Max(SafeNumber(MinimumRadius.Value, 25f, 1f, ZoneState.CurrentRadius), ZoneState.CurrentRadius * num);
			}
		}
		else
		{
			radius = Mathf.Max(1f, radius);
		}
		pos = Flat(pos);
		if (RequireNextInsideCurrent.Value)
		{
			float num2 = DistanceXZ(ZoneState.CurrentCenter, pos);
			if (num2 + radius > ZoneState.CurrentRadius)
			{
				Log.LogWarning((object)$"Next zone rejected: distance {num2:F0} + radius {radius:F0} > current radius {ZoneState.CurrentRadius:F0}.");
				return;
			}
		}
		ZoneState.NextCenter = pos;
		ZoneState.NextRadius = radius;
		ZoneState.HasNext = true;
		ZoneState.Phase = ZonePhase.Waiting;
		ZoneState.PhaseStartedAt = ServerTime();
		ZoneState.WaitSecondsAtAssignment = SafeNumber(WaitBeforeShrink.Value, 300f, 0f, 86400f);
		Log.LogInfo((object)string.Format("Next zone {0}: X={1:F0} Z={2:F0} R={3:F0}; wait={4:F0}s", hasNext ? "moved" : "assigned", pos.x, pos.z, radius, ZoneState.WaitSecondsAtAssignment));
	}

	private void FixedUpdate()
	{
		//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)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ZoneState.HasCurrent)
		{
			return;
		}
		try
		{
			double num = ServerTime();
			if (ZoneState.Starting && num >= ZoneState.MatchStartAt)
			{
				ZoneState.Starting = false;
				ZoneState.Running = true;
				if (ZoneState.HasNext)
				{
					ZoneState.Phase = ZonePhase.Waiting;
					ZoneState.PhaseStartedAt = num;
					ZoneState.WaitSecondsAtAssignment = SafeNumber(WaitBeforeShrink.Value, 300f, 0f, 86400f);
				}
				_nextServerDamageAt = num;
				Log.LogInfo((object)("Battle Royale started; match=" + _matchId));
				BroadcastState();
			}
			if (ZoneState.Running)
			{
				if (ZoneState.Phase == ZonePhase.Waiting && ZoneState.HasNext && num - ZoneState.PhaseStartedAt >= (double)ZoneState.WaitSecondsAtAssignment)
				{
					ZoneState.Phase = ZonePhase.Shrinking;
					ZoneState.PhaseStartedAt = num;
					ZoneState.ShrinkFromCenter = ZoneState.CurrentCenter;
					ZoneState.ShrinkFromRadius = ZoneState.CurrentRadius;
					ZoneState.ShrinkSeconds = SafeNumber(ShrinkDuration.Value, 180f, 0.01f, 86400f);
					BroadcastState();
				}
				else if (ZoneState.Phase == ZonePhase.Shrinking && ZoneState.HasNext && num - ZoneState.PhaseStartedAt >= (double)ZoneState.ShrinkSeconds)
				{
					ZoneState.CurrentCenter = ZoneState.NextCenter;
					ZoneState.CurrentRadius = ZoneState.NextRadius;
					ZoneState.HasNext = false;
					ZoneState.Phase = ZonePhase.Idle;
					BroadcastState();
				}
			}
		}
		catch (Exception ex)
		{
			Diagnose("phase-error", "Zone phase update failed: " + ex, warning: true);
		}
	}

	private void UpdateZonePresence()
	{
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: 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_00df: 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)
		if (!_hasSnapshot || !ZoneState.Running || ZoneState.Starting || !ZoneState.HasCurrent)
		{
			_presence.Clear();
			return;
		}
		double num = LocalClock();
		if (num < _nextPresenceCheck)
		{
			return;
		}
		_nextPresenceCheck = num + 0.5;
		HashSet<long> hashSet = new HashSet<long>();
		if (ZNet.instance.IsServer() && ZDOMan.instance != null)
		{
			float health = default(float);
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!peer.IsReady() || IsExempt(peer.m_uid))
				{
					continue;
				}
				ZDO val = (((ZDOID)(ref peer.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer.m_characterID));
				if (val != null && val.GetOwner() == peer.m_uid && Finite(val.GetPosition()))
				{
					bool hasHealth = val.GetFloat(ZDOVars.s_health, ref health);
					if (!SkipDamageForHealth(val.GetBool(ZDOVars.s_dead, false), hasHealth, health))
					{
						hashSet.Add(peer.m_uid);
						LogZonePresence(peer.m_uid, val.GetPosition(), num);
					}
				}
			}
		}
		Player localPlayer = Player.m_localPlayer;
		if (!_localExempt && (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsOwner() && !((Character)localPlayer).IsDead() && Finite(((Component)localPlayer).transform.position))
		{
			long uID = ZNet.GetUID();
			if (hashSet.Add(uID))
			{
				LogZonePresence(uID, ((Component)localPlayer).transform.position, num);
			}
		}
		foreach (long item in new List<long>(_presence.Keys))
		{
			if (!hashSet.Contains(item))
			{
				_presence.Remove(item);
			}
		}
	}

	private void LogZonePresence(long id, Vector3 position, double now)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		float num = DistanceXZ(position, GetEffectiveCurrentCenter());
		float effectiveCurrentRadius = GetEffectiveCurrentRadius();
		if (!_presence.TryGetValue(id, out var value))
		{
			value = new ZonePresence();
			_presence[id] = value;
		}
		if (value.Observe(num > effectiveCurrentRadius, DiagnosticLogging.Value, now, out var entered))
		{
			string text = (entered ? "Player entered safe zone" : (value.Outside ? "Player outside safe zone" : "Player in safe zone"));
			Log.LogInfo((object)("[BR111 " + (ZNet.instance.IsServer() ? "server" : "client") + "] " + text + " peer=" + id + " distance=" + num.ToString("F1") + " radius=" + effectiveCurrentRadius.ToString("F1")));
		}
	}

	private unsafe void ApplyServerDamage()
	{
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0591: Unknown result type (might be due to invalid IL or missing references)
		//IL_0597: 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)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0370: 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_0398: Unknown result type (might be due to invalid IL or missing references)
		//IL_039f: Expected O, but got Unknown
		//IL_03d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0452: Unknown result type (might be due to invalid IL or missing references)
		//IL_0457: Unknown result type (might be due to invalid IL or missing references)
		if (!ZoneState.Running || ZoneState.Starting || !ZoneState.HasCurrent || ZDOMan.instance == null)
		{
			return;
		}
		double num = ServerTime();
		if (num < _nextServerDamageAt)
		{
			return;
		}
		_nextServerDamageAt = num + (double)ZoneState.DamageTick;
		if (ZoneState.DamageAmount <= 0f)
		{
			return;
		}
		long num2 = ++_damageSequence;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		float health = default(float);
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (!peer.IsReady())
			{
				continue;
			}
			try
			{
				if (IsExempt(peer.m_uid))
				{
					continue;
				}
				ZDO val = (((ZDOID)(ref peer.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer.m_characterID));
				ZDOID characterID;
				if (val == null || val.GetOwner() != peer.m_uid || !Finite(val.GetPosition()))
				{
					num5++;
					string key = "missing-zdo-" + peer.m_uid;
					string[] obj = new string[5]
					{
						"Damage skipped peer=",
						peer.m_uid.ToString(),
						" char=",
						null,
						null
					};
					characterID = peer.m_characterID;
					obj[3] = ((object)(*(ZDOID*)(&characterID))/*cast due to .constrained prefix*/).ToString();
					obj[4] = "; character ZDO/owner not ready.";
					Diagnose(key, string.Concat(obj), warning: true, 30.0);
					continue;
				}
				num3++;
				bool flag = val.GetFloat(ZDOVars.s_health, ref health);
				if (SkipDamageForHealth(val.GetBool(ZDOVars.s_dead, false), flag, health))
				{
					continue;
				}
				if (!flag)
				{
					Diagnose("health-default-" + peer.m_uid, "Character has no explicit health; using native alive/default-health semantics peer=" + peer.m_uid, warning: false, 30.0);
				}
				float num6 = DistanceXZ(val.GetPosition(), GetEffectiveCurrentCenter());
				if (num6 <= GetEffectiveCurrentRadius())
				{
					continue;
				}
				num4++;
				if (!_peers.TryGetValue(peer.m_uid, out var value))
				{
					value = new PeerDiagnostics();
					_peers[peer.m_uid] = value;
				}
				if (value.AckRevision < 0)
				{
					SendState(peer.m_uid);
					Diagnose("damage-handshake-" + peer.m_uid, "Damage deferred peer=" + peer.m_uid + "; waiting for 1.1.1 state ACK.", LocalClock() - value.FirstSeenAt > 15.0, 30.0);
					continue;
				}
				if (value.AckRevision != _revision)
				{
					SendState(peer.m_uid);
				}
				if (value.FirstUnackedAt >= 0.0 && LocalClock() - value.FirstUnackedAt > 10.0)
				{
					Diagnose("damage-no-ack-" + peer.m_uid, "Damage ACK missing peer=" + peer.m_uid + " lastSent=" + value.LastDamageSent + " lastAck=" + value.LastDamageAck, warning: true, 30.0);
				}
				value.LastDamageSent = num2;
				value.Character = peer.m_characterID;
				if (value.FirstUnackedAt < 0.0)
				{
					value.FirstUnackedAt = LocalClock();
				}
				ZPackage val2 = new ZPackage();
				val2.Write(_session);
				val2.Write(_matchId);
				val2.Write(_revision);
				val2.Write(num2);
				val2.Write(peer.m_characterID);
				val2.Write(num);
				val2.Write(ZoneState.DamageAmount);
				ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "XBR111_ZoneDamage", new object[1] { val2 });
				string key2 = "damage-tx-" + peer.m_uid;
				string[] obj2 = new string[14]
				{
					"Damage TX peer=",
					peer.m_uid.ToString(),
					" char=",
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null
				};
				characterID = peer.m_characterID;
				obj2[3] = ((object)(*(ZDOID*)(&characterID))/*cast due to .constrained prefix*/).ToString();
				obj2[4] = " seq=";
				obj2[5] = num2.ToString();
				obj2[6] = " rev=";
				obj2[7] = _revision.ToString();
				obj2[8] = " distance=";
				obj2[9] = num6.ToString("F1");
				obj2[10] = " radius=";
				obj2[11] = GetEffectiveCurrentRadius().ToString("F1");
				obj2[12] = " amount=";
				obj2[13] = ZoneState.DamageAmount.ToString();
				Diagnose(key2, string.Concat(obj2));
			}
			catch (Exception ex)
			{
				Diagnose("damage-peer-" + peer.m_uid, "Peer damage failed: " + ex, warning: true);
			}
		}
		Player localPlayer = Player.m_localPlayer;
		if (!_localExempt && (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsOwner() && !((Character)localPlayer).IsDead() && DistanceXZ(((Component)localPlayer).transform.position, GetEffectiveCurrentCenter()) > GetEffectiveCurrentRadius())
		{
			float before;
			float after;
			string text = ApplyAuthorizedZoneDamage(localPlayer, ZoneState.DamageAmount, out before, out after);
			Diagnose("damage-local", "Listen-server damage=" + text + " hp=" + before + "->" + after, text != "applied");
		}
		Diagnose("damage-summary", "Damage tick=" + num2 + " inspected=" + num3 + " outside=" + num4 + " missing=" + num5);
	}

	private void RPC_ZoneDamage(long sender, ZPackage pkg)
	{
		//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_0296: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: Expected O, but got Unknown
		//IL_02b7: 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_0159: Unknown result type (might be due to invalid IL or missing references)
		if (!IsServerSender(sender))
		{
			Diagnose("forged-damage", "Rejected damage sender=" + sender, warning: true);
			return;
		}
		try
		{
			if (pkg == null || pkg.Size() > 256)
			{
				throw new InvalidDataException("damage size");
			}
			string text = pkg.ReadString();
			long num = pkg.ReadLong();
			long num2 = pkg.ReadLong();
			long num3 = pkg.ReadLong();
			ZDOID val = pkg.ReadZDOID();
			double num4 = pkg.ReadDouble();
			float num5 = pkg.ReadSingle();
			if (!Finite(num4) || !Finite(num5) || num5 <= 0f || num5 > 100000f || num3 <= 0)
			{
				throw new InvalidDataException("damage values");
			}
			string text2 = null;
			float before = -1f;
			float after = -1f;
			Player localPlayer = Player.m_localPlayer;
			if (!_hasSnapshot || text != _session || num != _matchId || num2 > _revision)
			{
				text2 = "state-mismatch";
			}
			else if (!ZoneState.Running || ZoneState.Starting || !ZoneState.HasCurrent)
			{
				text2 = "match-inactive";
			}
			else if (num2 < _revision)
			{
				text2 = "old-revision";
			}
			else if (num3 <= _lastDamageSequence)
			{
				text2 = "duplicate";
			}
			else if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).GetZDOID() != val)
			{
				text2 = "character-not-ready";
			}
			else if (Math.Abs(ServerTime() - num4) > (double)Math.Max(10f, ZoneState.DamageTick * 3f))
			{
				text2 = "expired";
			}
			else
			{
				_lastDamageSequence = num3;
				text2 = ApplyAuthorizedZoneDamage(localPlayer, num5, out before, out after);
				if (text2 == "applied")
				{
					EnsureZoneFireVisual(localPlayer);
				}
			}
			if (text2 == "state-mismatch")
			{
				_snapshotNeeded = true;
				_nextSnapshotAt = 0.0;
			}
			Diagnose("damage-rx-" + text2, "Damage RX sender=" + sender + " seq=" + num3 + " rev=" + num2 + " result=" + text2 + " hp=" + before + "->" + after, text2 != "applied" && text2 != "duplicate");
			ZPackage val2 = new ZPackage();
			val2.Write(text);
			val2.Write(num);
			val2.Write(num3);
			val2.Write(val);
			val2.Write(text2);
			val2.Write(before);
			val2.Write(after);
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "XBR111_DamageAck", new object[1] { val2 });
		}
		catch (Exception ex)
		{
			Diagnose("damage-rpc-error", "Damage RPC failed: " + ex, warning: true);
		}
	}

	private static string ApplyAuthorizedZoneDamage(Player player, float damage, out float before, out float after)
	{
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Expected O, but got Unknown
		//IL_00a2: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
		before = (after = -1f);
		if ((Object)(object)Instance != (Object)null && Instance._localExempt)
		{
			return "exempt";
		}
		if ((Object)(object)player == (Object)null || !((Character)player).IsOwner())
		{
			return "not-owner";
		}
		if (((Character)player).IsDead())
		{
			return "dead";
		}
		before = ((Character)player).GetHealth();
		if (!Finite(before) || before <= 0f)
		{
			return "invalid-health";
		}
		if (damage >= before && (CheckDeath == null || LastHit == null))
		{
			return "death-api-unavailable";
		}
		if (damage >= before)
		{
			HitData val = new HitData();
			val.m_hitType = (HitType)0;
			val.m_damage.m_damage = damage;
			val.m_point = ((Component)player).transform.position;
			LastHit.SetValue(player, val);
		}
		((Character)player).SetHealth(Mathf.Max(0f, before - damage));
		after = ((Character)player).GetHealth();
		if (after >= before)
		{
			return "health-unchanged";
		}
		if (after <= 0f)
		{
			try
			{
				CheckDeath.Invoke(player, null);
			}
			catch (Exception ex)
			{
				Instance?.Diagnose("death-error", "Health reached zero but death pipeline failed: " + ex, warning: true);
				return "death-error";
			}
		}
		return "applied";
	}

	private void RPC_DamageAck(long sender, ZPackage pkg)
	{
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: 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)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !IsConnectedPeer(sender) || !Allow(_rpcTimes, "damage-ack-" + sender, 0.05))
		{
			return;
		}
		try
		{
			if (pkg == null || pkg.Size() > 256)
			{
				throw new InvalidDataException("ack size");
			}
			string text = pkg.ReadString();
			long num = pkg.ReadLong();
			long num2 = pkg.ReadLong();
			ZDOID val = pkg.ReadZDOID();
			string text2 = pkg.ReadString();
			float v = pkg.ReadSingle();
			float v2 = pkg.ReadSingle();
			if (!(text != _session) && num == _matchId && _peers.TryGetValue(sender, out var value) && num2 > value.LastDamageAck && num2 <= value.LastDamageSent && !(val != value.Character))
			{
				if (!Finite(v) || !Finite(v2) || text2.Length > 64)
				{
					throw new InvalidDataException("ack values");
				}
				value.LastDamageAck = num2;
				value.FirstUnackedAt = -1.0;
				Diagnose("damage-ack-" + sender, "Damage ACK peer=" + sender + " seq=" + num2 + " result=" + text2 + " hp=" + v + "->" + v2, text2 != "applied" && text2 != "dead", (text2 == "applied") ? DiagnosticSeconds : 10.0);
				if (text2 == "state-mismatch")
				{
					SendState(sender);
				}
			}
		}
		catch (Exception ex)
		{
			Diagnose("ack-error", "Malformed damage ACK: " + ex, warning: true);
		}
	}

	private void ApplyLocalZoneVisual()
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		Player localPlayer = Player.m_localPlayer;
		if (!_localExempt && ZoneState.Running && !ZoneState.Starting && ZoneState.HasCurrent && !((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && !(LocalClock() < _nextVisualAt))
		{
			_nextVisualAt = LocalClock() + (double)ZoneState.DamageTick;
			if (DistanceXZ(((Component)localPlayer).transform.position, GetEffectiveCurrentCenter()) > GetEffectiveCurrentRadius())
			{
				EnsureZoneFireVisual(localPlayer);
			}
		}
	}

	private static void EnsureZoneFireVisual(Player player)
	{
		if ((Object)(object)player == (Object)null || ((Character)player).GetSEMan() == null || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return;
		}
		try
		{
			if ((Object)(object)_zoneFireVisual == (Object)null)
			{
				StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Burning"));
				if ((Object)(object)statusEffect == (Object)null)
				{
					Instance.Diagnose("fire-missing", "Burning visual unavailable.", warning: true);
					return;
				}
				_zoneFireVisual = ScriptableObject.CreateInstance<StatusEffect>();
				((Object)_zoneFireVisual).name = "BR_ZoneFireVisual";
				_zoneFireVisual.m_name = "";
				_zoneFireVisual.m_tooltip = "";
				_zoneFireVisual.m_ttl = Mathf.Max(0.5f, ZoneState.DamageTick + 0.35f);
				_zoneFireVisual.m_startEffects = statusEffect.m_startEffects;
				_zoneFireVisual.m_stopEffects = statusEffect.m_stopEffects;
			}
			_zoneFireVisual.m_ttl = Mathf.Max(0.5f, ZoneState.DamageTick + 0.35f);
			((Character)player).GetSEMan().AddStatusEffect(_zoneFireVisual, true, 0, 0f, (short)(-1));
		}
		catch (Exception ex)
		{
			Instance.Diagnose("fire-error", "Cosmetic zone fire failed: " + ex.Message, warning: true);
		}
	}

	private void RPC_SnapshotRequest(long sender)
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && IsConnectedPeer(sender) && Allow(_rpcTimes, "snapshot-" + sender, 2.0))
		{
			Diagnose("snapshot-rx-" + sender, "Snapshot request RX peer=" + sender);
			SendState(sender);
		}
	}

	private void BroadcastState()
	{
		_revision++;
		Diagnose("transition", "State transition match=" + _matchId + " rev=" + _revision + " " + ZoneState.Describe(), warning: false, 0.0);
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer.IsReady())
			{
				SendState(peer.m_uid);
			}
		}
	}

	private void SendState(long target)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Expected O, but got Unknown
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null && _session != null)
		{
			ZPackage val = new ZPackage();
			val.Write(111);
			val.Write(_session);
			val.Write(_matchId);
			val.Write(_revision);
			val.Write(++_snapshotSerial);
			val.Write(ServerTime());
			val.Write(IsExempt(target));
			ZoneState.Write(val);
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "XBR111_State", new object[1] { val });
			Diagnose("state-tx-" + target, "State TX target=" + target + " rev=" + _revision + " serial=" + _snapshotSerial);
		}
	}

	private void RPC_State(long sender, ZPackage pkg)
	{
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0234: Expected O, but got Unknown
		if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
		{
			return;
		}
		if (!IsServerSender(sender))
		{
			Diagnose("forged-state", "Rejected state sender=" + sender, warning: true);
			return;
		}
		try
		{
			if (pkg == null || pkg.Size() > 1024 || pkg.ReadInt() != 111)
			{
				throw new InvalidDataException("state protocol/size");
			}
			string text = pkg.ReadString();
			long num = pkg.ReadLong();
			long num2 = pkg.ReadLong();
			long num3 = pkg.ReadLong();
			double num4 = pkg.ReadDouble();
			if (text.Length != 32 || num < 0 || num2 < 0 || num3 <= 0 || !Finite(num4) || num4 < 0.0)
			{
				throw new InvalidDataException("state header");
			}
			if (_hasSnapshot && text != _session)
			{
				Diagnose("state-session", "Unexpected server session; reconnect required.", warning: true);
			}
			else if (!_hasSnapshot || (num3 > _lastSnapshotSerial && num2 >= _revision && num >= _matchId))
			{
				bool localExemption = pkg.ReadBool();
				ZoneState.Read(pkg);
				bool flag = !_hasSnapshot || num2 != _revision;
				if (!_hasSnapshot || num != _matchId)
				{
					_lastDamageSequence = 0L;
				}
				SetLocalExemption(localExemption);
				_session = text;
				_matchId = num;
				_revision = num2;
				_lastSnapshotSerial = num3;
				_lastStateAt = (_clockAtReceipt = LocalClock());
				_serverClockAtReceipt = num4;
				_hasSnapshot = true;
				_snapshotNeeded = false;
				Diagnose("state-rx", "State RX sender=" + sender + " rev=" + num2 + " serial=" + num3 + " " + ZoneState.Describe(), warning: false, flag ? 0.0 : DiagnosticSeconds);
				ZPackage val = new ZPackage();
				val.Write(text);
				val.Write(num2);
				val.Write("1.1.1");
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "XBR111_StateAck", new object[1] { val });
			}
		}
		catch (Exception ex)
		{
			Diagnose("state-error", "State rejected without modifying current snapshot: " + ex, warning: true);
			_snapshotNeeded = true;
			_nextSnapshotAt = 0.0;
		}
	}

	private void RPC_StateAck(long sender, ZPackage pkg)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !IsConnectedPeer(sender) || !Allow(_rpcTimes, "state-ack-" + sender, 0.2))
		{
			return;
		}
		try
		{
			if (pkg == null || pkg.Size() > 128)
			{
				throw new InvalidDataException("state ack size");
			}
			string text = pkg.ReadString();
			long num = pkg.ReadLong();
			string text2 = pkg.ReadString();
			if (!(text != _session) && num >= 0 && num <= _revision && !(text2 != "1.1.1"))
			{
				if (!_peers.TryGetValue(sender, out var value))
				{
					value = new PeerDiagnostics();
					_peers[sender] = value;
				}
				value.AckRevision = Math.Max(value.AckRevision, num);
				value.LastStateAckAt = LocalClock();
				Diagnose("state-ack-" + sender, "State ACK peer=" + sender + " rev=" + num + " version=" + text2);
			}
		}
		catch (Exception ex)
		{
			Diagnose("state-ack-error", "State ACK failed: " + ex, warning: true);
		}
	}

	internal static double ServerTime()
	{
		ValheimBattleRoyalePlugin instance = Instance;
		if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && (Object)(object)instance != (Object)null && instance._hasSnapshot)
		{
			return instance._serverClockAtReceipt + LocalClock() - instance._clockAtReceipt;
		}
		return LocalClock();
	}

	internal static float DistanceXZ(Vector3 a, Vector3 b)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		float num = a.x - b.x;
		float num2 = a.z - b.z;
		return Mathf.Sqrt(num * num + num2 * num2);
	}

	internal static Vector3 Flat(Vector3 v)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		v.y = 0f;
		return v;
	}

	internal Vector3 GetEffectiveCurrentCenter()
	{
		//IL_000f: 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_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		if (ZoneState.Phase != ZonePhase.Shrinking || !ZoneState.HasNext)
		{
			return ZoneState.CurrentCenter;
		}
		float shrinkSeconds = ZoneState.ShrinkSeconds;
		float num = Mathf.Clamp01((float)((ServerTime() - ZoneState.PhaseStartedAt) / (double)shrinkSeconds));
		return Vector3.Lerp(ZoneState.ShrinkFromCenter, ZoneState.NextCenter, num);
	}

	internal float GetEffectiveCurrentRadius()
	{
		if (ZoneState.Phase != ZonePhase.Shrinking || !ZoneState.HasNext)
		{
			return ZoneState.CurrentRadius;
		}
		float shrinkSeconds = ZoneState.ShrinkSeconds;
		float num = Mathf.Clamp01((float)((ServerTime() - ZoneState.PhaseStartedAt) / (double)shrinkSeconds));
		return Mathf.Lerp(ZoneState.ShrinkFromRadius, ZoneState.NextRadius, num);
	}

	private static bool IsRussianLanguage()
	{
		string selectedGameLanguage = GetSelectedGameLanguage();
		if (!string.IsNullOrEmpty(selectedGameLanguage))
		{
			if (!selectedGameLanguage.Equals("Russian", StringComparison.OrdinalIgnoreCase) && !selectedGameLanguage.Equals("Русский", StringComparison.OrdinalIgnoreCase))
			{
				return selectedGameLanguage.StartsWith("ru", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
		return false;
	}

	private static string GetSelectedGameLanguage()
	{
		//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)
		if (!string.IsNullOrEmpty(_selectedLanguage) && Time.realtimeSinceStartup < _nextLanguageProbeAt)
		{
			return _selectedLanguage;
		}
		_nextLanguageProbeAt = Time.realtimeSinceStartup + 2f;
		try
		{
			object obj = ((LocalizationInstance == null) ? null : LocalizationInstance.GetValue(null, null));
			if (obj != null && SelectedLanguage != null)
			{
				string text = SelectedLanguage.Invoke(obj, null) as string;
				if (!string.IsNullOrEmpty(text))
				{
					return _selectedLanguage = text;
				}
			}
		}
		catch (Exception ex)
		{
			Instance?.Diagnose("language-error", "Language lookup failed: " + ex.Message, warning: true);
		}
		string text2 = PlayerPrefs.GetString("language", "");
		if (string.IsNullOrEmpty(text2))
		{
			text2 = PlayerPrefs.GetString("Language", "");
		}
		return _selectedLanguage = (string.IsNullOrEmpty(text2) ? ((object)Application.systemLanguage/*cast due to .constrained prefix*/).ToString() : text2);
	}

	private static string Localize(string english, string russian)
	{
		if (!IsRussianLanguage())
		{
			return english;
		}
		return russian;
	}

	private static TMP_FontAsset GetValheimNorseBold()
	{
		if ((Object)(object)_valheimNorseBold != (Object)null)
		{
			return _valheimNorseBold;
		}
		if ((Object)(object)Instance != (Object)null && LocalClock() < Instance._nextFontProbe)
		{
			return TMP_Settings.defaultFontAsset;
		}
		if ((Object)(object)Instance != (Object)null)
		{
			Instance._nextFontProbe = LocalClock() + 2.0;
		}
		TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
		foreach (TMP_FontAsset val in array)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name.Equals("Valheim-Norsebold", StringComparison.OrdinalIgnoreCase))
			{
				_valheimNorseBold = val;
				Instance.Diagnose("font-ready", "HUD font=" + ((Object)val).name, warning: false, 0.0);
				return val;
			}
		}
		Instance.Diagnose("font-pending", "Norsebold not yet loaded; default TMP font and retry are used.", warning: true, 30.0);
		return TMP_Settings.defaultFontAsset;
	}

	private void DestroyHud()
	{
		if ((Object)(object)_hudRoot != (Object)null)
		{
			Object.Destroy((Object)(object)_hudRoot);
		}
		_hudRoot = null;
		_hudTitle = (_hudTimer = (_hudDanger = null));
	}

	private void EnsureTmpHud()
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Expected O, but got Unknown
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_hudRoot != (Object)null) || !((Object)(object)_hudRoot.GetComponent<Canvas>() != (Object)null) || !((Object)(object)_hudTitle != (Object)null) || !((Object)(object)_hudTimer != (Object)null) || !((Object)(object)_hudDanger != (Object)null))
		{
			DestroyHud();
			GameObject val = new GameObject("XBR_TMP_HUD");
			Object.DontDestroyOnLoad((Object)(object)val);
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 200;
			val.AddComponent<CanvasScaler>().uiScaleMode = (ScaleMode)0;
			((Behaviour)val.AddComponent<GraphicRaycaster>()).enabled = false;
			_hudRoot = val;
			_hudTitle = CreateTmpText("Title", val.transform, 18f);
			_hudTimer = CreateTmpText("Timer", val.transform, 18f);
			_hudDanger = CreateTmpText("Danger", val.transform, 25f);
			((Graphic)_hudDanger).color = new Color(1f, 0.35f, 0.15f, 1f);
		}
	}

	private static TextMeshProUGUI CreateTmpText(string name, Transform parent, float size)
	{
		//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_0049: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name);
		val.transform.SetParent(parent, false);
		TextMeshProUGUI val2 = val.AddComponent<TextMeshProUGUI>();
		TMP_FontAsset valheimNorseBold = GetValheimNorseBold();
		if ((Object)(object)valheimNorseBold != (Object)null)
		{
			((TMP_Text)val2).font = valheimNorseBold;
		}
		((TMP_Text)val2).fontSize = size;
		((TMP_Text)val2).fontStyle = (FontStyles)1;
		((TMP_Text)val2).alignment = (TextAlignmentOptions)514;
		((Graphic)val2).color = Color.white;
		((Graphic)val2).raycastTarget = false;
		((TMP_Text)val2).textWrappingMode = (TextWrappingModes)0;
		return val2;
	}

	private static void SetScreenRect(RectTransform rt, Rect screenRect)
	{
		//IL_0001: 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_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		rt.anchorMin = Vector2.zero;
		rt.anchorMax = Vector2.zero;
		rt.pivot = Vector2.zero;
		rt.anchoredPosition = new Vector2(((Rect)(ref screenRect)).x, (float)Screen.height - ((Rect)(ref screenRect)).yMax);
		rt.sizeDelta = new Vector2(((Rect)(ref screenRect)).width, ((Rect)(ref screenRect)).height);
	}

	private void UpdateTmpHud()
	{
		//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0577: Unknown result type (might be due to invalid IL or missing references)
		//IL_057c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0747: Unknown result type (might be due to invalid IL or missing references)
		//IL_063e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0644: Unknown result type (might be due to invalid IL or missing references)
		if (!ShowHud.Value || (Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			if ((Object)(object)_hudRoot != (Object)null)
			{
				_hudRoot.SetActive(false);
			}
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				Diagnose("hud-hidden", "HUD hidden: ShowHud=" + ShowHud.Value + " localPlayer=" + ((Object)(object)Player.m_localPlayer != (Object)null));
			}
		}
		else
		{
			if (LocalClock() < _nextHudAttempt)
			{
				return;
			}
			EnsureTmpHud();
			_hudRoot.SetActive(true);
			Canvas component = _hudRoot.GetComponent<Canvas>();
			if (!((Behaviour)component).enabled || !((Behaviour)_hudTitle).enabled || !((Behaviour)_hudTimer).enabled || !((Behaviour)_hudDanger).enabled)
			{
				Diagnose("hud-components-disabled", "Restoring disabled BR HUD rendering components.", warning: true, 30.0);
			}
			((Behaviour)component).enabled = true;
			TextMeshProUGUI hudTitle = _hudTitle;
			TextMeshProUGUI hudTimer = _hudTimer;
			bool flag = (((Behaviour)_hudDanger).enabled = true);
			bool enabled = (((Behaviour)hudTimer).enabled = flag);
			((Behaviour)hudTitle).enabled = enabled;
			TMP_FontAsset valheimNorseBold = GetValheimNorseBold();
			if ((Object)(object)valheimNorseBold != (Object)null)
			{
				if ((Object)(object)((TMP_Text)_hudTitle).font != (Object)(object)valheimNorseBold)
				{
					((TMP_Text)_hudTitle).font = valheimNorseBold;
				}
				if ((Object)(object)((TMP_Text)_hudTimer).font != (Object)(object)valheimNorseBold)
				{
					((TMP_Text)_hudTimer).font = valheimNorseBold;
				}
				if ((Object)(object)((TMP_Text)_hudDanger).font != (Object)(object)valheimNorseBold)
				{
					((TMP_Text)_hudDanger).font = valheimNorseBold;
				}
			}
			double num = ServerTime();
			string text = null;
			string text2 = null;
			if (!_hasSnapshot)
			{
				text = Localize("BATTLE ROYALE", "КОРОЛЕВСКАЯ БИТВА");
				text2 = Localize("SYNCHRONIZING...", "СИНХРОНИЗАЦИЯ...");
			}
			else if (ZoneState.Starting)
			{
				text = Localize("BATTLE ROYALE", "КОРОЛЕВСКАЯ БИТВА");
				text2 = Localize("MATCH STARTS IN ", "МАТЧ НАЧНЁТСЯ ЧЕРЕЗ ") + FormatSeconds((float)Math.Max(0.0, ZoneState.MatchStartAt - num));
			}
			else if (ZoneState.Running)
			{
				if (ZoneState.Phase == ZonePhase.Waiting && ZoneState.HasNext)
				{
					text = Localize("ZONE SHRINKS IN", "СУЖЕНИЕ ЗОНЫ ЧЕРЕЗ");
					text2 = FormatSeconds(Mathf.Max(0f, ZoneState.WaitSecondsAtAssignment - (float)(num - ZoneState.PhaseStartedAt)));
				}
				else if (ZoneState.Phase == ZonePhase.Shrinking && ZoneState.HasNext)
				{
					text = Localize("ZONE IS SHRINKING", "ЗОНА СУЖАЕТСЯ");
					text2 = FormatSeconds(Mathf.Max(0f, ZoneState.ShrinkSeconds - (float)(num - ZoneState.PhaseStartedAt)));
				}
				else
				{
					text = Localize("BATTLE ROYALE", "КОРОЛЕВСКАЯ БИТВА");
				}
			}
			bool flag4 = !string.IsNullOrEmpty(text);
			((Component)_hudTitle).gameObject.SetActive(flag4);
			((Component)_hudTimer).gameObject.SetActive(flag4 && !string.IsNullOrEmpty(text2));
			if (flag4)
			{
				if (!MinimapUpdatePatch.TryGetSmallMinimapScreenRect(Minimap.instance, out var screenRect))
				{
					((Rect)(ref screenRect))..ctor((float)Screen.width - 320f, 245f, 300f, 70f);
				}
				float num2 = Mathf.Min(Mathf.Max(300f, ((Rect)(ref screenRect)).width), (float)Screen.width);
				float num3 = Mathf.Clamp(((Rect)(ref screenRect)).xMax - num2, 0f, Mathf.Max(0f, (float)Screen.width - num2));
				float num4 = Mathf.Clamp(((Rect)(ref screenRect)).yMax + 8f, 0f, Mathf.Max(0f, (float)Screen.height - 72f));
				SetScreenRect(((TMP_Text)_hudTitle).rectTransform, new Rect(num3, num4, num2, 28f));
				SetScreenRect(((TMP_Text)_hudTimer).rectTransform, new Rect(num3, num4 + 27f, num2, 34f));
				((TMP_Text)_hudTitle).text = text;
				((TMP_Text)_hudTimer).text = text2 ?? "";
			}
			string text3 = "snapshot=" + _hasSnapshot + " phase=" + ZoneState.Phase.ToString() + " title=" + flag4 + " timer=" + !string.IsNullOrEmpty(text2) + " font=" + (((Object)(object)((TMP_Text)_hudTimer).font == (Object)null) ? "missing" : ((Object)((TMP_Text)_hudTimer).font).name);
			Diagnose("hud-status", "HUD " + text3 + " screen=" + Screen.width + "x" + Screen.height + " rev=" + _revision + " rootActive=" + _hudRoot.activeInHierarchy + " timerEnabled=" + ((Behaviour)_hudTimer).isActiveAndEnabled + " timerRect=" + ((object)((TMP_Text)_hudTimer).rectTransform.rect/*cast due to .constrained prefix*/).ToString() + " canvasEnabled=" + ((Behaviour)_hudRoot.GetComponent<Canvas>()).enabled, warning: false, (text3 == _lastHudStatus) ? DiagnosticSeconds : 0.0);
			_lastHudStatus = text3;
			if ((Object)(object)((TMP_Text)_hudTimer).font == (Object)null)
			{
				Diagnose("hud-no-font", "HUD has no usable font; waiting for assets.", warning: true, 30.0);
			}
			bool flag5 = !_localExempt && ZoneState.Running && ZoneState.HasCurrent && !((Character)Player.m_localPlayer).IsDead() && DistanceXZ(((Component)Player.m_localPlayer).transform.position, GetEffectiveCurrentCenter()) > GetEffectiveCurrentRadius();
			((Component)_hudDanger).gameObject.SetActive(flag5);
			if (flag5)
			{
				float num5 = SafeNumber(OutsideWarningFontSize.Value, 25f, 12f, 100f);
				float num6 = SafeNumber(OutsideWarningTopPercent.Value, 20f, 0f, 100f);
				((TMP_Text)_hudDanger).fontSize = num5;
				float num7 = Mathf.Min((float)Screen.height, num5 * 1.8f);
				float num8 = Mathf.Max(1f, (float)Screen.width - 20f);
				float num9 = Mathf.Clamp((float)Screen.height * num6 / 100f - num7 / 2f, 0f, Mathf.Max(0f, (float)Screen.height - num7));
				SetScreenRect(((TMP_Text)_hudDanger).rectTransform, new Rect(((float)Screen.width - num8) / 2f, num9, num8, num7));
				((TMP_Text)_hudDanger).text = Localize("YOU ARE OUTSIDE THE SAFE ZONE!", "ВЫ ВНЕ БЕЗОПАСНОЙ ЗОНЫ!");
			}
		}
	}

	private static string FormatSeconds(float value)
	{
		int num = Mathf.CeilToInt(value);
		return (num / 60).ToString("00") + ":" + (num % 60).ToString("00");
	}

	private void OnGUI()
	{
		if ((Object)(object)Minimap.instance != (Object)null)
		{
			MinimapUpdatePatch.DrawZoneCircles(Minimap.instance);
		}
	}

	internal static bool HandleCommand(Terminal terminal, string line)
	{
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_028f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Instance == (Object)null || string.IsNullOrWhiteSpace(line))
		{
			return false;
		}
		string[] array = line.Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length == 0 || !array[0].Equals("br", StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		try
		{
			if (array.Length == 1 || array[1].Equals("help", StringComparison.OrdinalIgnoreCase))
			{
				terminal.AddString("br start | stop | toggle | status | zone <x> <z> [r] | next <x> <z> [r] | here [r] | nexthere [r] | cancelnext");
				return true;
			}
			string text = array[1].ToLowerInvariant();
			switch (text)
			{
			case "status":
				terminal.AddString(ZoneState.Describe() + " snapshot=" + Instance._hasSnapshot + " rev=" + Instance._revision + " stateAge=" + (((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) ? "N/A(server)" : ((!Instance._hasSnapshot) ? "N/A(no snapshot)" : (LocalClock() - Instance._lastStateAt).ToString("F1"))));
				return true;
			case "start":
			case "stop":
			case "toggle":
			case "cancelnext":
				Instance.SendRequest(text);
				return true;
			case "here":
			case "nexthere":
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					terminal.AddString("No local player.");
					return true;
				}
				float radius2 = ((array.Length >= 3) ? ParseFloat(array[2]) : 0f);
				Instance.SendRequest((text == "here") ? "zone" : "next", ((Component)Player.m_localPlayer).transform.position, radius2);
				return true;
			}
			case "zone":
			case "next":
			{
				if (array.Length < 4)
				{
					terminal.AddString("Usage: br " + text + " <x> <z> [radius]");
					return true;
				}
				float num = ParseFloat(array[2]);
				float num2 = ParseFloat(array[3]);
				float radius = ((array.Length >= 5) ? ParseFloat(array[4]) : 0f);
				Instance.SendRequest(text, new Vector3(num, 0f, num2), radius);
				return true;
			}
			default:
				terminal.AddString("Unknown BR command. Type: br help");
				break;
			}
		}
		catch (Exception ex)
		{
			terminal.AddString("BR error: " + ex.Message);
		}
		return true;
	}

	private static float ParseFloat(string s)
	{
		if (!float.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !Finite(result))
		{
			throw new FormatException("Invalid number: " + s);
		}
		return result;
	}
}
internal enum ZonePhase
{
	Idle,
	Waiting,
	Shrinking
}
internal static class ZoneState
{
	internal static bool Running;

	internal static bool Starting;

	internal static bool HasCurrent;

	internal static bool HasNext;

	internal static Vector3 CurrentCenter;

	internal static Vector3 NextCenter;

	internal static Vector3 ShrinkFromCenter;

	internal static float CurrentRadius;

	internal static float NextRadius;

	internal static float ShrinkFromRadius;

	internal static float WaitSecondsAtAssignment;

	internal static float ShrinkSeconds = 180f;

	internal static float DamageTick = 2f;

	internal static float DamageAmount = 10f;

	internal static ZonePhase Phase;

	internal static double PhaseStartedAt;

	internal static double MatchStartAt;

	internal static void Reset()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//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_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		Running = (Starting = (HasCurrent = (HasNext = false)));
		CurrentCenter = (NextCenter = (ShrinkFromCenter = Vector3.zero));
		CurrentRadius = (NextRadius = (ShrinkFromRadius = (WaitSecondsAtAssignment = 0f)));
		ShrinkSeconds = 180f;
		DamageTick = 2f;
		DamageAmount = 10f;
		Phase = ZonePhase.Idle;
		PhaseStartedAt = (MatchStartAt = 0.0);
	}

	internal static void Write(ZPackage p)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		p.Write(Running);
		p.Write(Starting);
		p.Write(MatchStartAt);
		p.Write(HasCurrent);
		p.Write(HasNext);
		p.Write(CurrentCenter);
		p.Write(CurrentRadius);
		p.Write(NextCenter);
		p.Write(NextRadius);
		p.Write((int)Phase);
		p.Write(PhaseStartedAt);
		p.Write(ShrinkFromCenter);
		p.Write(ShrinkFromRadius);
		p.Write(WaitSecondsAtAssignment);
		p.Write(ShrinkSeconds);
		p.Write(DamageTick);
		p.Write(DamageAmount);
	}

	internal static void Read(ZPackage p)
	{
		//IL_0025: 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_0053: 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_0082: 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_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e5: 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_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		bool flag = p.ReadBool();
		bool flag2 = p.ReadBool();
		double num = p.ReadDouble();
		bool flag3 = p.ReadBool();
		bool flag4 = p.ReadBool();
		Vector3 val = p.ReadVector3();
		float num2 = p.ReadSingle();
		Vector3 val2 = p.ReadVector3();
		float num3 = p.ReadSingle();
		int num4 = p.ReadInt();
		double num5 = p.ReadDouble();
		Vector3 val3 = p.ReadVector3();
		float num6 = p.ReadSingle();
		float num7 = p.ReadSingle();
		float num8 = p.ReadSingle();
		float num9 = p.ReadSingle();
		float num10 = p.ReadSingle();
		if (!ValheimBattleRoyalePlugin.Finite(val) || !ValheimBattleRoyalePlugin.Finite(val2) || !ValheimBattleRoyalePlugin.Finite(val3) || !Valid(num2, 0f, 100000f) || !Valid(num3, 0f, 100000f) || !Valid(num6, 0f, 100000f) || !Valid(num7, 0f, 86400f) || !Valid(num8, 0.01f, 86400f) || !Valid(num9, 0.1f, 3600f) || !Valid(num10, 0f, 100000f) || !ValheimBattleRoyalePlugin.Finite(num) || !ValheimBattleRoyalePlugin.Finite(num5) || num < 0.0 || num5 < 0.0 || num4 < 0 || num4 > 2 || (flag && flag2) || ((flag || flag2 || flag4) && !flag3) || (flag3 && num2 < 1f) || (flag4 && num3 < 1f) || (num4 != 0 && !flag4) || (num4 == 2 && num6 < 1f))
		{
			throw new InvalidDataException("Invalid zone snapshot");
		}
		Running = flag;
		Starting = flag2;
		MatchStartAt = num;
		HasCurrent = flag3;
		HasNext = flag4;
		CurrentCenter = val;
		CurrentRadius = num2;
		NextCenter = val2;
		NextRadius = num3;
		Phase = (ZonePhase)num4;
		PhaseStartedAt = num5;
		ShrinkFromCenter = val3;
		ShrinkFromRadius = num6;
		WaitSecondsAtAssignment = num7;
		ShrinkSeconds = num8;
		DamageTick = num9;
		DamageAmount = num10;
	}

	private static bool Valid(float value, float min, float max)
	{
		if (ValheimBattleRoyalePlugin.Finite(value) && value >= min)
		{
			return value <= max;
		}
		return false;
	}

	internal static string Describe()
	{
		return string.Format("BR running={0}, starting={1}, phase={2}, current=({3:F0},{4:F0}) R={5:F0}, next={6}, wait={7:F1}, shrink={8:F1}, damage={9:F1}/{10:F1}s", Running, Starting, Phase, CurrentCenter.x, CurrentCenter.z, CurrentRadius, HasNext ? $"({NextCenter.x:F0},{NextCenter.z:F0}) R={NextRadius:F0}" : "none", WaitSecondsAtAssignment, ShrinkSeconds, DamageAmount, DamageTick);
	}
}
[HarmonyPatch(typeof(ZRoutedRpc), "RPC_RoutedRPC", new Type[]
{
	typeof(ZRpc),
	typeof(ZPackage)
})]
internal static class BattleRoyaleIngressPatch
{
	private static bool Prefix(ZRpc __0, ZPackage __1)
	{
		ValheimBattleRoyalePlugin instance = ValheimBattleRoyalePlugin.Instance;
		if (!((Object)(object)instance == (Object)null))
		{
			return instance.ValidateIngress(__0, __1);
		}
		return true;
	}
}
[HarmonyPatch(typeof(Player), "SetPVP")]
internal static class PlayerSetPvpPatch
{
	private static void Prefix(Player __instance, ref bool __0)
	{
		ValheimBattleRoyalePlugin instance = ValheimBattleRoyalePlugin.Instance;
		if ((Object)(object)instance != (Object)null && !instance._restoringPvp)
		{
			instance.GuardPvp(__instance, ref __0);
		}
	}
}
[HarmonyPatch(typeof(Player), "Message", new Type[]
{
	typeof(MessageType),
	typeof(string),
	typeof(int),
	typeof(Sprite),
	typeof(bool)
})]
internal static class PvpNotificationPatch
{
	private static bool Prefix(Character __instance, string msg)
	{
		ValheimBattleRoyalePlugin instance = ValheimBattleRoyalePlugin.Instance;
		if (!((Object)(object)instance == (Object)null))
		{
			return instance.AllowPvpNotice(__instance, msg);
		}
		return true;
	}
}
[HarmonyPatch(typeof(Terminal), "TryRunCommand", new Type[]
{
	typeof(string),
	typeof(bool),
	typeof(bool)
})]
internal static class TerminalTryRunCommandPatch
{
	private static bool Prefix(Terminal __instance, string text)
	{
		return !ValheimBattleRoyalePlugin.HandleCommand(__instance, text);
	}
}
[HarmonyPatch(typeof(Minimap), "Update")]
internal static class MinimapUpdatePatch
{
	private static readonly MethodInfo ScreenToWorldPoint = FindMethod("ScreenToWorldPoint", typeof(Vector3));

	private static readonly FieldInfo MapImageLargeField = AccessTools.Field(typeof(Minimap), "m_mapImageLarge");

	private static readonly FieldInfo MapImageSmallField = AccessTools.Field(typeof(Minimap), "m_mapImageSmall");

	private static readonly FieldInfo PixelSizeField = AccessTools.Field(typeof(Minimap), "m_pixelSize");

	private static readonly FieldInfo TextureSizeField = AccessTools.Field(typeof(Minimap), "m_textureSize");

	private static Texture2D _lineTex;

	private static MethodInfo FindMethod(string name, params Type[] parameterTypes)
	{
		MethodInfo[] methods = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!string.Equals(methodInfo.Name, name, StringComparison.Ordinal))
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length != parameterTypes.Length)
			{
				continue;
			}
			bool flag = true;
			for (int j = 0; j < parameters.Length; j++)
			{
				if (parameters[j].ParameterType != parameterTypes[j])
				{
					flag = false;
					break;
				}
			}
			if (flag)
			{
				return methodInfo;
			}
		}
		return null;
	}

	private static void Postfix(Minimap __instance)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Invalid comparison between Unknown and I4
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: 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_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		ValheimBattleRoyalePlugin instance = ValheimBattleRoyalePlugin.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || ScreenToWorldPoint == null || (int)__instance.m_mode != 2)
		{
			return;
		}
		KeyboardShortcut value = instance.PlaceZoneHotkey.Value;
		bool flag = ((KeyboardShortcut)(ref value)).IsDown();
		value = instance.CancelZoneHotkey.Value;
		bool flag2 = ((KeyboardShortcut)(ref value)).IsDown();
		if ((!flag && !flag2) || !CanUseMapInput(__instance))
		{
			return;
		}
		if (flag2)
		{
			if (ZoneState.Phase == ZonePhase.Shrinking)
			{
				ValheimBattleRoyalePlugin.Log.LogInfo((object)"Map Ctrl+Click ignored: zone shrinking is already in progress.");
				return;
			}
			ValheimBattleRoyalePlugin.Log.LogInfo((object)(ZoneState.HasNext ? "Map Ctrl+Click: cancel next zone" : "Map Ctrl+Click: cancel current/start zone"));
			instance.SendRequest("cancelzone");
		}
		else
		{
			if (!flag)
			{
				return;
			}
			if (ZoneState.Phase != ZonePhase.Shrinking)
			{
				try
				{
					Vector3 val = (Vector3)ScreenToWorldPoint.Invoke(__instance, new object[1] { Input.mousePosition });
					val.y = 0f;
					if (!ZoneState.HasCurrent)
					{
						ValheimBattleRoyalePlugin.Log.LogInfo((object)$"Map Shift+Click: initial zone at X={val.x:F0} Z={val.z:F0}");
						instance.SendRequest("zone", val, instance.InitialRadius.Value);
					}
					else
					{
						float num = (ZoneState.HasNext ? ZoneState.NextRadius : 0f);
						ValheimBattleRoyalePlugin.Log.LogInfo((object)(ZoneState.HasNext ? $"Map Shift+Click: move next zone to X={val.x:F0} Z={val.z:F0}, keep R={num:F0}" : $"Map Shift+Click: next zone at X={val.x:F0} Z={val.z:F0} (automatic radius)"));
						instance.SendRequest("next", val, num);
					}
					return;
				}
				catch (Exception ex)
				{
					ValheimBattleRoyalePlugin.Log.LogError((object)("Map click conversion failed: " + ex));
					return;
				}
			}
			ValheimBattleRoyalePlugin.Log.LogInfo((object)"Map Shift+Click ignored: zone shrinking is already in progress.");
		}
	}

	private static bool CanUseMapInput(Minimap map)
	{
		//IL_0077: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Expected O, but got Unknown
		//IL_00e6: 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)
		if (ValheimBattleRoyalePlugin.IsTextInputActive())
		{
			return false;
		}
		if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null && Hud.instance.m_buildUi.SearchFieldFocused)
		{
			return false;
		}
		object? obj = MapImageLargeField?.GetValue(map);
		Graphic val = (Graphic)((obj is Graphic) ? obj : null);
		if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled)
		{
			return false;
		}
		Canvas canvas = val.canvas;
		Camera val2 = (((Object)(object)canvas != (Object)null && (int)canvas.renderMode != 0) ? canvas.worldCamera : null);
		if (!RectTransformUtility.RectangleContainsScreenPoint(val.rectTransform, Vector2.op_Implicit(Input.mousePosition), val2))
		{
			return false;
		}
		EventSystem current = EventSystem.current;
		if ((Object)(object)current == (Object)null)
		{
			return false;
		}
		List<RaycastResult> list = new List<RaycastResult>();
		current.RaycastAll(new PointerEventData(current)
		{
			position = Vector2.op_Implicit(Input.mousePosition)
		}, list);
		if (list.Count == 0)
		{
			return false;
		}
		RaycastResult val3 = list[0];
		Transform transform = ((RaycastResult)(ref val3)).gameObject.transform;
		if ((Object)(object)transform == (Object)(object)((Component)val).transform || transform.IsChildOf(((Component)val).transform))
		{
			return (Object)(object)((Component)transform).GetComponentInParent<Selectable>() == (Object)null;
		}
		return false;
	}

	internal static void Cleanup()
	{
		if ((Object)(object)_lineTex != (Object)null)
		{
			Object.Destroy((Object)(object)_lineTex);
		}
		_lineTex = null;
	}

	internal static void DrawZoneCircles(Minimap map)
	{
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		if (!ZoneState.HasCurrent || (Object)(object)map == (Object)null)
		{
			return;
		}
		try
		{
			object activeMapImage = GetActiveMapImage(map);
			if (activeMapImage == null || !TryGetImageGeometry(activeMapImage, out var uvRect, out var screenRect))
			{
				return;
			}
			float numericField = GetNumericField(PixelSizeField, map, 1f);
			float numericField2 = GetNumericField(TextureSizeField, map, 2048f);
			float num = numericField * numericField2;
			if (!(num <= 0.001f) && !(Mathf.Abs(((Rect)(ref uvRect)).width) < 1E-05f) && !(Mathf.Abs(((Rect)(ref uvRect)).height) < 1E-05f))
			{
				Vector3 effectiveCurrentCenter = ValheimBattleRoyalePlugin.Instance.GetEffectiveCurrentCenter();
				float effectiveCurrentRadius = ValheimBattleRoyalePlugin.Instance.GetEffectiveCurrentRadius();
				DrawCircle(effectiveCurrentCenter, effectiveCurrentRadius, new Color(0.2f, 1f, 0.25f, 0.95f), uvRect, screenRect, num, 3f);
				if (ZoneState.HasNext)
				{
					DrawCircle(ZoneState.NextCenter, ZoneState.NextRadius, new Color(1f, 0.85f, 0.15f, 0.95f), uvRect, screenRect, num, 3f);
				}
			}
		}
		catch (Exception ex)
		{
			ValheimBattleRoyalePlugin.Instance?.Diagnose("overlay-error", "Zone map overlay unavailable: " + ex, warning: true);
		}
	}

	private static object GetActiveMapImage(Minimap map)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		if ((int)map.m_mode == 2)
		{
			return MapImageLargeField?.GetValue(map);
		}
		return MapImageSmallField?.GetValue(map);
	}

	internal static bool TryGetSmallMinimapScreenRect(Minimap map, out Rect screenRect)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		screenRect = default(Rect);
		if ((Object)(object)map == (Object)null)
		{
			return false;
		}
		try
		{
			object obj = MapImageSmallField?.GetValue(map);
			if (obj == null)
			{
				return false;
			}
			Rect uvRect;
			return TryGetImageGeometry(obj, out uvRect, out screenRect);
		}
		catch
		{
			return false;
		}
	}

	private static float GetNumericField(FieldInfo field, object instance, float fallback)
	{
		if (field == null)
		{
			return fallback;
		}
		object value = field.GetValue(instance);
		if (value == null)
		{
			return fallback;
		}
		try
		{
			return Convert.ToSingle(value, CultureInfo.InvariantCulture);
		}
		catch
		{
			return fallback;
		}
	}

	private static bool TryGetImageGeometry(object image, out Rect uvRect, out Rect screenRect)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: 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_0101: 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_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type