Decompiled source of AFKResourcePreserver v1.0.0

plugins\AFKResourcePreserver\AFKResourcePreserver.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SpaceCraft;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("AFKResourcePreserver")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+87efc3bba54d9be5bac599b91ab55d137c16b97f")]
[assembly: AssemblyProduct("AFKResourcePreserver")]
[assembly: AssemblyTitle("AFKResourcePreserver")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AFKResourcePreserver
{
	[HarmonyPatch]
	internal static class PassiveDepletionPatch
	{
		internal static MethodBase TargetMethod()
		{
			return AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(PlayerGaugesHandler), "GaugesTick", new Type[1] { typeof(float) }, (Type[])null)) ?? throw new MissingMethodException("PlayerGaugesHandler.GaugesTick iterator was not found.");
		}

		internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> source)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(PlayerGaugesHandler), "AddHealth", new Type[1] { typeof(float) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerGaugesHandler), "AddWater", new Type[1] { typeof(float) }, (Type[])null);
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			int num2 = 0;
			foreach (CodeInstruction item in source)
			{
				CodeInstruction val = new CodeInstruction(item);
				if (CodeInstructionExtensions.Calls(val, methodInfo))
				{
					val.opcode = OpCodes.Call;
					val.operand = AccessTools.Method(typeof(PassiveDepletionPatch), "ApplyHunger", (Type[])null, (Type[])null);
					num++;
				}
				else if (CodeInstructionExtensions.Calls(val, methodInfo2))
				{
					val.opcode = OpCodes.Call;
					val.operand = AccessTools.Method(typeof(PassiveDepletionPatch), "ApplyThirst", (Type[])null, (Type[])null);
					num2++;
				}
				list.Add(val);
			}
			if (num != 1 || num2 != 1)
			{
				throw new InvalidOperationException($"Expected one passive hunger and one thirst call; found {num}/{num2}.");
			}
			return list;
		}

		internal static void ApplyHunger(PlayerGaugesHandler gauges, float delta)
		{
			Plugin instance = Plugin.Instance;
			if (instance == null || !instance.ShouldSuppress(gauges, delta, isHunger: true))
			{
				gauges.AddHealth(delta);
			}
		}

		internal static void ApplyThirst(PlayerGaugesHandler gauges, float delta)
		{
			Plugin instance = Plugin.Instance;
			if (instance == null || !instance.ShouldSuppress(gauges, delta, isHunger: false))
			{
				gauges.AddWater(delta);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMainController), "SetPlayerPlacement")]
	internal static class PlacementResetPatch
	{
		private static void Prefix(PlayerMainController __instance)
		{
			Plugin.Instance?.ResetPlayer(__instance);
		}
	}
	[HarmonyPatch(typeof(PlayerStatus), "DieAndRespawn")]
	internal static class RespawnResetPatch
	{
		private static void Prefix(PlayerStatus __instance)
		{
			Plugin.Instance?.ResetPlayer(((Component)__instance).GetComponent<PlayerMainController>());
		}
	}
	[BepInPlugin("noxie.planetcrafter.afkresourcepreserver", "AFK Resource Preserver", "1.0.0")]
	[BepInProcess("Planet Crafter.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class RemoteState
		{
			internal PlayerMainController Player;

			internal readonly RemoteProtection Protection = new RemoteProtection();

			internal bool ReportedActive;
		}

		public const string Id = "noxie.planetcrafter.afkresourcepreserver";

		internal static Plugin Instance;

		private const string StateMessage = "noxie.planetcrafter.afkresourcepreserver.state.v1";

		private const string AckMessage = "noxie.planetcrafter.afkresourcepreserver.ack.v1";

		private ConfigEntry<bool> enabledSetting;

		private ConfigEntry<bool> hunger;

		private ConfigEntry<bool> thirst;

		private ConfigEntry<float> delay;

		private ConfigEntry<float> threshold;

		private Harmony harmony;

		private bool ready;

		private bool configDirty;

		private bool warnedHost;

		private readonly StationaryTracker local = new StationaryTracker();

		private PlayerMainController localPlayer;

		private NetworkManager network;

		private CustomMessagingManager messaging;

		private double lastSend = -100.0;

		private double lastAck = -100.0;

		private double connectedSince;

		private double nextError;

		private int lastMask = -1;

		private readonly Dictionary<ulong, RemoteState> remotes = new Dictionary<ulong, RemoteState>();

		private readonly List<ulong> expired = new List<ulong>();

		private void Awake()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			Instance = this;
			enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable preservation for this player. On a host, false also disables remote preservation.");
			delay = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "AfkDelaySeconds", 30f, new ConfigDescription("Continuous stationary seconds before preservation starts.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3600f), Array.Empty<object>()));
			threshold = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "MovementThreshold", 0.02f, new ConfigDescription("World-space distance from the stationary anchor. Includes vertical movement; ignores camera rotation.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.001f, 1f), Array.Empty<object>()));
			hunger = ((BaseUnityPlugin)this).Config.Bind<bool>("Preservation", "PreserveHunger", true, "Pause passive food drain only; damage and healing remain normal.");
			thirst = ((BaseUnityPlugin)this).Config.Bind<bool>("Preservation", "PreserveThirst", true, "Pause passive thirst drain only; drinking remains normal.");
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			harmony = new Harmony("noxie.planetcrafter.afkresourcepreserver");
			try
			{
				harmony.PatchAll(typeof(Plugin).Assembly);
				ready = true;
				SceneManager.activeSceneChanged += OnSceneChanged;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"AFK Resource Preserver 1.0.0 loaded. Applied GaugesTick.MoveNext depletion transpiler and placement/respawn reset prefixes. Oxygen and damage untouched.");
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)("Unsupported game or patch conflict; all AFK Resource Preserver patches removed. " + ex));
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			configDirty = true;
		}

		private void OnSceneChanged(Scene oldScene, Scene newScene)
		{
			ResetLocal("scene transition");
			remotes.Clear();
			lastSend = -100.0;
		}

		private void Update()
		{
			if (!ready)
			{
				return;
			}
			try
			{
				double unscaledTimeAsDouble = Time.unscaledTimeAsDouble;
				RefreshNetwork(unscaledTimeAsDouble);
				if (configDirty)
				{
					configDirty = false;
					ResetLocal("configuration changed");
					remotes.Clear();
					lastSend = -100.0;
				}
				SampleLocal(unscaledTimeAsDouble);
				if ((Object)(object)network != (Object)null && network.IsServer)
				{
					expired.Clear();
					foreach (KeyValuePair<ulong, RemoteState> remote in remotes)
					{
						if (!network.ConnectedClients.ContainsKey(remote.Key) || !remote.Value.Protection.Fresh(unscaledTimeAsDouble))
						{
							expired.Add(remote.Key);
						}
						else
						{
							SampleRemote(remote.Value, unscaledTimeAsDouble);
						}
					}
					foreach (ulong item in expired)
					{
						remotes.Remove(item);
					}
				}
				SendLocalState(unscaledTimeAsDouble);
			}
			catch (Exception ex)
			{
				FailOpen(ex);
			}
		}

		private void LateUpdate()
		{
			if (!ready)
			{
				return;
			}
			try
			{
				SampleLocal(Time.unscaledTimeAsDouble);
				SendLocalState(Time.unscaledTimeAsDouble);
			}
			catch (Exception ex)
			{
				FailOpen(ex);
			}
		}

		private bool WorldReady()
		{
			PlanetLoader manager = Managers.GetManager<PlanetLoader>();
			if ((Object)(object)manager != (Object)null && manager.GetIsLoaded())
			{
				return manager.GetIsInitialLoadingSequenceCompleted();
			}
			return false;
		}

		private void SampleLocal(double now)
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: 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)
			PlayersManager manager = Managers.GetManager<PlayersManager>();
			PlayerMainController val = (((Object)(object)manager != (Object)null) ? manager.GetActivePlayerController() : null);
			if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).IsOwner || !((NetworkBehaviour)val).IsSpawned || !((Component)val).gameObject.activeInHierarchy || !WorldReady())
			{
				ResetLocal("local player unavailable/loading");
				return;
			}
			if ((Object)(object)localPlayer != (Object)(object)val)
			{
				ResetLocal("local player changed");
				localPlayer = val;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Local player acquired (owner " + ((NetworkBehaviour)val).OwnerClientId + ")."));
			}
			PlayerGaugesHandler playerGaugesHandler = val.GetPlayerGaugesHandler();
			PlayerCanAct playerCanAct = val.GetPlayerCanAct();
			if (!enabledSetting.Value || !StationaryTracker.ValidSettings(delay.Value, threshold.Value) || (Object)(object)playerGaugesHandler == (Object)null || playerGaugesHandler.GetPauseConsumption() || playerGaugesHandler.GetPlayerIsDying() || (Object)(object)playerCanAct == (Object)null || !playerCanAct.GetCanMoveAfterStart())
			{
				ResetLocalTimer("disabled/loading/respawning");
				return;
			}
			bool active = local.Active;
			Vector3 position = ((Component)val).transform.position;
			local.Sample(position.x, position.y, position.z, now, delay.Value, threshold.Value);
			if (active != local.Active)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)((!local.Active) ? "AFK protection deactivated following movement or interrupted sampling; timer reset." : (((Object)(object)network != (Object)null && network.IsServer) ? "AFK protection activated for the local player." : "AFK stationary delay reached; preservation requested (co-op requires host support).")));
			}
		}

		private void SampleRemote(RemoteState state, double now)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			RemoteProtection protection = state.Protection;
			PlayerMainController player = state.Player;
			if (!enabledSetting.Value || (Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsSpawned || !((Component)player).gameObject.activeInHierarchy || !WorldReady())
			{
				protection.Reset();
				state.ReportedActive = false;
				return;
			}
			PlayerGaugesHandler playerGaugesHandler = player.GetPlayerGaugesHandler();
			if ((Object)(object)playerGaugesHandler == (Object)null || !((NetworkBehaviour)playerGaugesHandler).IsServer || !playerGaugesHandler.HasStarted() || playerGaugesHandler.GetPauseConsumption() || playerGaugesHandler.GetPlayerIsDying())
			{
				protection.Reset();
				state.ReportedActive = false;
				return;
			}
			Vector3 position = ((Component)player).transform.position;
			protection.Tracker.Sample(position.x, position.y, position.z, now, protection.Delay, protection.Threshold);
			bool flag = protection.Suppress(-1f, hunger: true, now) || protection.Suppress(-1f, hunger: false, now);
			if (state.ReportedActive != flag)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("AFK protection " + (flag ? "activated" : "deactivated following movement/reset") + " for owner " + ((NetworkBehaviour)player).OwnerClientId + "."));
			}
			state.ReportedActive = flag;
		}

		internal bool ShouldSuppress(PlayerGaugesHandler gauges, float delta, bool isHunger)
		{
			if (!ready || !enabledSetting.Value || delta >= 0f || (Object)(object)gauges == (Object)null || !((NetworkBehaviour)gauges).IsServer || !((NetworkBehaviour)gauges).IsSpawned)
			{
				return false;
			}
			try
			{
				double unscaledTimeAsDouble = Time.unscaledTimeAsDouble;
				if (((NetworkBehaviour)gauges).IsOwner)
				{
					SampleLocal(unscaledTimeAsDouble);
					return (Object)(object)localPlayer != (Object)null && (Object)(object)localPlayer.GetPlayerGaugesHandler() == (Object)(object)gauges && StationaryTracker.Suppress(delta, enabledSetting.Value, local.Active, isHunger ? hunger.Value : thirst.Value);
				}
				if (remotes.TryGetValue(((NetworkBehaviour)gauges).OwnerClientId, out var value) && value.Protection.Fresh(unscaledTimeAsDouble) && (Object)(object)value.Player != (Object)null && (Object)(object)value.Player.GetPlayerGaugesHandler() == (Object)(object)gauges)
				{
					SampleRemote(value, unscaledTimeAsDouble);
					return value.Protection.Suppress(delta, isHunger, unscaledTimeAsDouble);
				}
			}
			catch (Exception ex)
			{
				FailOpen(ex);
			}
			return false;
		}

		private void RefreshNetwork(double now)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Expected O, but got Unknown
			NetworkManager singleton = NetworkManager.Singleton;
			CustomMessagingManager val = (((Object)(object)singleton != (Object)null && singleton.IsListening) ? singleton.CustomMessagingManager : null);
			if (val != messaging || !((Object)(object)singleton == (Object)(object)network))
			{
				DetachNetwork();
				ResetLocal("network session changed");
				remotes.Clear();
				network = singleton;
				messaging = val;
				lastAck = -100.0;
				lastSend = -100.0;
				lastMask = -1;
				warnedHost = false;
				connectedSince = now;
				if (messaging != null)
				{
					messaging.RegisterNamedMessageHandler("noxie.planetcrafter.afkresourcepreserver.state.v1", new HandleNamedMessageDelegate(ReceiveState));
					messaging.RegisterNamedMessageHandler("noxie.planetcrafter.afkresourcepreserver.ack.v1", new HandleNamedMessageDelegate(ReceiveAck));
					network.OnClientDisconnectCallback += OnDisconnect;
				}
			}
		}

		private unsafe void SendLocalState(double now)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			if (messaging == null || (Object)(object)network == (Object)null || network.IsServer || !network.IsConnectedClient)
			{
				return;
			}
			int num = (((Object)(object)localPlayer != (Object)null && enabledSetting.Value) ? ((int)((hunger.Value ? 1u : 0u) | (uint)(thirst.Value ? 2 : 0)) | (local.Active ? 4 : 0)) : 0);
			if (num != lastMask || now - lastSend >= 5.0)
			{
				FastBufferWriter val = default(FastBufferWriter);
				((FastBufferWriter)(ref val))..ctor(16, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
					float value = delay.Value;
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value, default(ForPrimitives));
					value = threshold.Value;
					((FastBufferWriter)(ref val)).WriteValueSafe<float>(ref value, default(ForPrimitives));
					messaging.SendNamedMessage("noxie.planetcrafter.afkresourcepreserver.state.v1", 0uL, val, (NetworkDelivery)3);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
				}
				lastMask = num;
				lastSend = now;
			}
			if (!warnedHost && now - connectedSince > 10.0 && now - lastAck > 10.0)
			{
				warnedHost = true;
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Host has not acknowledged AFK Resource Preserver. Install/enable v1.0.0 on the host too; client-only installation cannot stop server survival drain.");
			}
		}

		private unsafe void ReceiveState(ulong sender, FastBufferReader reader)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!ready || (Object)(object)network == (Object)null || !network.IsServer || !network.ConnectedClients.ContainsKey(sender) || ((FastBufferReader)(ref reader)).Length != 12)
				{
					return;
				}
				int num = default(int);
				((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				float num2 = default(float);
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num2, default(ForPrimitives));
				float num3 = default(float);
				((FastBufferReader)(ref reader)).ReadValueSafe<float>(ref num3, default(ForPrimitives));
				if (num < 0 || num > 7 || !StationaryTracker.ValidSettings(num2, num3))
				{
					return;
				}
				PlayersManager manager = Managers.GetManager<PlayersManager>();
				PlayerMainController val = (((Object)(object)manager != (Object)null) ? manager.GetOwnedPlayerController(sender) : null);
				if ((Object)(object)val == (Object)null || !((NetworkBehaviour)val).IsSpawned || ((NetworkBehaviour)val).OwnerClientId != sender)
				{
					return;
				}
				if (!remotes.TryGetValue(sender, out var value) || (Object)(object)value.Player != (Object)(object)val)
				{
					value = new RemoteState
					{
						Player = val
					};
					remotes[sender] = value;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("AFK-capable co-op player registered (owner " + sender + ")."));
				}
				value.Protection.Receive(num, num2, num3, Time.unscaledTimeAsDouble);
				SampleRemote(value, Time.unscaledTimeAsDouble);
				int num4 = (enabledSetting.Value ? 1 : 0);
				FastBufferWriter val2 = default(FastBufferWriter);
				((FastBufferWriter)(ref val2))..ctor(4, (Allocator)2, -1);
				try
				{
					((FastBufferWriter)(ref val2)).WriteValueSafe<int>(ref num4, default(ForPrimitives));
					messaging.SendNamedMessage("noxie.planetcrafter.afkresourcepreserver.ack.v1", sender, val2, (NetworkDelivery)3);
				}
				finally
				{
					((IDisposable)(*(FastBufferWriter*)(&val2))/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch (Exception ex)
			{
				FailOpen(ex);
			}
		}

		private void ReceiveAck(ulong sender, FastBufferReader reader)
		{
			//IL_002f: 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)
			if ((Object)(object)network == (Object)null || network.IsServer || sender != 0L || ((FastBufferReader)(ref reader)).Length != 4)
			{
				return;
			}
			int num = default(int);
			((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			if (num == 1)
			{
				if (lastAck < 0.0 || warnedHost)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Host confirmed AFK Resource Preserver support.");
				}
				lastAck = Time.unscaledTimeAsDouble;
				warnedHost = false;
			}
		}

		internal void ResetPlayer(PlayerMainController player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				if ((Object)(object)player == (Object)(object)localPlayer)
				{
					ResetLocalTimer("placement/respawn");
					lastSend = -100.0;
				}
				if (remotes.TryGetValue(((NetworkBehaviour)player).OwnerClientId, out var value))
				{
					value.Protection.Reset();
				}
			}
		}

		private void ResetLocalTimer(string reason)
		{
			if (local.Active)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("AFK protection deactivated: " + reason + "."));
			}
			local.Reset();
		}

		private void ResetLocal(string reason)
		{
			ResetLocalTimer(reason);
			localPlayer = null;
		}

		private void OnDisconnect(ulong id)
		{
			remotes.Remove(id);
			if ((Object)(object)network != (Object)null && id == network.LocalClientId)
			{
				ResetLocal("disconnected");
			}
		}

		private void FailOpen(Exception ex)
		{
			ResetLocal("runtime error");
			remotes.Clear();
			if (!(Time.unscaledTimeAsDouble < nextError))
			{
				nextError = Time.unscaledTimeAsDouble + 30.0;
				((BaseUnityPlugin)this).Logger.LogError((object)("Preservation reset; normal survival continues. " + ex));
			}
		}

		private void DetachNetwork()
		{
			if ((Object)(object)network != (Object)null)
			{
				network.OnClientDisconnectCallback -= OnDisconnect;
			}
			if (messaging != null)
			{
				messaging.UnregisterNamedMessageHandler("noxie.planetcrafter.afkresourcepreserver.state.v1");
				messaging.UnregisterNamedMessageHandler("noxie.planetcrafter.afkresourcepreserver.ack.v1");
			}
			messaging = null;
			network = null;
		}

		private void OnDestroy()
		{
			ready = false;
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			SceneManager.activeSceneChanged -= OnSceneChanged;
			DetachNetwork();
			remotes.Clear();
			local.Reset();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	internal sealed class RemoteProtection
	{
		internal readonly StationaryTracker Tracker = new StationaryTracker();

		internal float Delay { get; private set; }

		internal float Threshold { get; private set; }

		internal int Mask { get; private set; }

		internal double Received { get; private set; } = -100.0;

		internal bool Receive(int mask, float delay, float threshold, double now)
		{
			if (mask < 0 || mask > 7 || !StationaryTracker.ValidSettings(delay, threshold) || !StationaryTracker.Finite(now))
			{
				return false;
			}
			if (Delay != delay || Threshold != threshold || (Mask & 3) != (mask & 3) || ((Mask & 4) != 0 && (mask & 4) == 0) || !Fresh(now))
			{
				Tracker.Reset();
			}
			Delay = delay;
			Threshold = threshold;
			Mask = mask;
			Received = now;
			return true;
		}

		internal bool Fresh(double now)
		{
			if (now >= Received)
			{
				return now - Received <= 15.0;
			}
			return false;
		}

		internal bool Suppress(float delta, bool hunger, double now)
		{
			if (Fresh(now))
			{
				return StationaryTracker.Suppress(delta, enabled: true, Tracker.Active && (Mask & 4) != 0, (Mask & (hunger ? 1 : 2)) != 0);
			}
			return false;
		}

		internal void Reset()
		{
			Tracker.Reset();
			Mask &= 3;
		}
	}
	internal sealed class StationaryTracker
	{
		private bool sampled;

		private double anchorX;

		private double anchorY;

		private double anchorZ;

		private double since;

		private double lastSample;

		internal bool Active { get; private set; }

		internal void Reset()
		{
			sampled = false;
			Active = false;
		}

		internal bool Sample(double x, double y, double z, double now, double delay, double threshold)
		{
			if (!Finite(x) || !Finite(y) || !Finite(z) || !Finite(now) || !ValidSettings(delay, threshold))
			{
				Reset();
				return false;
			}
			double num = x - anchorX;
			double num2 = y - anchorY;
			double num3 = z - anchorZ;
			if (!sampled || now < lastSample || now - lastSample > 2.0 || num * num + num2 * num2 + num3 * num3 > threshold * threshold)
			{
				anchorX = x;
				anchorY = y;
				anchorZ = z;
				since = now;
				sampled = true;
				Active = false;
			}
			else
			{
				Active = now - since >= delay;
			}
			lastSample = now;
			return Active;
		}

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

		internal static bool ValidSettings(double delay, double threshold)
		{
			if (Finite(delay) && delay >= 1.0 && delay <= 3600.0 && Finite(threshold) && threshold >= 0.001)
			{
				return threshold <= 1.0;
			}
			return false;
		}

		internal static bool Suppress(float delta, bool enabled, bool active, bool preserve)
		{
			return delta < 0f && enabled && active && preserve;
		}
	}
}