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