Decompiled source of SyncFix v1.0.0

syncfix.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LLBML.Messages;
using LLBML.Players;
using LLBML.Settings;
using LLBML.Utils;
using Microsoft.CodeAnalysis;
using Multiplayer;
using SyncFix.FrameRecorder;
using SyncFix.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("ca.gov.mechasoulindustries.llb.my.cute.syncfix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+7c7594535044a6becc0877c0ef102699d7f49cef")]
[assembly: AssemblyProduct("Sync Fix")]
[assembly: AssemblyTitle("ca.gov.mechasoulindustries.llb.my.cute.syncfix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 SyncFix
{
	public class FrameAccumulator
	{
		public readonly int playerIndex;

		public readonly float updateRate;

		public readonly float threshold;

		public float currentValue = -1f;

		public float accumulator;

		public float upperBound;

		public float lowerBound;

		public Func<int, int, float> upperBoundFunc;

		public Func<int, int, float> lowerBoundFunc;

		public FrameAccumulator(int playerIndex, float updateRate, float threshold)
		{
			this.playerIndex = playerIndex;
			this.updateRate = updateRate;
			this.threshold = threshold;
		}

		public void Reset()
		{
			currentValue = -1f;
			accumulator = 0f;
		}

		private void UpdateValue(float newValue)
		{
			if (currentValue == -1f)
			{
				currentValue = newValue;
			}
			else
			{
				currentValue = Mathf.Lerp(currentValue, newValue, updateRate);
			}
		}

		public void FrameUpdate(int frame, float frameValue)
		{
			upperBound = upperBoundFunc(frame, playerIndex);
			lowerBound = lowerBoundFunc(frame, playerIndex);
			_ = currentValue;
			UpdateValue(frameValue);
			if (currentValue > upperBound)
			{
				float num = currentValue - upperBound;
				accumulator += num;
			}
			else if (currentValue < lowerBound)
			{
				float num2 = currentValue - lowerBound;
				accumulator = Math.Max(accumulator + num2, 0f);
			}
		}

		public bool ThresholdReached()
		{
			return accumulator >= threshold;
		}

		public bool ThresholdVeryReached()
		{
			return accumulator >= threshold * 10f;
		}
	}
	public interface ITimeSyncComponent
	{
		void FrameUpdate();

		float GetSleepInterval();

		void OnSleep(float frames);

		void Reset();

		bool ShouldEmergencySleep();
	}
	[BepInPlugin("ca.gov.mechasoulindustries.llb.my.cute.syncfix", "Sync Fix", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("LLBlaze.exe")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		private Harmony _harmony;

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			SyncFixConfig.LoadConfig(((BaseUnityPlugin)this).Config);
			PathUtils.Init(((BaseUnityPlugin)this).Info);
			_harmony = new Harmony("ca.gov.mechasoulindustries.llb.my.cute.syncfix");
			_harmony.PatchAll();
			Logger.LogInfo((object)"Plugin ca.gov.mechasoulindustries.llb.my.cute.syncfix is loaded!");
		}

		private void Start()
		{
			StateManager.RegisterLobbyMessages();
			SyncFixManager.RegisterGameMessages();
			ModDependenciesUtils.RegisterToModMenu(((BaseUnityPlugin)this).Info, new List<string> { "Fixes host advantage" });
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public class RollbackStats
	{
		private static int numRollbacks = 0;

		private static float total = 0f;

		private static float recentSize = -1f;

		private static int numSleeps = 0;

		public static int NumRollbacks => numRollbacks;

		public static float Average => total / (float)NumRollbacks;

		public static float RecentSize => recentSize;

		public static int NumSleeps => numSleeps;

		public static void Reset()
		{
			numRollbacks = 0;
			total = 0f;
			recentSize = -1f;
			numSleeps = 0;
		}

		public static void AddRollback(int size)
		{
			numRollbacks++;
			total += size;
			if (recentSize == -1f)
			{
				recentSize = size;
			}
			else
			{
				recentSize = Mathf.Lerp(recentSize, (float)size, 0.1f);
			}
		}

		public static void AddSleep(float size)
		{
			numSleeps++;
		}

		public static string GetStats()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("rollbacks: ");
			stringBuilder.Append(NumRollbacks);
			stringBuilder.AppendLine();
			stringBuilder.Append("average: ");
			stringBuilder.Append(Average);
			stringBuilder.AppendLine();
			stringBuilder.Append("recent: ");
			stringBuilder.Append(RecentSize);
			stringBuilder.AppendLine();
			stringBuilder.Append("sleeps: ");
			stringBuilder.Append(NumSleeps);
			stringBuilder.AppendLine();
			return stringBuilder.ToString();
		}
	}
	public class StateManager
	{
		public enum SyncFixMode
		{
			SOLO,
			GROUP
		}

		public enum LobbyPeerModStatus
		{
			UNKNOWN,
			CONFIRMED
		}

		public static SyncFixMode CurrentMode = SyncFixMode.SOLO;

		private static readonly LobbyPeerModStatus[] peerModStatus = new LobbyPeerModStatus[4];

		public static bool HostHasSyncFix = false;

		public static void RegisterLobbyMessages()
		{
			MessageApi.RegisterCustomMessage(((BaseUnityPlugin)Plugin.Instance).Info, (ushort)5040, SyncFixMessages.LOBBY_MOD_CHECK.ToString(), (Action<Message>)ReceiveModCheck);
			MessageApi.RegisterCustomMessage(((BaseUnityPlugin)Plugin.Instance).Info, (ushort)5041, SyncFixMessages.LOBBY_MOD_REPLY.ToString(), (Action<Message>)ReceiveModReply);
			MessageApi.RegisterCustomMessage(((BaseUnityPlugin)Plugin.Instance).Info, (ushort)5042, SyncFixMessages.GAME_USE_GROUP.ToString(), (Action<Message>)ReceiveGroupMessage);
		}

		public static void PeerJoined(int playerIndex)
		{
			if (ShouldManageState())
			{
				if (IsLocalPeer(playerIndex))
				{
					SetPeerModStatus(playerIndex, LobbyPeerModStatus.CONFIRMED);
				}
				else
				{
					SendModCheck(playerIndex);
				}
			}
		}

		public static void PeerLeft(int playerIndex)
		{
			if (ShouldManageState())
			{
				SetPeerModStatus(playerIndex, LobbyPeerModStatus.UNKNOWN);
			}
		}

		public static void SendModCheck(int playerIndex)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldManageState())
			{
				P2P.SendToPlayerNr(playerIndex, new Message((Msg)5040, ((Peer)P2P.localPeer).playerNr, -1, (object)null, -1));
				Plugin.Logger.LogInfo((object)$"sent mod check to player {playerIndex}");
			}
		}

		public static void ReceiveModCheck(Message message)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if (SyncFixConfig.Instance.Enabled)
			{
				if (message.playerNr == 0)
				{
					HostHasSyncFix = true;
				}
				P2P.SendToPlayerNr(message.playerNr, new Message((Msg)5041, ((Peer)P2P.localPeer).playerNr, -1, (object)null, -1));
				Plugin.Logger.LogInfo((object)$"received mod check from player {message.playerNr}, sent reply");
			}
		}

		public static void ReceiveModReply(Message message)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldManageState())
			{
				SetPeerModStatus(message.playerNr, LobbyPeerModStatus.CONFIRMED);
				Plugin.Logger.LogInfo((object)$"received mod reply from player {message.playerNr}, set CONFIRMED");
			}
		}

		public static bool AllPeersConfirmed()
		{
			bool flag = !peerModStatus.Where((LobbyPeerModStatus status, int index) => Player.GetPlayer(index).IsInMatch && status == LobbyPeerModStatus.UNKNOWN).Any();
			Plugin.Logger.LogInfo((object)$"all peers confirmed: {flag}");
			return flag;
		}

		public static void SendAllGroupMessage()
		{
			if (ShouldManageState())
			{
				ForAllRemotePeersInMatch(delegate(int i)
				{
					//IL_0013: Unknown result type (might be due to invalid IL or missing references)
					P2P.SendToPlayerNr(i, new Message((Msg)5042, ((Peer)P2P.localPeer).playerNr, -1, (object)null, -1));
				});
				CurrentMode = SyncFixMode.GROUP;
				Plugin.Logger.LogInfo((object)"sent all peers group message");
			}
		}

		public static void ReceiveGroupMessage(Message message)
		{
			if (SyncFixConfig.Instance.Enabled)
			{
				CurrentMode = SyncFixMode.GROUP;
				Plugin.Logger.LogInfo((object)"received group message");
			}
		}

		public static bool IsUsingGroup()
		{
			return CurrentMode == SyncFixMode.GROUP;
		}

		private static void SetPeerModStatus(int playerIndex, LobbyPeerModStatus status)
		{
			if (ShouldManageState())
			{
				peerModStatus[playerIndex] = status;
			}
		}

		public static void ResetState()
		{
			ResetPeerModStatus();
			ResetMode();
		}

		public static void ResetPeerModStatus()
		{
			if (SyncFixConfig.Instance.Enabled)
			{
				for (int i = 0; i < peerModStatus.Length; i++)
				{
					SetPeerModStatus(i, (i == ((Peer)P2P.localPeer).playerNr) ? LobbyPeerModStatus.CONFIRMED : LobbyPeerModStatus.UNKNOWN);
				}
				if (P2P.isHost)
				{
					HostHasSyncFix = true;
				}
				else
				{
					HostHasSyncFix = false;
				}
				Plugin.Logger.LogInfo((object)("reset status: " + string.Join(", ", peerModStatus.Select((LobbyPeerModStatus status) => status.ToString()).ToArray())));
			}
		}

		public static void ResetMode()
		{
			CurrentMode = SyncFixMode.SOLO;
		}

		private static bool IsLocalPeer(int playerIndex)
		{
			return playerIndex == ((Peer)P2P.localPeer).playerNr;
		}

		private static bool ShouldManageState()
		{
			if (P2P.isHost)
			{
				return SyncFixConfig.Instance.Enabled;
			}
			return false;
		}

		private static void ForAllRemotePeersInMatch(Action<int> action)
		{
			Player.ForAllInMatch((Action<Player>)delegate(Player player)
			{
				if (!IsLocalPeer(player.nr))
				{
					action(player.nr);
				}
			});
		}
	}
	public class SyncFixConfig
	{
		private static SyncFixConfig instance;

		private readonly ConfigEntry<bool> enabled;

		private readonly ConfigEntry<bool> showDebugInfo;

		private readonly ConfigEntry<KeyCode> debugInfoKey;

		private readonly ConfigEntry<bool> recordDebugInfo;

		public static SyncFixConfig Instance
		{
			get
			{
				return instance;
			}
			private set
			{
				instance = value;
			}
		}

		public bool Enabled
		{
			get
			{
				return enabled.Value;
			}
			set
			{
				enabled.Value = value;
			}
		}

		public bool ShowDebugInfo
		{
			get
			{
				return showDebugInfo.Value;
			}
			set
			{
				showDebugInfo.Value = value;
			}
		}

		public KeyCode DebugInfoKey
		{
			get
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				return debugInfoKey.Value;
			}
			set
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				debugInfoKey.Value = value;
			}
		}

		public bool RecordDebugInfo
		{
			get
			{
				return recordDebugInfo.Value;
			}
			set
			{
				recordDebugInfo.Value = value;
			}
		}

		private SyncFixConfig(ConfigFile configFile)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			enabled = configFile.Bind<bool>(new ConfigDefinition("Sync Fix", "Enable host advantage fix"), true, (ConfigDescription)null);
			showDebugInfo = configFile.Bind<bool>(new ConfigDefinition("Sync Fix", "Show debug info ingame"), false, (ConfigDescription)null);
			debugInfoKey = configFile.Bind<KeyCode>("Sync Fix", "Toggle debug info key", (KeyCode)0, (ConfigDescription)null);
			recordDebugInfo = configFile.Bind<bool>(new ConfigDefinition("Sync Fix", "Save debug info to disk at match end"), false, (ConfigDescription)null);
		}

		internal static void LoadConfig(ConfigFile configFile)
		{
			if (Instance != null)
			{
				throw new InvalidOperationException("config already loaded");
			}
			configFile.SaveOnConfigSet = true;
			Instance = new SyncFixConfig(configFile);
		}
	}
	public class SyncFixManager
	{
		[CompilerGenerated]
		private sealed class <CSendSelfTimeAlignAfterDelay>d__32 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public float initialDelay;

			public float time;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CSendSelfTimeAlignAfterDelay>d__32(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Expected O, but got Unknown
				//IL_007e: Unknown result type (might be due to invalid IL or missing references)
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					Plugin.Logger.LogInfo((object)$"delaying self-timesync by {initialDelay}");
					<>2__current = (object)new WaitForSeconds(initialDelay);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					P2P.SendToPlayerNr(((Peer)P2P.localPeer).playerNr, new Message((Msg)189, Sync.matchNr, Mathf.RoundToInt(time * 1000f), (object)null, -1));
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly SyncFixManager instance;

		public static readonly int GROUP_SLEEP_CHECK_INTERVAL;

		public static readonly int GROUP_ADVANTAGE_UPDATE_INTERVAL;

		public TimeSync[] timeSync = new TimeSync[4]
		{
			new TimeSync(0),
			new TimeSync(1),
			new TimeSync(2),
			new TimeSync(3)
		};

		private int nextAdvantageUpdate = int.MaxValue;

		private int nextRecommendedSleep = int.MaxValue;

		private int lastSleep = -1;

		public static SyncFixManager Instance => instance;

		public int NextAdvantageUpdate => nextAdvantageUpdate;

		public int NextRecommendedSleep => nextRecommendedSleep;

		public int LastSleep => lastSleep;

		static SyncFixManager()
		{
			instance = new SyncFixManager();
			GROUP_SLEEP_CHECK_INTERVAL = 120;
			GROUP_ADVANTAGE_UPDATE_INTERVAL = 60;
		}

		public static void RegisterGameMessages()
		{
			MessageApi.RegisterCustomMessage(((BaseUnityPlugin)Plugin.Instance).Info, (ushort)5043, SyncFixMessages.GAME_LOCAL_ADVANTAGE.ToString(), (Action<Message>)Instance.ReceiveRemoteAdvantage);
		}

		public void Reset()
		{
			if (SyncFixConfig.Instance.Enabled)
			{
				for (int i = 0; i < Sync.nPlayers; i++)
				{
					timeSync[i].Reset();
				}
				nextAdvantageUpdate = int.MaxValue;
				nextRecommendedSleep = int.MaxValue;
				lastSleep = -1;
			}
		}

		public void Start()
		{
			if (SyncFixConfig.Instance.Enabled)
			{
				UpdateNextRecommendedSleep();
				UpdateNextAdvantageTime();
			}
		}

		public void MidMatchReset()
		{
			if (SyncFixConfig.Instance.Enabled)
			{
				for (int i = 0; i < Sync.nPlayers; i++)
				{
					timeSync[i].ResetActiveComponent();
				}
				UpdateNextAdvantageTime();
				UpdateNextRecommendedSleep();
				lastSleep = -1;
			}
		}

		public void UpdateNextRecommendedSleep()
		{
			nextRecommendedSleep = Sync.curFrame + GROUP_SLEEP_CHECK_INTERVAL;
		}

		public void UpdateLastSleep()
		{
			lastSleep = Sync.curFrame;
		}

		public void OnSleep(float sleepDuration)
		{
			UpdateNextRecommendedSleep();
			UpdateLastSleep();
			if (StateManager.IsUsingGroup())
			{
				ForAllValidOthers(delegate(int i)
				{
					float frames = sleepDuration * (float)World.FPS;
					timeSync[i].OnSleep(frames);
					SendLocalAdvantageToPlayer(i, notifySleep: true);
				});
			}
			UpdateNextAdvantageTime();
		}

		public void UpdateNextAdvantageTime()
		{
			nextAdvantageUpdate = Sync.curFrame + GROUP_ADVANTAGE_UPDATE_INTERVAL;
		}

		public float GetRecommendedSleepInterval(int playerIndex)
		{
			if (!SyncFixConfig.Instance.Enabled)
			{
				throw new InvalidOperationException("asked for sleep interval when sync fix disabled?");
			}
			return timeSync[playerIndex].GetSleepInterval();
		}

		public float GetCurrentLocalAdvantage(int playerIndex)
		{
			if (!SyncFixConfig.Instance.Enabled)
			{
				throw new InvalidOperationException("asked for local advantage when sync fix disabled?");
			}
			return timeSync[playerIndex].GetCurrentLocalAdvantage();
		}

		public void SendLocalAdvantageToPlayer(int i, bool notifySleep = false)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (SyncFixConfig.Instance.Enabled)
			{
				byte[] bytes = BitConverter.GetBytes(GetCurrentLocalAdvantage(i));
				Message val = default(Message);
				((Message)(ref val))..ctor((Msg)5043, ((Peer)P2P.localPeer).playerNr, notifySleep ? 1 : 0, (object)bytes, bytes.Length);
				P2P.SendToPlayerNr(i, val);
			}
		}

		public void ReceiveRemoteAdvantage(Message message)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (SyncFixConfig.Instance.Enabled)
			{
				float remoteAdvantage = BitConverter.ToSingle((byte[])message.ob, 0);
				UpdateRemoteAdvantage(message.playerNr, remoteAdvantage);
			}
		}

		public void UpdateRemoteAdvantage(int playerIndex, float remoteAdvantage)
		{
			timeSync[playerIndex].UpdateRemoteFrameAdvantage(remoteAdvantage);
		}

		public void GroupAlignTimes()
		{
			if (Sync.isAwaiting)
			{
				return;
			}
			ForAllValidOthers(delegate(int i)
			{
				timeSync[i].FrameUpdate();
			});
			bool flag = Sync.curFrame > NextRecommendedSleep;
			for (int j = 0; j < Sync.nPlayers; j++)
			{
				if (Sync.IsValidOther(j))
				{
					flag = flag || timeSync[j].ShouldEmergencySleep();
					if (flag)
					{
						break;
					}
				}
			}
			if (!flag || Sync.isAwaiting)
			{
				return;
			}
			float num = 0f;
			for (int k = 0; k < Sync.nPlayers; k++)
			{
				if (Sync.othersInfo[k] != null)
				{
					num = Math.Max(num, GetRecommendedSleepInterval(k));
				}
			}
			if (num > 0f)
			{
				Plugin.Logger.LogInfo((object)$"waiting for {num}s");
				P2P.Wait(num);
			}
		}

		public void SoloHostAlignTimes()
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			if (Sync.isAwaiting)
			{
				return;
			}
			float num = float.MaxValue;
			for (int i = 0; i < Sync.nPlayers; i++)
			{
				timeSync[i].FrameUpdate();
				if (timeSync[i].GetCurrentFrameEstimate() < num)
				{
					num = timeSync[i].GetCurrentFrameEstimate();
				}
			}
			for (int j = 0; j < Sync.nPlayers; j++)
			{
				timeSync[j].UpdateRunAheadEstimate(num);
				if (!timeSync[j].CanSleep())
				{
					continue;
				}
				float sleepInterval = timeSync[j].GetSleepInterval();
				if (sleepInterval > 0f)
				{
					Plugin.Logger.LogInfo((object)$"sleeping p{j + 1} for {sleepInterval}");
					timeSync[j].OnSleep(sleepInterval * (float)World.FPS);
					if (j == 0)
					{
						SendSelfTimeAlignAfterDelay(sleepInterval);
					}
					else
					{
						P2P.SendToPlayerNr(j, new Message((Msg)189, Sync.matchNr, Mathf.RoundToInt(sleepInterval * 1000f), (object)null, -1));
					}
				}
			}
		}

		private static void SendSelfTimeAlignAfterDelay(float time)
		{
			float num = (from player in Player.EPlayers()
				where player.LAADACKBGLL() && Sync.IsValidOther(player.CJFLMDNNMIE)
				select player).Average((ALDOKEMAOMB player) => player.KLEEADMGHNE.ping);
			num *= 0.49f;
			((MonoBehaviour)P2P.instance).StartCoroutine(CSendSelfTimeAlignAfterDelay(num, time));
		}

		private static IEnumerator CSendSelfTimeAlignAfterDelay(float initialDelay, float time)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CSendSelfTimeAlignAfterDelay>d__32(0)
			{
				initialDelay = initialDelay,
				time = time
			};
		}

		public static void ForAllValidOthers(Action<int> action)
		{
			for (int i = 0; i < Sync.nPlayers; i++)
			{
				if (Sync.IsValidOther(i))
				{
					action(i);
				}
			}
		}
	}
	public enum SyncFixMessages
	{
		LOBBY_MOD_CHECK = 5040,
		LOBBY_MOD_REPLY,
		GAME_USE_GROUP,
		GAME_LOCAL_ADVANTAGE
	}
	public class TimeSync
	{
		protected readonly int playerIndex;

		private readonly TimeSyncGroupComponent groupComponent;

		private readonly TimeSyncSoloHostComponent soloComponent;

		private ITimeSyncComponent activeComponent;

		public TimeSync(int playerIndex)
		{
			this.playerIndex = playerIndex;
			groupComponent = new TimeSyncGroupComponent(playerIndex);
			soloComponent = new TimeSyncSoloHostComponent(playerIndex);
			SetActiveComponent();
		}

		public void Reset()
		{
			SetActiveComponent();
			activeComponent.Reset();
		}

		public void ResetActiveComponent()
		{
			activeComponent.Reset();
		}

		public void FrameUpdate()
		{
			activeComponent.FrameUpdate();
		}

		public float GetSleepInterval()
		{
			return activeComponent.GetSleepInterval();
		}

		public void OnSleep(float frames)
		{
			activeComponent.OnSleep(frames);
		}

		public void UpdateRemoteFrameAdvantage(float remoteAdvantage)
		{
			groupComponent.UpdateRemoteFrameAdvantage(remoteAdvantage);
		}

		public float GetCurrentLocalAdvantage()
		{
			return groupComponent.CurrentLocalAdvantage;
		}

		public void UpdateRunAheadEstimate(float minimumFrame)
		{
			soloComponent.UpdateRunAheadEstimate(minimumFrame);
		}

		public float GetCurrentFrameEstimate()
		{
			return soloComponent.CurrentFrameEstimate;
		}

		public bool CanSleep()
		{
			if (Sync.curFrame <= soloComponent.NextRecommendedSleep)
			{
				return soloComponent.ShouldEmergencySleep();
			}
			return true;
		}

		public bool ShouldEmergencySleep()
		{
			return activeComponent.ShouldEmergencySleep();
		}

		public void SetActiveComponent()
		{
			if (StateManager.IsUsingGroup())
			{
				activeComponent = groupComponent;
			}
			else
			{
				activeComponent = soloComponent;
			}
		}
	}
	public abstract class TimeSyncComponentBase : ITimeSyncComponent
	{
		protected static readonly float RECENT_SLEEP_WINDOW_HOST = 1800f;

		protected static readonly float RECENT_SLEEP_WINDOW_CLIENT = 1800f;

		protected static readonly float ALIGN_TIMES_FACTOR = 0.8f;

		protected readonly int playerIndex;

		protected readonly Queue<int> recentSleeps = new Queue<int>();

		public TimeSyncComponentBase(int playerIndex)
		{
			this.playerIndex = playerIndex;
		}

		public abstract float GetSleepInterval();

		public abstract bool ShouldEmergencySleep();

		protected abstract float GetRecentSleepBaseFactor();

		protected abstract float GetRecentSleepWindow();

		public virtual void Reset()
		{
			recentSleeps.Clear();
		}

		public virtual void FrameUpdate()
		{
			while (recentSleeps.Count > 0 && (float)Sync.curFrame > (float)recentSleeps.Peek() + GetRecentSleepWindow())
			{
				recentSleeps.Dequeue();
			}
		}

		public virtual void OnSleep(float frames)
		{
			recentSleeps.Enqueue(Sync.curFrame);
		}

		protected float GetRecentSleepFactor()
		{
			float num = 0f;
			foreach (int recentSleep in recentSleeps)
			{
				num += (GetRecentSleepWindow() - (float)(Sync.curFrame - recentSleep)) / GetRecentSleepWindow() * GetRecentSleepBaseFactor();
			}
			return num;
		}

		protected static float LinearClamped(float x, float xStart, float xEnd, float yMin, float yMax)
		{
			return Mathf.Clamp((yMax - yMin) / (xEnd - xStart) * (x - xStart) + yMin, yMin, yMax);
		}
	}
	public class TimeSyncGroupComponent : TimeSyncComponentBase
	{
		private static readonly float MAX_SLEEP_DURATION = 0.5f;

		private static readonly float MIN_SLEEP_DURATION = World.DELTA_TIME;

		private static readonly float LOCAL_ADVANTAGE_UPDATE_RATE = 0.1f;

		private static readonly float RECENT_SLEEP_BASE_FACTOR = 0.25f;

		private float currentLocalAdvantage;

		private float currentRemoteAdvantage;

		private float lastRemoteAdvantage;

		private readonly FrameAccumulator accumulator;

		public float CurrentLocalAdvantage => currentLocalAdvantage;

		public float CurrentRemoteAdvantage => currentRemoteAdvantage;

		public TimeSyncGroupComponent(int playerIndex)
			: base(playerIndex)
		{
			accumulator = new FrameAccumulator(base.playerIndex, 0.25f, 1.5f);
			accumulator.upperBoundFunc = (int frame, int i) => TimeSyncComponentBase.LinearClamped(NetUtils.MaxPing, 0.03f, 0.3f, 0.5f, 0.7f) * (1f + GetRecentSleepFactor());
			accumulator.lowerBoundFunc = (int frame, int i) => TimeSyncComponentBase.LinearClamped(NetUtils.MaxPing, 0.03f, 0.3f, 0.1f, 0.4f);
		}

		public override void Reset()
		{
			currentLocalAdvantage = 0f;
			currentRemoteAdvantage = 0f;
			lastRemoteAdvantage = 0f;
			accumulator.Reset();
			base.Reset();
		}

		public override void FrameUpdate()
		{
			int curFrame = Sync.curFrame;
			float num = Sync.statusInput.otherReceived[playerIndex];
			if (!(num < 0f))
			{
				float travelTimeEstimate = NetUtils.GetTravelTimeEstimate(Sync.othersInfo[playerIndex].peer.ping);
				float num2 = num + travelTimeEstimate - (float)curFrame;
				currentLocalAdvantage = Mathf.Lerp(CurrentLocalAdvantage, num2, LOCAL_ADVANTAGE_UPDATE_RATE);
				currentRemoteAdvantage = Mathf.Lerp(CurrentRemoteAdvantage, lastRemoteAdvantage, 0.2f);
				accumulator.FrameUpdate(Sync.curFrame, (CurrentRemoteAdvantage - CurrentLocalAdvantage) / 2f);
				base.FrameUpdate();
			}
		}

		public override float GetSleepInterval()
		{
			if (!accumulator.ThresholdReached())
			{
				return 0f;
			}
			return Mathf.Clamp(accumulator.currentValue * World.DELTA_TIME * TimeSyncComponentBase.ALIGN_TIMES_FACTOR, MIN_SLEEP_DURATION, MAX_SLEEP_DURATION);
		}

		public override bool ShouldEmergencySleep()
		{
			return accumulator.ThresholdVeryReached();
		}

		public override void OnSleep(float frames)
		{
			currentLocalAdvantage += frames;
			currentRemoteAdvantage -= frames;
			lastRemoteAdvantage -= frames;
			accumulator.Reset();
			base.OnSleep(frames);
		}

		public void UpdateRemoteFrameAdvantage(float remoteAdvantage)
		{
			lastRemoteAdvantage = remoteAdvantage;
		}

		protected override float GetRecentSleepBaseFactor()
		{
			return RECENT_SLEEP_BASE_FACTOR;
		}

		protected override float GetRecentSleepWindow()
		{
			return TimeSyncComponentBase.RECENT_SLEEP_WINDOW_CLIENT;
		}
	}
	public class TimeSyncSoloHostComponent : TimeSyncComponentBase
	{
		private static readonly float MAX_SLEEP_DURATION = 0.5f;

		private static readonly float MIN_SLEEP_DURATION = World.DELTA_TIME;

		private static readonly int ESTIMATE_SLEEP_CHECK_INTERVAL = 120;

		private static readonly float RUN_AHEAD_UPDATE_RATE = 0.1f;

		private static readonly float RUN_AHEAD_ACCUMULATOR_THRESHOLD = 1.5f;

		private static readonly float RECENT_SLEEP_BASE_FACTOR = 0.3f;

		private int nextRecommendedSleep = int.MaxValue;

		private float currentFrameEstimate = -1f;

		private int noRunAheadUpdatesUntil = -1;

		private readonly FrameAccumulator accumulator;

		public int NextRecommendedSleep => nextRecommendedSleep;

		public float CurrentFrameEstimate => currentFrameEstimate;

		public float RunAheadEstimate => accumulator.currentValue;

		public TimeSyncSoloHostComponent(int playerIndex)
			: base(playerIndex)
		{
			accumulator = new FrameAccumulator(base.playerIndex, RUN_AHEAD_UPDATE_RATE, RUN_AHEAD_ACCUMULATOR_THRESHOLD);
			accumulator.upperBoundFunc = (int frame, int i) => TimeSyncComponentBase.LinearClamped(NetUtils.MaxPing, 0.03f, 0.3f, 0.55f, 1.08f) * (1f + GetRecentSleepFactor());
			accumulator.lowerBoundFunc = (int frame, int i) => TimeSyncComponentBase.LinearClamped(NetUtils.MaxPing, 0.03f, 0.3f, 0.25f, 0.5f);
		}

		public override void Reset()
		{
			nextRecommendedSleep = 60;
			currentFrameEstimate = -1f;
			noRunAheadUpdatesUntil = -1;
			accumulator.Reset();
			base.Reset();
		}

		public override void FrameUpdate()
		{
			currentFrameEstimate = EstimateCurrentFrame();
			base.FrameUpdate();
		}

		public float EstimateCurrentFrame()
		{
			if (playerIndex == 0)
			{
				return Sync.curFrame;
			}
			float travelTimeEstimate = NetUtils.GetTravelTimeEstimate(Player.GetPlayer(playerIndex).peer.ping);
			return (float)Sync.statusInput.otherReceived[playerIndex] + travelTimeEstimate;
		}

		public void UpdateRunAheadEstimate(float minimumFrame)
		{
			if (Sync.curFrame >= noRunAheadUpdatesUntil)
			{
				if (Sync.curFrame == noRunAheadUpdatesUntil)
				{
					noRunAheadUpdatesUntil = -1;
				}
				float frameValue = CurrentFrameEstimate - minimumFrame;
				accumulator.FrameUpdate(Sync.curFrame, frameValue);
			}
		}

		public override bool ShouldEmergencySleep()
		{
			return accumulator.ThresholdVeryReached();
		}

		public override float GetSleepInterval()
		{
			if (!accumulator.ThresholdReached())
			{
				return 0f;
			}
			return Mathf.Clamp(RunAheadEstimate * World.DELTA_TIME * TimeSyncComponentBase.ALIGN_TIMES_FACTOR, MIN_SLEEP_DURATION, MAX_SLEEP_DURATION);
		}

		public override void OnSleep(float frames)
		{
			noRunAheadUpdatesUntil = (int)((double)Sync.curFrame + Math.Ceiling(frames + NetUtils.MaxPing * (float)World.FPS) + 2.0);
			nextRecommendedSleep = Sync.curFrame + ESTIMATE_SLEEP_CHECK_INTERVAL;
			accumulator.Reset();
			base.OnSleep(frames);
		}

		protected override float GetRecentSleepBaseFactor()
		{
			return RECENT_SLEEP_BASE_FACTOR;
		}

		protected override float GetRecentSleepWindow()
		{
			if (playerIndex != 0)
			{
				return TimeSyncComponentBase.RECENT_SLEEP_WINDOW_CLIENT;
			}
			return TimeSyncComponentBase.RECENT_SLEEP_WINDOW_HOST;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ca.gov.mechasoulindustries.llb.my.cute.syncfix";

		public const string PLUGIN_NAME = "Sync Fix";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace SyncFix.Utils
{
	public class InstructionBuilder
	{
		private List<CodeInstruction> instructions = new List<CodeInstruction>();

		private Queue<OpCode> previousOpCodes = new Queue<OpCode>();

		private bool expectingOpCode = true;

		public InstructionBuilder OpCode(OpCode opcode)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			if (!expectingOpCode)
			{
				throw new InvalidOperationException($"tried to add opcode {opcode.Name} when expecting operand (previous instruction: {instructions.LastOrDefault()})");
			}
			previousOpCodes.Enqueue(opcode);
			expectingOpCode = false;
			if (opcode.OperandType == OperandType.InlineNone)
			{
				instructions.Add(new CodeInstruction(previousOpCodes.Dequeue(), (object)null));
				expectingOpCode = true;
			}
			return this;
		}

		public InstructionBuilder Operand(object operand)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			if (expectingOpCode)
			{
				if (operand == null)
				{
					CodeInstruction? obj = instructions.LastOrDefault();
					if (obj != null && obj.opcode.OperandType == OperandType.InlineNone)
					{
						return this;
					}
				}
				throw new InvalidOperationException($"tried to add operand {operand} when expecting opcode (previous instruction: {instructions.LastOrDefault()}, opcode: {previousOpCodes.LastOrDefault()}");
			}
			if (previousOpCodes.Count == 0)
			{
				throw new InvalidOperationException($"tried to add operand {operand} without previously adding an opcode (also this shouldnt happen?)");
			}
			instructions.Add(new CodeInstruction(previousOpCodes.Dequeue(), operand));
			expectingOpCode = true;
			return this;
		}

		public CodeInstruction[] Build()
		{
			return instructions.ToArray();
		}

		public CodeMatch[] BuildAsMatch()
		{
			return ((IEnumerable<CodeInstruction>)instructions).Select((Func<CodeInstruction, CodeMatch>)((CodeInstruction instruction) => new CodeMatch(instruction, (string)null))).ToArray();
		}
	}
	public class NetUtils
	{
		private static float maxPing = 0f;

		private static int maxPingPlayer = -1;

		public static float MaxPing => maxPing;

		public static float GetTravelTimeEstimate(float ping)
		{
			return Mathf.Pow(ping, 2f) * -17.3f + ping * 36.2f + 0.23f;
		}

		public static void UpdateMaxPing(Peer peer)
		{
			if (peer.playerNr == maxPingPlayer)
			{
				if (peer.ping > maxPing)
				{
					maxPing = peer.ping;
				}
				else
				{
					maxPing = GetMaxPing(out maxPingPlayer);
				}
			}
			else if (peer.ping > maxPing)
			{
				maxPing = peer.ping;
				maxPingPlayer = peer.playerNr;
			}
		}

		public static float GetMaxPing(out int maxPlayerIndex)
		{
			float num = -1f;
			int num2 = -1;
			foreach (ALDOKEMAOMB item in Player.EPlayers())
			{
				if (item.LAADACKBGLL() && (!Sync.isActive || Sync.IsValidOther(item.CJFLMDNNMIE)) && item.KLEEADMGHNE.ping > num)
				{
					num = item.KLEEADMGHNE.ping;
					num2 = item.CJFLMDNNMIE;
				}
			}
			maxPlayerIndex = num2;
			return num;
		}

		public static void ResetMaxPing()
		{
			maxPing = 0f;
			maxPingPlayer = -1;
		}
	}
	internal class PathUtils
	{
		public static DirectoryInfo ModdingFolder { get; private set; }

		public static string ModdingFolderName { get; private set; }

		public static void Init(PluginInfo info)
		{
			ModdingFolder = ModdingFolder.GetModSubFolder(info);
			ModdingFolderName = ModdingFolder.FullName;
		}

		public static string GetFilepath(string resourceName)
		{
			return Utility.CombinePaths(new string[2] { ModdingFolderName, resourceName });
		}

		public static string GetCurrentGameDebugPath()
		{
			return Utility.CombinePaths(new string[3]
			{
				ModdingFolderName,
				GetCurrentUserId(),
				GetCurrentGameString()
			});
		}

		private static string GetCurrentUserId()
		{
			KIIIINKJKNI gIGAKBJGFDI = CGLLJHHAJAK.GIGAKBJGFDI;
			return ((gIGAKBJGFDI != null) ? gIGAKBJGFDI.ECEAOMHNGOL() : null) ?? "no_id";
		}

		private static string GetCurrentGameString()
		{
			if (!Sync.isActive)
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(Sync.matchNr);
			stringBuilder.Append("_");
			for (int i = 0; i < Sync.nPlayers; i++)
			{
				if (Sync.IsValidOther(i))
				{
					stringBuilder.Append(Player.GetPlayer(i).peer.peerId);
					stringBuilder.Append("_");
				}
			}
			stringBuilder.Append($"P{((Peer)P2P.localPeer).playerNr}");
			stringBuilder.Append("_");
			stringBuilder.Append(StateManager.IsUsingGroup() ? "GROUP" : "SOLO");
			return stringBuilder.ToString();
		}
	}
}
namespace SyncFix.Patches
{
	[HarmonyPatch]
	public class Debug_Patches
	{
		[HarmonyPatch(typeof(Sync), "StopNow")]
		[HarmonyPrefix]
		public static bool RedirectStopNow()
		{
			if (SyncFixConfig.Instance.RecordDebugInfo)
			{
				FrameRecorders.SaveAll();
			}
			return true;
		}

