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;
}
}
}