Decompiled source of SharedStamina v0.1.2

SharedStamina.dll

Decompiled 2 hours ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using GTFO.API;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("SharedStamina")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("SharedStamina")]
[assembly: AssemblyTitle("SharedStamina")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.2.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 SharedStamina
{
	internal readonly struct ContributionData
	{
		public long Epoch { get; }

		public double Cumulative { get; }

		public ContributionData(long epoch, double cumulative)
		{
			Epoch = epoch;
			Cumulative = cumulative;
		}
	}
	internal readonly struct AckData
	{
		public ulong PlayerId { get; }

		public double Cumulative { get; }

		public AckData(ulong playerId, double cumulative)
		{
			PlayerId = playerId;
			Cumulative = cumulative;
		}
	}
	internal sealed class StateData
	{
		public long Epoch { get; set; }

		public bool Active { get; set; }

		public double Pool { get; set; }

		public double Capacity { get; set; }

		public int ParticipantCount { get; set; }

		public int HumanPlayers { get; set; }

		public int ModdedPlayers { get; set; }

		public List<AckData> Acks { get; } = new List<AckData>();
	}
	internal static class PacketCodec
	{
		public static byte[] EncodeHello()
		{
			using MemoryStream memoryStream = new MemoryStream(8);
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(2);
			return memoryStream.ToArray();
		}

		public static bool TryDecodeHello(byte[] bytes, out int protocol)
		{
			protocol = 0;
			try
			{
				using BinaryReader binaryReader = CreateReader(bytes);
				protocol = binaryReader.ReadInt32();
				return binaryReader.BaseStream.Position == binaryReader.BaseStream.Length;
			}
			catch
			{
				return false;
			}
		}

		public static byte[] EncodeContribution(long epoch, double cumulative)
		{
			using MemoryStream memoryStream = new MemoryStream(24);
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(epoch);
			binaryWriter.Write(cumulative);
			return memoryStream.ToArray();
		}

		public static bool TryDecodeContribution(byte[] bytes, out ContributionData data)
		{
			data = default(ContributionData);
			try
			{
				using BinaryReader binaryReader = CreateReader(bytes);
				data = new ContributionData(binaryReader.ReadInt64(), binaryReader.ReadDouble());
				return binaryReader.BaseStream.Position == binaryReader.BaseStream.Length && !double.IsNaN(data.Cumulative) && !double.IsInfinity(data.Cumulative);
			}
			catch
			{
				return false;
			}
		}

		public static byte[] EncodeState(StateData state)
		{
			using MemoryStream memoryStream = new MemoryStream(96);
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(state.Epoch);
			binaryWriter.Write(state.Active);
			binaryWriter.Write(state.Pool);
			binaryWriter.Write(state.Capacity);
			binaryWriter.Write(state.ParticipantCount);
			binaryWriter.Write(state.HumanPlayers);
			binaryWriter.Write(state.ModdedPlayers);
			binaryWriter.Write(state.Acks.Count);
			foreach (AckData ack in state.Acks)
			{
				binaryWriter.Write(ack.PlayerId);
				binaryWriter.Write(ack.Cumulative);
			}
			return memoryStream.ToArray();
		}

		public static bool TryDecodeState(byte[] bytes, out StateData state)
		{
			state = new StateData();
			try
			{
				using BinaryReader binaryReader = CreateReader(bytes);
				state.Epoch = binaryReader.ReadInt64();
				state.Active = binaryReader.ReadBoolean();
				state.Pool = binaryReader.ReadDouble();
				state.Capacity = binaryReader.ReadDouble();
				state.ParticipantCount = binaryReader.ReadInt32();
				state.HumanPlayers = binaryReader.ReadInt32();
				state.ModdedPlayers = binaryReader.ReadInt32();
				int num = binaryReader.ReadInt32();
				if (num < 0 || num > 4)
				{
					return false;
				}
				for (int i = 0; i < num; i++)
				{
					state.Acks.Add(new AckData(binaryReader.ReadUInt64(), binaryReader.ReadDouble()));
				}
				return binaryReader.BaseStream.Position == binaryReader.BaseStream.Length && state.Capacity > 0.0 && !double.IsNaN(state.Pool) && !double.IsInfinity(state.Pool);
			}
			catch
			{
				return false;
			}
		}

		private static BinaryReader CreateReader(byte[] bytes)
		{
			return new BinaryReader(new MemoryStream(bytes, writable: false));
		}
	}
	[BepInPlugin("qlsl.gtfo.sharedstamina", "Shared Stamina", "0.1.2")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BasePlugin
	{
		public const string PluginGuid = "qlsl.gtfo.sharedstamina";

		public const string PluginName = "Shared Stamina";

		public const string PluginVersion = "0.1.2";

		internal static ManualLogSource Logger { get; private set; }

		internal static ConfigEntry<int> NetworkRate { get; private set; }

		internal static ConfigEntry<float> ClientTimeout { get; private set; }

		public override void Load()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			Logger = ((BasePlugin)this).Log;
			NetworkRate = ((BasePlugin)this).Config.Bind<int>("Network", "UpdatesPerSecond", 20, new ConfigDescription("Shared stamina network update rate.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 30), Array.Empty<object>()));
			ClientTimeout = ((BasePlugin)this).Config.Bind<float>("Network", "ClientTimeoutSeconds", 3f, new ConfigDescription("How long the host waits before treating a modded client as missing.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1.5f, 10f), Array.Empty<object>()));
			SharedStaminaController.Initialize();
			((BasePlugin)this).AddComponent<SharedStaminaBehaviour>();
			new Harmony("qlsl.gtfo.sharedstamina").PatchAll();
			ManualLogSource log = ((BasePlugin)this).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(90, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Shared Stamina");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("0.1.2");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded. Dynamic 1-4 player pool enabled; all participating humans must install the mod.");
			}
			log.LogInfo(val);
		}
	}
	public sealed class SharedStaminaBehaviour : MonoBehaviour
	{
		public void Update()
		{
			SharedStaminaController.Update();
		}
	}
	internal static class SharedStaminaController
	{
		private enum PacketKind
		{
			Hello,
			Contribution,
			State
		}

		private readonly struct InboundPacket
		{
			public PacketKind Kind { get; }

			public ulong Sender { get; }

			public byte[] Bytes { get; }

			public InboundPacket(PacketKind kind, ulong sender, byte[] bytes)
			{
				Kind = kind;
				Sender = sender;
				Bytes = bytes;
			}
		}

		private readonly struct ClientPresence
		{
			public int Protocol { get; }

			public float LastSeen { get; }

			public ClientPresence(int protocol, float lastSeen)
			{
				Protocol = protocol;
				LastSeen = lastSeen;
			}
		}

		internal const int ProtocolVersion = 2;

		private const string HelloEvent = "qlsl.sharedstamina.hello.v2";

		private const string ContributionEvent = "qlsl.sharedstamina.contribution.v2";

		private const string StateEvent = "qlsl.sharedstamina.state.v2";

		private static readonly ConcurrentQueue<InboundPacket> InboundPackets = new ConcurrentQueue<InboundPacket>();

		private static readonly Dictionary<ulong, ClientPresence> ClientPresenceById = new Dictionary<ulong, ClientPresence>();

		private static readonly Dictionary<ulong, double> HostCumulativeById = new Dictionary<ulong, double>();

		private static bool _initialized;

		private static bool _sessionPresent;

		private static bool _hostActive;

		private static bool _sharedActive;

		private static long _epoch;

		private static double _serverPool = 1.0;

		private static double _capacity = 1.0;

		private static double _predictedPool = 1.0;

		private static double _localCumulative;

		private static double _localAcknowledged;

		private static float _lastAppliedRatio = float.NaN;

		private static float _nextHelloTime;

		private static float _nextNetworkTime;

		private static int _humanPlayers;

		private static int _moddedPlayers;

		private static int _lastLoggedHumanPlayers = -1;

		private static int _lastLoggedModdedPlayers = -1;

		private static bool _loggedStaminaHook;

		internal static void Initialize()
		{
			if (!_initialized)
			{
				NetworkAPI.RegisterFreeSizedEvent("qlsl.sharedstamina.hello.v2", (Action<ulong, byte[]>)delegate(ulong sender, byte[] bytes)
				{
					InboundPackets.Enqueue(new InboundPacket(PacketKind.Hello, sender, bytes));
				});
				NetworkAPI.RegisterFreeSizedEvent("qlsl.sharedstamina.contribution.v2", (Action<ulong, byte[]>)delegate(ulong sender, byte[] bytes)
				{
					InboundPackets.Enqueue(new InboundPacket(PacketKind.Contribution, sender, bytes));
				});
				NetworkAPI.RegisterFreeSizedEvent("qlsl.sharedstamina.state.v2", (Action<ulong, byte[]>)delegate(ulong sender, byte[] bytes)
				{
					InboundPackets.Enqueue(new InboundPacket(PacketKind.State, sender, bytes));
				});
				LevelAPI.OnEnterLevel += ResetForLevel;
				LevelAPI.OnLevelCleanup += ResetAll;
				_initialized = true;
			}
		}

		internal static void Update()
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			bool flag = HasPlayableLocalPlayer();
			if (flag && !_sessionPresent)
			{
				ResetForLevel();
			}
			else if (!flag && _sessionPresent)
			{
				ResetAll();
			}
			_sessionPresent = flag;
			ProcessInbound(realtimeSinceStartup);
			if (!flag || !SNet.HasLocalPlayer)
			{
				return;
			}
			if (realtimeSinceStartup >= _nextHelloTime)
			{
				_nextHelloTime = realtimeSinceStartup + 1f;
				SendHello(realtimeSinceStartup);
			}
			if (SNet.IsMaster)
			{
				UpdateHostReadiness(realtimeSinceStartup);
			}
			float num = 1f / (float)Plugin.NetworkRate.Value;
			if (!(realtimeSinceStartup < _nextNetworkTime))
			{
				_nextNetworkTime = realtimeSinceStartup + num;
				if (_sharedActive)
				{
					SendContribution();
				}
				if (SNet.IsMaster)
				{
					BroadcastHostState();
				}
			}
		}

		internal static void AfterLocalStaminaUpdate(PlayerStamina stamina)
		{
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			if (!_sharedActive || !_sessionPresent || (Object)(object)stamina == (Object)null || (Object)(object)stamina.m_owner == (Object)null || !((Agent)stamina.m_owner).IsLocallyOwned)
			{
				_lastAppliedRatio = float.NaN;
				return;
			}
			float maxStamina = PlayerStamina.MaxStamina;
			if (maxStamina <= 0f || _capacity <= 0.0)
			{
				return;
			}
			float num = Mathf.Clamp01(stamina.StaminaRelative);
			if (float.IsNaN(_lastAppliedRatio))
			{
				_lastAppliedRatio = num;
				_predictedPool = ClampPool(_serverPool + (_localCumulative - _localAcknowledged));
			}
			else
			{
				float num2 = num - _lastAppliedRatio;
				if (Math.Abs(num2) <= 0.5f)
				{
					_localCumulative += num2;
					_predictedPool = ClampPool(_predictedPool + (double)num2);
				}
			}
			float num3 = (float)Math.Clamp(_predictedPool / _capacity, 0.0, 1.0);
			stamina.Stamina = num3 * maxStamina;
			_lastAppliedRatio = num3;
			if (!_loggedStaminaHook)
			{
				_loggedStaminaHook = true;
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(46, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Local stamina hook active: capacity=x");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(_capacity, "0");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ratio=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(num3, "0.000");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				logger.LogInfo(val);
			}
		}

		private static void ProcessInbound(float now)
		{
			InboundPacket result;
			while (InboundPackets.TryDequeue(out result))
			{
				switch (result.Kind)
				{
				case PacketKind.Hello:
					ProcessHello(result.Sender, result.Bytes, now);
					break;
				case PacketKind.Contribution:
					ProcessContributionPacket(result.Sender, result.Bytes);
					break;
				case PacketKind.State:
					ProcessStatePacket(result.Sender, result.Bytes);
					break;
				}
			}
		}

		private static void ProcessHello(ulong sender, byte[] bytes, float now)
		{
			if (SNet.IsMaster && PacketCodec.TryDecodeHello(bytes, out var protocol))
			{
				ClientPresenceById[sender] = new ClientPresence(protocol, now);
			}
		}

		private static void ProcessContributionPacket(ulong sender, byte[] bytes)
		{
			if (SNet.IsMaster && PacketCodec.TryDecodeContribution(bytes, out var data))
			{
				ApplyContribution(sender, data);
			}
		}

		private static void ProcessStatePacket(ulong sender, byte[] bytes)
		{
			if (!SNet.IsMaster && SNet.HasMaster && sender == SNet.Master.Lookup && PacketCodec.TryDecodeState(bytes, out StateData state))
			{
				ApplyState(state);
			}
		}

		private static void SendHello(float now)
		{
			ulong lookup = SNet.LocalPlayer.Lookup;
			if (SNet.IsMaster)
			{
				ClientPresenceById[lookup] = new ClientPresence(2, now);
			}
			else if (SNet.HasMaster)
			{
				NetworkAPI.InvokeFreeSizedEvent("qlsl.sharedstamina.hello.v2", PacketCodec.EncodeHello(), SNet.Master, (SNet_ChannelType)4);
			}
		}

		private static void SendContribution()
		{
			if (_epoch != 0L && SNet.HasLocalPlayer)
			{
				ContributionData contribution = new ContributionData(_epoch, _localCumulative);
				if (SNet.IsMaster)
				{
					ApplyContribution(SNet.LocalPlayer.Lookup, contribution);
				}
				else if (SNet.HasMaster)
				{
					NetworkAPI.InvokeFreeSizedEvent("qlsl.sharedstamina.contribution.v2", PacketCodec.EncodeContribution(contribution.Epoch, contribution.Cumulative), SNet.Master, (SNet_ChannelType)4);
				}
			}
		}

		private static void UpdateHostReadiness(float now)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Expected O, but got Unknown
			HashSet<ulong> humanPlayerIds = GetHumanPlayerIds();
			_humanPlayers = humanPlayerIds.Count;
			_moddedPlayers = 0;
			foreach (ulong item in humanPlayerIds)
			{
				if (ClientPresenceById.TryGetValue(item, out var value) && value.Protocol == 2 && now - value.LastSeen <= Plugin.ClientTimeout.Value)
				{
					_moddedPlayers++;
				}
			}
			bool flag = default(bool);
			if (_humanPlayers != _lastLoggedHumanPlayers || _moddedPlayers != _lastLoggedModdedPlayers)
			{
				ManualLogSource logger = Plugin.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(46, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Participant scan: humans=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_humanPlayers);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", modded=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(_moddedPlayers);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", protocol=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(2);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				logger.LogInfo(val);
				_lastLoggedHumanPlayers = _humanPlayers;
				_lastLoggedModdedPlayers = _moddedPlayers;
			}
			bool flag2 = _humanPlayers > 0 && _moddedPlayers == _humanPlayers;
			int num = Math.Clamp(_moddedPlayers, 1, 4);
			bool flag3 = Math.Abs(_capacity - (double)num) > 0.001;
			if (flag2 && (!_hostActive || flag3))
			{
				StartHostEpoch(num);
			}
			else if (!flag2 && _hostActive)
			{
				_hostActive = false;
				_sharedActive = false;
				_lastAppliedRatio = float.NaN;
				ManualLogSource logger2 = Plugin.Logger;
				BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(76, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Shared stamina paused: every participating human needs protocol ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(2);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" (modded ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(_moddedPlayers);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("/");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<int>(_humanPlayers);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(").");
				}
				logger2.LogWarning(val2);
			}
		}

		private static void StartHostEpoch(int participantCount)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			double num = ((_sharedActive && _capacity > 0.0) ? Math.Clamp(_predictedPool / _capacity, 0.0, 1.0) : 1.0);
			_epoch = DateTime.UtcNow.Ticks ^ (long)SNet.LocalPlayer.Lookup;
			if (_epoch == 0L)
			{
				_epoch = 1L;
			}
			_capacity = Math.Clamp(participantCount, 1, 4);
			_serverPool = num * _capacity;
			_predictedPool = _serverPool;
			_localCumulative = 0.0;
			_localAcknowledged = 0.0;
			_lastAppliedRatio = float.NaN;
			_loggedStaminaHook = false;
			HostCumulativeById.Clear();
			_hostActive = true;
			_sharedActive = true;
			ManualLogSource logger = Plugin.Logger;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(59, 3, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Shared stamina activated: participants=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(participantCount);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", capacity=x");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(_capacity, "0");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", pool=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(_serverPool, "0.000");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
			}
			logger.LogInfo(val);
		}

		private static void ApplyContribution(ulong sender, ContributionData contribution)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			if (!_hostActive || contribution.Epoch != _epoch || !IsCurrentHuman(sender) || !ClientPresenceById.TryGetValue(sender, out var value) || value.Protocol != 2)
			{
				return;
			}
			HostCumulativeById.TryGetValue(sender, out var value2);
			double num = contribution.Cumulative - value2;
			if (Math.Abs(num) > 2.0)
			{
				ManualLogSource logger = Plugin.Logger;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(41, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Rejected suspicious stamina delta ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<double>(num, "0.000");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" from ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<ulong>(sender);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
				}
				logger.LogWarning(val);
			}
			else
			{
				HostCumulativeById[sender] = contribution.Cumulative;
				_serverPool = ClampPool(_serverPool + num);
			}
		}

		private static void BroadcastHostState()
		{
			StateData state = BuildHostState();
			ApplyState(state);
			NetworkAPI.InvokeFreeSizedEvent("qlsl.sharedstamina.state.v2", PacketCodec.EncodeState(state), (SNet_ChannelType)4);
		}

		private static StateData BuildHostState()
		{
			StateData stateData = new StateData
			{
				Epoch = _epoch,
				Active = _hostActive,
				Pool = _serverPool,
				Capacity = _capacity,
				ParticipantCount = (int)_capacity,
				HumanPlayers = _humanPlayers,
				ModdedPlayers = _moddedPlayers
			};
			foreach (KeyValuePair<ulong, double> item in HostCumulativeById)
			{
				stateData.Acks.Add(new AckData(item.Key, item.Value));
			}
			return stateData;
		}

		private static void ApplyState(StateData state)
		{
			_humanPlayers = state.HumanPlayers;
			_moddedPlayers = state.ModdedPlayers;
			if (!state.Active)
			{
				_sharedActive = false;
				_lastAppliedRatio = float.NaN;
				return;
			}
			if (state.Epoch != _epoch)
			{
				_epoch = state.Epoch;
				_localCumulative = 0.0;
				_localAcknowledged = 0.0;
				_lastAppliedRatio = float.NaN;
			}
			_capacity = Math.Max(1.0, state.Capacity);
			_serverPool = Math.Clamp(state.Pool, 0.0, _capacity);
			_sharedActive = true;
			ulong num = (SNet.HasLocalPlayer ? SNet.LocalPlayer.Lookup : 0);
			double localAcknowledged = 0.0;
			foreach (AckData ack in state.Acks)
			{
				if (ack.PlayerId == num)
				{
					localAcknowledged = ack.Cumulative;
					break;
				}
			}
			_localAcknowledged = localAcknowledged;
			_predictedPool = ClampPool(_serverPool + (_localCumulative - _localAcknowledged));
		}

		private static HashSet<ulong> GetHumanPlayerIds()
		{
			HashSet<ulong> hashSet = new HashSet<ulong>();
			List<SNet_Player> playing = SNet_PlayerSendGroupManager.Playing;
			if (playing == null)
			{
				return hashSet;
			}
			for (int i = 0; i < playing.Count; i++)
			{
				SNet_Player val = playing[i];
				if ((Object)(object)val != (Object)null && !val.IsBot && val.IsInGame)
				{
					hashSet.Add(val.Lookup);
				}
			}
			return hashSet;
		}

		private static bool IsCurrentHuman(ulong playerId)
		{
			return GetHumanPlayerIds().Contains(playerId);
		}

		private static bool HasPlayableLocalPlayer()
		{
			try
			{
				return SNet.HasLocalPlayer && PlayerManager.HasLocalPlayerAgent();
			}
			catch
			{
				return false;
			}
		}

		private static double ClampPool(double value)
		{
			return Math.Clamp(value, 0.0, Math.Max(1.0, _capacity));
		}

		private static void ResetForLevel()
		{
			_sessionPresent = true;
			_hostActive = false;
			_sharedActive = false;
			_epoch = 0L;
			_capacity = 1.0;
			_serverPool = 1.0;
			_predictedPool = 1.0;
			_localCumulative = 0.0;
			_localAcknowledged = 0.0;
			_lastAppliedRatio = float.NaN;
			_nextHelloTime = 0f;
			_nextNetworkTime = 0f;
			_humanPlayers = 0;
			_moddedPlayers = 0;
			_lastLoggedHumanPlayers = -1;
			_lastLoggedModdedPlayers = -1;
			_loggedStaminaHook = false;
			ClientPresenceById.Clear();
			HostCumulativeById.Clear();
			InboundPacket result;
			while (InboundPackets.TryDequeue(out result))
			{
			}
		}

		private static void ResetAll()
		{
			ResetForLevel();
			_sessionPresent = false;
		}
	}
	[HarmonyPatch(typeof(PlayerStamina), "LateUpdate")]
	internal static class StaminaPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerStamina __instance)
		{
			SharedStaminaController.AfterLocalStaminaUpdate(__instance);
		}
	}
}