		[HarmonyPatch(typeof(World), "Init1")]
		[HarmonyPostfix]
		public static void Init1Postfix(World __instance)
		{
			if (GameSettings.IsOnline)
			{
				RollbackStats.Reset();
				((Component)__instance).gameObject.AddComponent<DebugInfo>().parent = __instance;
			}
		}

		[HarmonyPatch(typeof(World), "Update")]
		[HarmonyPrefix]
		public static bool RedirectUpdate(World __instance)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(SyncFixConfig.Instance.DebugInfoKey))
			{
				SyncFixConfig.Instance.ShowDebugInfo = !SyncFixConfig.Instance.ShowDebugInfo;
			}
			return true;
		}

		[HarmonyPatch(typeof(Sync), "Rollback")]
		[HarmonyPrefix]
		public static bool RedirectRollback(int frame)
		{
			int num = Sync.curFrame - frame;
			if (SyncFixConfig.Instance.RecordDebugInfo)
			{
				FrameRecorders.Record("rollbacks", Sync.curFrame, num);
			}
			RollbackStats.AddRollback(num);
			return true;
		}

		[HarmonyPatch(typeof(P2P), "Wait")]
		[HarmonyPrefix]
		public static bool RedirectWait(float wait)
		{
			if (SyncFixConfig.Instance.RecordDebugInfo)
			{
				FrameRecorders.Record("wait", Sync.curFrame, wait);
			}
			RollbackStats.AddSleep(wait);
			return true;
		}
	}
	public class DebugInfo : MonoBehaviour
	{
		public World parent;

		public GUIStyle guiStyle;

		private void Awake()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			guiStyle = new GUIStyle();
			guiStyle.fontSize = 12;
			guiStyle.normal.textColor = Color32.op_Implicit(new Color32((byte)100, (byte)100, (byte)100, byte.MaxValue));
		}

		private void OnGUI()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (SyncFixConfig.Instance.ShowDebugInfo)
			{
				GUI.Label(new Rect(20f, 20f, 100f, 100f), RollbackStats.GetStats(), guiStyle);
			}
		}
	}
	[HarmonyPatch]
	public class LobbyState_Patches
	{
		[CompilerGenerated]
		private sealed class <DJLJONJDDDOPostfix>d__3 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public IEnumerator __result;

			public HDLIJDBFGKN __instance;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <DJLJONJDDDOPostfix>d__3(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					break;
				}
				if (__result.MoveNext())
				{
					<>2__current = __result.Current;
					<>1__state = 1;
					return true;
				}
				if (__instance.FBJIDODJNFN)
				{
					StateManager.ResetState();
				}
				else
				{
					StateManager.ResetMode();
				}
				NetUtils.ResetMaxPing();
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[HarmonyPatch(typeof(LocalHost), "OnOtherLeft")]
		[HarmonyPostfix]
		public static void OnOtherLeftPostfix(LocalHost __instance, Peer otherPeer)
		{
			StateManager.PeerLeft(otherPeer.playerNr);
		}

		[HarmonyPatch(typeof(LocalHost), "OnOtherJoined")]
		[HarmonyPostfix]
		public static void OnOtherJoinedPostfix(LocalHost __instance, string otherPeerId, string otherPeerName, int otherPlayerNr)
		{
			StateManager.PeerJoined(otherPlayerNr);
		}

		[HarmonyPatch(typeof(HDLIJDBFGKN), "OAACLLGMFLH", new Type[] { })]
		[HarmonyPostfix]
		public static void OAACLLGMFLHPostfix(HDLIJDBFGKN __instance)
		{
			if (StateManager.AllPeersConfirmed())
			{
				StateManager.SendAllGroupMessage();
			}
		}

		[HarmonyPatch(typeof(HDLIJDBFGKN), "DJLJONJDDDO")]
		[HarmonyPostfix]
		public static IEnumerator DJLJONJDDDOPostfix(IEnumerator __result, HDLIJDBFGKN __instance)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <DJLJONJDDDOPostfix>d__3(0)
			{
				__result = __result,
				__instance = __instance
			};
		}

		[HarmonyPatch(typeof(Peer), "ResetPing")]
		[HarmonyPostfix]
		public static void ResetPingPostfix(Peer __instance)
		{
			__instance.pingsPrev[0] = -1f;
			__instance.ping = 0f;
		}
	}
	[HarmonyPatch]
	public class TimeSyncGroup_Patches
	{
		[HarmonyPatch(typeof(Sync), "AlignTimes")]
		[HarmonyPrefix]
		public static bool RedirectAlignTimes()
		{
			if (!SyncFixConfig.Instance.Enabled)
			{
				return true;
			}
			if (Sync.doAwait)
			{
				SyncFixManager.Instance.MidMatchReset();
			}
			if (Sync.isAwaiting)
			{
				return false;
			}
			if (StateManager.IsUsingGroup())
			{
				SyncFixManager.Instance.GroupAlignTimes();
				return false;
			}
			if (P2P.isHost)
			{
				if (Sync.curFrame < 60)
				{
					return false;
				}
				SyncFixManager.Instance.SoloHostAlignTimes();
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Sync), "Init")]
		[HarmonyPostfix]
		public static void InitPostfix()
		{
			SyncFixManager.Instance.Reset();
		}

		[HarmonyPatch(typeof(LocalPeer), "SendToPlayerNr")]
		[HarmonyPrefix]
		public static bool RedirectSendToPlayerNr(LocalPeer __instance, int receiverPlayerNr, ref Message message)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (!SyncFixConfig.Instance.Enabled)
			{
				return true;
			}
			if ((int)message.msg == 190)
			{
				JKMAAHELEMF val = (JKMAAHELEMF)message.ob;
				float num = (float)val.CGJJEHPPOAN * 0.5f;
				if (val.GCPKPHMKLBN == 126)
				{
					num += 30000f * World.DELTA_TIME;
				}
				val.CGJJEHPPOAN = (int)num;
				message = new Message(message.msg, message.playerNr, message.index, (object)val, message.obSize);
			}
			return true;
		}

		[HarmonyPatch(typeof(OGONAGCFDPK), "IMEGGOOAADG")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> IMEGGOOAADGTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			val.MatchStartForward((CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Sync), "Start", (Type[])null, (Type[])null), (string)null)
			});
			val.Advance(1).Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Action>((Action)delegate
			{
				if (SyncFixConfig.Instance.Enabled)
				{
					SyncFixManager.Instance.Start();
				}
			}) });
			return val.InstructionEnumeration();
		}

		[HarmonyPatch(typeof(P2P), "Update")]
		[HarmonyPostfix]
		public static void UpdatePostfix(P2P __instance)
		{
			if (SyncFixConfig.Instance.Enabled && P2P.isPinging && Sync.isActive && !Sync.doAwait && !Sync.isAwaiting && Sync.curFrame > SyncFixManager.Instance.NextAdvantageUpdate && Sync.curFrame > SyncFixManager.Instance.LastSleep + 1 && StateManager.IsUsingGroup())
			{
				SyncFixManager.ForAllValidOthers(delegate(int i)
				{
					SyncFixManager.Instance.SendLocalAdvantageToPlayer(i);
				});
				SyncFixManager.Instance.UpdateNextAdvantageTime();
			}
		}

		[HarmonyPatch(typeof(P2P), "Wait")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> WaitTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			val.End();
			val.Insert((CodeInstruction[])(object)new CodeInstruction[2]
			{
				new CodeInstruction(OpCodes.Ldarg_0, (object)null),
				Transpilers.EmitDelegate<Action<float>>((Action<float>)delegate(float f)
				{
					if (SyncFixConfig.Instance.Enabled)
					{
						SyncFixManager.Instance.OnSleep(f);
					}
				})
			});
			return val.InstructionEnumeration();
		}

		[HarmonyPatch(typeof(Sync), "UpdateAwait")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> UpdateAwaitTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			val.MatchStartForward(new InstructionBuilder().OpCode(OpCodes.Ldsfld).Operand(AccessTools.Field(typeof(Sync), "statusInput")).OpCode(OpCodes.Ldfld)
				.Operand(AccessTools.Field(typeof(FrameStatus), "handledByAll"))
				.OpCode(OpCodes.Call)
				.Operand(AccessTools.PropertyGetter(typeof(Sync), "curFrame"))
				.OpCode(OpCodes.Ldc_I4_S)
				.Operand((sbyte)30)
				.OpCode(OpCodes.Sub)
				.BuildAsMatch());
			val.Advance(2);
			val.RemoveInstructions(3);
			val.Insert(new InstructionBuilder().OpCode(OpCodes.Ldsfld).Operand(AccessTools.Field(typeof(Sync), "awaitFrame")).OpCode(OpCodes.Ldc_I4_S)
				.Operand((sbyte)120)
				.OpCode(OpCodes.Add)
				.Build());
			return val.InstructionEnumeration();
		}
	}
	[HarmonyPatch]
	public class TimeSyncSolo_Patches
	{
		[HarmonyPatch(typeof(Sync), "AlignTimes")]
		[HarmonyPostfix]
		public static void AlignTimesPostfix(Sync __instance)
		{
			if (SyncFixConfig.Instance.Enabled && !P2P.isHost && !StateManager.HostHasSyncFix && Sync.curFrame % 60 == 0)
			{
				SelfVanillaAlignTimes();
			}
		}

		private static void SelfVanillaAlignTimes()
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			float fixedDeltaTime = Time.fixedDeltaTime;
			float num = (float)Sync.curFrame * fixedDeltaTime;
			float num2 = num;
			for (int i = 0; i < Sync.nPlayers; i++)
			{
				OtherInfo val = Sync.othersInfo[i];
				if (val != null)
				{
					float num3 = (float)Sync.statusInput.otherReceived[i] * fixedDeltaTime;
					num3 += val.peer.ping * 0.5f;
					if (num3 < num2)
					{
						num2 = num3;
					}
				}
			}
			float num4 = num - num2;
			num4 *= 0.75f;
			if (num4 >= 0.02f)
			{
				num4 = Mathf.Min(num4, 0.5f);
				P2P.SendToPlayerNr(((Peer)P2P.localPeer).playerNr, new Message((Msg)189, Sync.matchNr, Mathf.RoundToInt(num4 * 1000f), (object)null, -1));
			}
		}

		[HarmonyPatch(typeof(LocalPeer), "OnReceiveMessage")]
		[HarmonyPrefix]
		public static bool RedirectOnReceiveMessage(LocalPeer __instance, Envelope envelope)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!SyncFixConfig.Instance.Enabled)
			{
				return true;
			}
			if ((int)envelope.message.msg == 189 && envelope.sender != ((Peer)P2P.localPeer).peerId && !StateManager.HostHasSyncFix)
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Peer), "ResolvePing", new Type[] { typeof(int) })]
		[HarmonyPostfix]
		public static void ResolvePingPostfix(Peer __instance)
		{
			NetUtils.UpdateMaxPing(__instance);
		}
	}
}
namespace SyncFix.FrameRecorder
{
	internal class FrameRecord<T> : IComparable<FrameRecord<T>>
	{
		public int frame;

		public T value;

		public FrameRecord(int frame, T value)
		{
			this.frame = frame;
			this.value = value;
		}

		public int CompareTo(FrameRecord<T> other)
		{
			return frame.CompareTo(other.frame);
		}

		public override string ToString()
		{
			return $"{frame},{value}";
		}
	}
	internal class FrameRecorder<T> : IFrameRecorder
	{
		public readonly string name;

		public List<FrameRecord<T>> records;

		private Func<FrameRecord<T>, string> toStringFunction = (FrameRecord<T> record) => record.ToString();

		public Func<FrameRecord<T>, string> ToStringFunc
		{
			get
			{
				return toStringFunction;
			}
			set
			{
				toStringFunction = value;
			}
		}

		public FrameRecorder(string name)
		{
			this.name = name;
			records = new List<FrameRecord<T>>();
		}

		public void Record<U>(int frame, U value)
		{
			if (!(value is T))
			{
				throw new InvalidCastException($"tried to record a {typeof(U)} in a FrameRecorder<{typeof(T)}>");
			}
			object obj = value;
			T value2 = (T)((obj is T) ? obj : null);
			records.Add(new FrameRecord<T>(frame, value2));
		}

		public void SaveToFile()
		{
			if (records.Count == 0)
			{
				return;
			}
			records.Sort();
			StringBuilder stringBuilder = new StringBuilder();
			foreach (FrameRecord<T> record in records)
			{
				stringBuilder.Append(ToStringFunc(record));
				stringBuilder.AppendLine();
			}
			string path = Utility.CombinePaths(new string[2]
			{
				PathUtils.GetCurrentGameDebugPath(),
				name + ".csv"
			});
			Directory.CreateDirectory(Directory.GetParent(path).FullName);
			File.AppendAllText(path, stringBuilder.ToString());
		}

		public void Clear()
		{
			records.Clear();
		}
	}
	internal class FrameRecorders
	{
		private static readonly Dictionary<string, IFrameRecorder> _frameRecorders;

		static FrameRecorders()
		{
			_frameRecorders = new Dictionary<string, IFrameRecorder>();
		}

		public static IFrameRecorder GetFrameRecorder<T>(string name)
		{
			if (_frameRecorders.TryGetValue(name, out var value))
			{
				return value;
			}
			IFrameRecorder frameRecorder = new FrameRecorder<T>(name);
			_frameRecorders.Add(name, frameRecorder);
			return frameRecorder;
		}

		public static void Record<T>(string name, int frame, T value)
		{
			GetFrameRecorder<T>(name).Record(frame, value);
		}

		public static void SaveAll()
		{
			foreach (IFrameRecorder value in _frameRecorders.Values)
			{
				value.SaveToFile();
				value.Clear();
			}
		}

		public static void ClearAll()
		{
			foreach (IFrameRecorder value in _frameRecorders.Values)
			{
				value.Clear();
			}
		}
	}
	internal interface IFrameRecorder
	{
		void Record<T>(int frame, T value);

		void SaveToFile();

		void Clear();
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}