Decompiled source of BulletTime v1.5.2

BulletTime.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using NebulaAPI;
using NebulaAPI.GameState;
using NebulaAPI.Networking;
using NebulaAPI.Packets;
using NebulaWorld;
using NebulaWorld.GameStates;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BulletTime")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BulletTime")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("4813207f-ca55-4857-9ede-b1c9d2a90373")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.2.0")]
[module: UnverifiableCode]
namespace BulletTime;

internal class SkillSystem_Patch
{
	public static bool Enable { get; set; }

	public static void GameTick(SkillSystem skillSystem)
	{
		if (Enable)
		{
			PerformanceMonitor.BeginSample((ECpuWorkEntry)33);
			PerformanceMonitor.BeginSample((ECpuWorkEntry)35);
			AdvanceSkillSystem(skillSystem);
			PerformanceMonitor.EndSample((ECpuWorkEntry)35);
			PerformanceMonitor.BeginSample((ECpuWorkEntry)33);
		}
	}

	private static void AdvanceSkillSystem(SkillSystem skillSystem)
	{
		skillSystem.PrepareTick();
		skillSystem.CollectPlayerStates();
		skillSystem.GameTick(GameMain.gameTick);
		skillSystem.AfterTick();
	}
}
internal class BuildTool_Patch
{
	[HarmonyTranspiler]
	[HarmonyPatch(typeof(BuildTool_Click), "CreatePrebuilds")]
	[HarmonyPatch(typeof(BuildTool_Path), "CreatePrebuilds")]
	[HarmonyPatch(typeof(BuildTool_Addon), "CreatePrebuilds")]
	[HarmonyPatch(typeof(BuildTool_Inserter), "CreatePrebuilds")]
	[HarmonyPatch(typeof(BuildTool_BlueprintPaste), "CreatePrebuilds")]
	public static IEnumerable<CodeInstruction> RemoveGC_Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null).End();
		for (int i = 0; i < 5; i++)
		{
			val.Advance(-1);
			if (val.Opcode == OpCodes.Call && ((MethodInfo)val.Operand).Name == "Collect")
			{
				val.SetOpcodeAndAdvance(OpCodes.Nop);
				break;
			}
		}
		return val.InstructionEnumeration();
	}
}
internal class GameLoader_Patch
{
	[HarmonyTranspiler]
	[HarmonyPatch(typeof(GameLoader), "FixedUpdate")]
	private static IEnumerable<CodeInstruction> GameTick_Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Expected O, but got Unknown
		try
		{
			return new CodeMatcher(instructions, (ILGenerator)null).MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(StarData), "get_loaded", (Type[])null, (Type[])null), (string)null)
			}).Advance(-1).SetAndAdvance(OpCodes.Nop, (object)null)
				.SetAndAdvance(OpCodes.Nop, (object)null)
				.Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Func<bool>>((Func<bool>)(() => GameMain.localPlanet?.loaded ?? true)) })
				.InstructionEnumeration();
		}
		catch
		{
			Log.Warn("Transpiler GameLoader.FixedUpdate failed. Fast loading won't work");
			return instructions;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameLoader), "FixedUpdate")]
	private static void FixedUpdate_Postfix(GameLoader __instance)
	{
		if (__instance.frame >= 5 && DSPGame.Game.isMenuDemo && !DSPGame.IsCombatCutscene)
		{
			Log.Debug("MenuDemo - Fast forward");
			GameMain.data.SetReady();
			GameMain.Begin();
			__instance.SelfDestroy();
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(VFPreload), "IsMusicReached")]
	private static void IsMusicReached_Postfix(ref bool __result)
	{
		__result |= true;
	}
}
public static class NebulaCompat
{
	public const string APIGUID = "dsp.nebula-multiplayer-api";

	public const string GUID = "dsp.nebula-multiplayer";

	public static bool NebulaIsInstalled { get; private set; }

	public static bool IsMultiplayerActive { get; private set; }

	public static bool IsClient { get; private set; }

	public static bool IsPlayerJoining { get; set; }

	public static List<(string username, int planetId)> LoadingPlayers { get; } = new List<(string, int)>();


	public static bool DysonSpherePaused { get; set; }

	public static void Init(Harmony harmony)
	{
		try
		{
			NebulaIsInstalled = NebulaModAPI.NebulaIsInstalled;
			if (!NebulaIsInstalled)
			{
				return;
			}
			NebulaModAPI.RegisterPackets(Assembly.GetExecutingAssembly());
			NebulaModAPI.OnMultiplayerGameStarted = (Action)Delegate.Combine(NebulaModAPI.OnMultiplayerGameStarted, new Action(OnMultiplayerGameStarted));
			NebulaModAPI.OnMultiplayerGameEnded = (Action)Delegate.Combine(NebulaModAPI.OnMultiplayerGameEnded, new Action(OnMultiplayerGameEnded));
			NebulaModAPI.OnPlanetLoadRequest = (Action<int>)Delegate.Combine(NebulaModAPI.OnPlanetLoadRequest, new Action<int>(OnFactoryLoadRequest));
			NebulaModAPI.OnPlanetLoadFinished = (Action<int>)Delegate.Combine(NebulaModAPI.OnPlanetLoadFinished, new Action<int>(OnFactoryLoadFinished));
			NebulaModAPI.OnPlayerLeftGame = (Action<IPlayerData>)Delegate.Combine(NebulaModAPI.OnPlayerLeftGame, (Action<IPlayerData>)delegate(IPlayerData player)
			{
				LoadingPlayers.RemoveAll(((string username, int planetId) x) => x.username == player.Username);
				DetermineCurrentState();
			});
			harmony.PatchAll(typeof(NebulaPatch));
			Log.Debug("Nebula Compatibility OK");
		}
		catch (Exception obj)
		{
			Log.Error("Nebula Compatibility failed!");
			Log.Error(obj);
		}
	}

	public static void Dispose()
	{
		if (NebulaIsInstalled)
		{
			NebulaModAPI.OnMultiplayerGameStarted = (Action)Delegate.Remove(NebulaModAPI.OnMultiplayerGameStarted, new Action(OnMultiplayerGameStarted));
			NebulaModAPI.OnMultiplayerGameEnded = (Action)Delegate.Remove(NebulaModAPI.OnMultiplayerGameEnded, new Action(OnMultiplayerGameEnded));
			NebulaModAPI.OnPlanetLoadRequest = (Action<int>)Delegate.Remove(NebulaModAPI.OnPlanetLoadRequest, new Action<int>(OnFactoryLoadRequest));
			NebulaModAPI.OnPlanetLoadFinished = (Action<int>)Delegate.Remove(NebulaModAPI.OnPlanetLoadFinished, new Action<int>(OnFactoryLoadFinished));
		}
	}

	public static void OnMultiplayerGameStarted()
	{
		IsMultiplayerActive = NebulaModAPI.IsMultiplayerActive;
		IsClient = IsMultiplayerActive && NebulaModAPI.MultiplayerSession.LocalPlayer.IsClient;
		IsPlayerJoining = false;
		LoadingPlayers.Clear();
		DysonSpherePaused = false;
		GameStateManager.EnableMechaFunc = false;
		NebulaPatch.SetProgessMode(enable: false);
	}

	public static void OnMultiplayerGameEnded()
	{
		IsMultiplayerActive = false;
		IsClient = false;
		IsPlayerJoining = false;
		LoadingPlayers.Clear();
		DysonSpherePaused = false;
		GameStateManager.EnableMechaFunc = BulletTimePlugin.EnableMechaFunc.Value;
	}

	private static void OnFactoryLoadRequest(int planetId)
	{
		if (NebulaModAPI.MultiplayerSession.IsGameLoaded)
		{
			SendPacket(PauseEvent.FactoryRequest, planetId);
		}
	}

	private static void OnFactoryLoadFinished(int planetId)
	{
		if (NebulaModAPI.MultiplayerSession.IsGameLoaded)
		{
			SendPacket(PauseEvent.FactoryLoaded, planetId);
		}
	}

	public static void DetermineCurrentState()
	{
		if (LoadingPlayers.Count == 0 && !IsPlayerJoining)
		{
			if (GameStateManager.ManualPause)
			{
				GameStateManager.SetPauseMode(value: true);
				GameStateManager.SetSyncingLock(isLock: false);
				IngameUI.ShowStatus(BulletTimePlugin.StatusTextPause.Value);
				SendPacket(PauseEvent.Pause);
			}
			else
			{
				GameStateManager.SetPauseMode(value: false);
				GameStateManager.SetSyncingLock(isLock: false);
				IngameUI.ShowStatus("");
				SendPacket(PauseEvent.Resume);
			}
		}
		else if (LoadingPlayers.Count > 0)
		{
			PauseNotificationPacket pauseNotificationPacket = new PauseNotificationPacket(PauseEvent.FactoryRequest, LoadingPlayers[0].username, LoadingPlayers[0].planetId);
			NebulaModAPI.MultiplayerSession.Network.SendPacket<PauseNotificationPacket>(pauseNotificationPacket);
		}
	}

	public static void SendPacket(PauseEvent pauseEvent, int planetId = 0)
	{
		string username = NebulaModAPI.MultiplayerSession.LocalPlayer.Data.Username;
		NebulaModAPI.MultiplayerSession.Network.SendPacket<PauseNotificationPacket>(new PauseNotificationPacket(pauseEvent, username, planetId));
		if (pauseEvent == PauseEvent.Resume)
		{
			LoadingPlayers.Clear();
			IsPlayerJoining = false;
		}
	}

	public static void SetPacketProcessing(bool enable)
	{
		Log.Info("SetPacketProcessing: " + enable);
		NebulaModAPI.MultiplayerSession.Network.PacketProcessor.EnablePacketProcessing = enable;
	}
}
public class NebulaPatch
{
	private static int lastLength;

	private static bool enablePingIndicatorUpdate = true;

	[HarmonyPostfix]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	private static void RealGameTick(ref long __result)
	{
		if (GameStateManager.StoredGameTick != 0L)
		{
			__result = GameStateManager.StoredGameTick;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	private static void RealUPS(ref float __result)
	{
		if (!GameStateManager.Pause)
		{
			__result *= (1f - GameStateManager.SkipRatio) / 100f;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameStatesManager), "NotifyTickDifference")]
	private static void NotifyTickDifference(float delta)
	{
		if (!GameStateManager.Pause)
		{
			GameStateManager.SetSpeedRatio(Mathf.Clamp(1f + delta / (float)FPSController.currentUPS, 0.01f, 1f));
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(SimulatedWorld), "OnPlayerJoining")]
	private static bool OnPlayerJoining(string username)
	{
		NebulaCompat.IsPlayerJoining = true;
		GameMain.isFullscreenPaused = true;
		IngameUI.ShowStatus(string.Format(Localization.Translate("{0} joining the game"), username));
		GameStateManager.SetPauseMode(value: true);
		GameStateManager.SetSyncingLock(isLock: true);
		SetProgessMode(enable: true);
		return false;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(SimulatedWorld), "OnAllPlayersSyncCompleted")]
	private static void OnAllPlayersSyncCompleted()
	{
		NebulaCompat.IsPlayerJoining = false;
		if (!NebulaCompat.IsClient)
		{
			NebulaCompat.DetermineCurrentState();
			NebulaCompat.SendPacket(NebulaCompat.DysonSpherePaused ? PauseEvent.DysonSpherePaused : PauseEvent.DysonSphereResume);
		}
		SetProgessMode(enable: false);
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(GameSave), "SaveCurrentGame")]
	private static void SaveCurrentGame_Prefix()
	{
		if (NebulaCompat.IsMultiplayerActive)
		{
			NebulaCompat.SendPacket(PauseEvent.Save);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameSave), "SaveCurrentGame")]
	private static void SaveCurrentGame_Postfix(string saveName)
	{
		if (NebulaCompat.IsMultiplayerActive && !NebulaCompat.IsClient)
		{
			if (saveName != GameSave.AutoSaveTmp)
			{
				NebulaCompat.LoadingPlayers.Clear();
				NebulaCompat.IsPlayerJoining = false;
			}
			if (saveName != GameSave.saveExt)
			{
				NebulaCompat.DetermineCurrentState();
			}
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(MechaLab), "GameTick")]
	private static bool MechaLabGameTick_Prefix()
	{
		if (NebulaCompat.IsMultiplayerActive)
		{
			return !NebulaCompat.IsPlayerJoining;
		}
		return true;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(UIDESphereInfo), "_OnInit")]
	[HarmonyAfter(new string[] { "dsp.nebula-multiplayer" })]
	private static void UIDESphereInfo__OnInit()
	{
		((Selectable)UIRoot.instance.uiGame.dysonEditor.controlPanel.topFunction.pauseButton.button).interactable = true;
		SetDysonSpherePasued(NebulaCompat.DysonSpherePaused);
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(UIDETopFunction), "_OnLateUpdate")]
	private static bool UIDETopFunction__OnLateUpdate()
	{
		return !NebulaCompat.IsMultiplayerActive;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(UIDETopFunction), "OnPauseButtonClick")]
	private static bool OnPauseButtonClick()
	{
		if (NebulaCompat.IsMultiplayerActive)
		{
			SetDysonSpherePasued(!NebulaCompat.DysonSpherePaused);
			NebulaCompat.SendPacket(NebulaCompat.DysonSpherePaused ? PauseEvent.DysonSpherePaused : PauseEvent.DysonSphereResume);
			return false;
		}
		return true;
	}

	public static void SetDysonSpherePasued(bool state)
	{
		NebulaCompat.DysonSpherePaused = state;
		UIDETopFunction topFunction = UIRoot.instance.uiGame.dysonEditor.controlPanel.topFunction;
		topFunction.pauseButton.highlighted = !NebulaCompat.DysonSpherePaused;
		topFunction.pauseImg.sprite = (NebulaCompat.DysonSpherePaused ? topFunction.pauseSprite : topFunction.playSprite);
		topFunction.pauseText.text = Localization.Translate(NebulaCompat.DysonSpherePaused ? "Click to resume rotating" : "Click to stop rotating");
	}

	[HarmonyTranspiler]
	[HarmonyPatch(typeof(DysonSphereLayer), "GameTick")]
	private static IEnumerable<CodeInstruction> DysonSphereLayer_GameTick(IEnumerable<CodeInstruction> instructions, ILGenerator iL)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Expected O, but got Unknown
		try
		{
			return new CodeMatcher(instructions, iL).MatchForward(true, (CodeMatch[])(object)new CodeMatch[2]
			{
				new CodeMatch((OpCode?)OpCodes.Ldarg_0, (object)null, (string)null),
				new CodeMatch((OpCode?)OpCodes.Ldfld, (object)AccessTools.Field(typeof(DysonSphereLayer), "orbitAngularSpeed"), (string)null)
			}).Repeat((Action<CodeMatcher>)delegate(CodeMatcher matcher)
			{
				matcher.SetAndAdvance(OpCodes.Call, (object)AccessTools.Method(typeof(NebulaPatch), "GetOrbitAngularSpeed", (Type[])null, (Type[])null));
			}, (Action<string>)null).InstructionEnumeration();
		}
		catch (Exception obj)
		{
			Log.Error("DysonSphereLayer_GameTick Transpiler error");
			Log.Error(obj);
			return instructions;
		}
	}

	private static float GetOrbitAngularSpeed(DysonSphereLayer @this)
	{
		if (!NebulaCompat.DysonSpherePaused)
		{
			return @this.orbitAngularSpeed;
		}
		return 0f;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameStatesManager), "UpdateBufferLength")]
	private static void UpdateBufferLength_Postfix(int length)
	{
		if (lastLength != length && GameStatesManager.FragmentSize > 0)
		{
			NebulaModAPI.MultiplayerSession.Network.SendPacket<ProgressUpdatePacket>(new ProgressUpdatePacket(GameStatesManager.FragmentSize, (float)length / (float)GameStatesManager.FragmentSize));
			lastLength = length;
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(SimulatedWorld), "UpdatePingIndicator")]
	private static bool UpdatePingIndicator_Prefix()
	{
		return enablePingIndicatorUpdate;
	}

	public static void SetProgessMode(bool enable)
	{
		if (enable)
		{
			enablePingIndicatorUpdate = false;
			return;
		}
		enablePingIndicatorUpdate = true;
		if (NebulaModAPI.MultiplayerSession.IsServer)
		{
			Multiplayer.Session.World.HidePingIndicator();
		}
	}

	public static void SetProgressTest(int fragmentSize, float percentage)
	{
		bool num = enablePingIndicatorUpdate;
		enablePingIndicatorUpdate = true;
		Multiplayer.Session.World.UpdatePingIndicator($"Progress {fragmentSize / 1000:n0} KB ({percentage:P1})");
		enablePingIndicatorUpdate = num;
	}
}
internal class PauseNotificationPacket
{
	public string Username { get; set; }

	public PauseEvent Event { get; set; }

	public int PlanetId { get; set; }

	public PauseNotificationPacket()
	{
	}

	public PauseNotificationPacket(PauseEvent pauseEvent, string username, int planetId = 0)
	{
		Event = pauseEvent;
		Username = username;
		PlanetId = planetId;
	}
}
public enum PauseEvent
{
	None,
	Resume,
	Pause,
	Save,
	FactoryRequest,
	FactoryLoaded,
	DysonSpherePaused,
	DysonSphereResume
}
[RegisterPacketProcessor]
internal class PauseNotificationProcessor : BasePacketProcessor<PauseNotificationPacket>
{
	public override void ProcessPacket(PauseNotificationPacket packet, INebulaConnection conn)
	{
		NebulaPatch.SetProgessMode(enable: false);
		switch (packet.Event)
		{
		case PauseEvent.Resume:
			if (base.IsHost)
			{
				if (GameStateManager.Interactable && GameStateManager.Pause)
				{
					IngameUI.OnKeyPause();
				}
			}
			else
			{
				GameStateManager.ManualPause = false;
				GameStateManager.SetPauseMode(value: false);
				GameStateManager.SetSyncingLock(isLock: false);
				IngameUI.SetHotkeyPauseMode(active: false);
				IngameUI.ShowStatus("");
			}
			break;
		case PauseEvent.Pause:
			if (base.IsHost)
			{
				if (GameStateManager.Interactable && !GameStateManager.Pause)
				{
					IngameUI.OnKeyPause();
				}
			}
			else
			{
				GameStateManager.ManualPause = true;
				GameStateManager.SetPauseMode(value: true);
				GameStateManager.SetSyncingLock(isLock: false);
				IngameUI.SetHotkeyPauseMode(active: true);
			}
			break;
		case PauseEvent.Save:
			if (base.IsClient)
			{
				GameMain.isFullscreenPaused = true;
				GameStateManager.SetPauseMode(value: true);
				GameStateManager.SetSyncingLock(isLock: true);
				IngameUI.ShowStatus(Localization.Translate("Host is saving game..."));
			}
			break;
		case PauseEvent.FactoryRequest:
		{
			GameStateManager.SetPauseMode(value: true);
			GameStateManager.SetSyncingLock(GameMain.localPlanet?.id == packet.PlanetId);
			string format = Localization.Translate("{0} arriving {1}");
			string username = packet.Username;
			PlanetData obj = GameMain.galaxy.PlanetById(packet.PlanetId);
			IngameUI.ShowStatus(string.Format(format, username, (obj != null) ? obj.displayName : null));
			if (base.IsHost)
			{
				NebulaCompat.LoadingPlayers.Add((packet.Username, packet.PlanetId));
				NebulaModAPI.MultiplayerSession.Network.SendPacket<PauseNotificationPacket>(packet);
			}
			NebulaPatch.SetProgessMode(packet.Username != NebulaModAPI.MultiplayerSession.LocalPlayer.Data.Username);
			break;
		}
		case PauseEvent.FactoryLoaded:
			if (base.IsHost)
			{
				NebulaCompat.LoadingPlayers.RemoveAll(((string username, int planetId) x) => x.username == packet.Username);
				NebulaCompat.DetermineCurrentState();
			}
			break;
		case PauseEvent.DysonSpherePaused:
			NebulaPatch.SetDysonSpherePasued(state: true);
			if (base.IsHost)
			{
				NebulaModAPI.MultiplayerSession.Network.SendPacket<PauseNotificationPacket>(packet);
			}
			break;
		case PauseEvent.DysonSphereResume:
			NebulaPatch.SetDysonSpherePasued(state: false);
			if (base.IsHost)
			{
				NebulaModAPI.MultiplayerSession.Network.SendPacket<PauseNotificationPacket>(packet);
			}
			break;
		}
	}
}
internal class ProgressUpdatePacket
{
	public int FragmentSize { get; set; }

	public float Percentage { get; set; }

	public ProgressUpdatePacket()
	{
	}

	public ProgressUpdatePacket(int framentSize, float percentage)
	{
		FragmentSize = framentSize;
		Percentage = percentage;
	}
}
[RegisterPacketProcessor]
internal class ProgressUpdateProcessor : BasePacketProcessor<ProgressUpdatePacket>
{
	public override void ProcessPacket(ProgressUpdatePacket packet, INebulaConnection conn)
	{
		if (base.IsHost)
		{
			NebulaModAPI.MultiplayerSession.Network.SendPacketExclude<ProgressUpdatePacket>(packet, conn);
			Multiplayer.Session.World.DisplayPingIndicator();
		}
		NebulaPatch.SetProgressTest(packet.FragmentSize, packet.Percentage);
	}
}
internal class IngameUI
{
	public static string CurrentStatus = "";

	private static Slider slider;

	private static Text sliderText;

	private static Text stateMessage;

	private static UIToggle backgroundSaveToggle;

	private static GameObject timeTextGo;

	private static GameObject infoTextGo;

	private static Text speedRatioText;

	private static readonly GameObject[] speedBtnGo = (GameObject[])(object)new GameObject[3];

	public static void Dispose()
	{
		if ((Object)(object)slider != (Object)null)
		{
			((UnityEventBase)slider.onValueChanged).RemoveAllListeners();
			Object.Destroy((Object)(object)((Component)slider).gameObject);
		}
		slider = null;
		sliderText = null;
		if ((Object)(object)stateMessage != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)((Component)stateMessage).transform.GetParent()).gameObject);
			stateMessage = null;
		}
		if ((Object)(object)backgroundSaveToggle != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)((Component)backgroundSaveToggle).transform.parent).gameObject);
			backgroundSaveToggle = null;
		}
		timeTextGo = null;
		for (int i = 0; i < speedBtnGo.Length; i++)
		{
			Object.Destroy((Object)(object)speedBtnGo[i]);
			speedBtnGo[i] = null;
		}
		Object.Destroy((Object)(object)infoTextGo);
		infoTextGo = null;
		Text obj = speedRatioText;
		Object.Destroy((Object)(object)((obj != null) ? ((Component)obj).gameObject : null));
		speedRatioText = null;
	}

	public static void Init()
	{
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0278: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_02db: Unknown result type (might be due to invalid IL or missing references)
		//IL_054e: Unknown result type (might be due to invalid IL or missing references)
		//IL_055d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0562: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)slider == (Object)null)
			{
				GameObject gameObject = ((Component)((Component)UIRoot.instance.optionWindow.audioVolumeComp).transform).gameObject;
				Transform parent = ((Component)UIRoot.instance.uiGame.statWindow.performancePanelUI.cpuActiveButton).gameObject.transform.parent;
				GameObject obj = Object.Instantiate<GameObject>(gameObject, parent);
				((Object)obj).name = "RealTickRatioSlider";
				obj.transform.localPosition = parent.Find("title-text").localPosition + new Vector3(-120f, 1f, 0f);
				slider = obj.GetComponent<Slider>();
				sliderText = obj.GetComponentInChildren<Text>();
				slider.value = BulletTimePlugin.StartingSpeed.Value;
				((UnityEvent<float>)(object)slider.onValueChanged).AddListener((UnityAction<float>)delegate(float value)
				{
					OnSliderChange(value);
				});
				OnSliderChange(slider.value);
				GameObject gameObject2 = ((Component)((Component)UIRoot.instance.optionWindow.fullscreenComp).transform.parent).gameObject;
				Transform parent2 = ((Component)UIRoot.instance.uiGame.statWindow.performancePanelUI.dataActiveButton).gameObject.transform.parent;
				GameObject obj2 = Object.Instantiate<GameObject>(gameObject2, parent2);
				((Object)obj2).name = "Background autosave toggle";
				obj2.transform.localPosition = new Vector3(250f, 390f, 0f);
				Object.Destroy((Object)(object)obj2.GetComponent<Localizer>());
				Text component = obj2.GetComponent<Text>();
				component.fontSize = 14;
				component.text = Localization.Translate("Background autosave");
				backgroundSaveToggle = obj2.GetComponentInChildren<UIToggle>();
				((Component)backgroundSaveToggle).transform.localPosition = new Vector3(65f, -20f, 0f);
				backgroundSaveToggle.isOn = GameSave_Patch.isEnabled;
				((UnityEvent<bool>)(object)backgroundSaveToggle.toggle.onValueChanged).AddListener((UnityAction<bool>)OnAutosaveToggleChange);
			}
			((Component)slider).gameObject.SetActive(!NebulaCompat.IsClient);
			GameStateManager.ManualPause = false;
			SetHotkeyPauseMode(active: false);
		}
		catch (Exception ex)
		{
			Log.Warn("IngameUI fail! error:\n" + ex);
		}
		try
		{
			if ((Object)(object)timeTextGo == (Object)null)
			{
				timeTextGo = GameObject.Find("UI Root/Overlay Canvas/In Game/Game Menu/time-text");
				timeTextGo.SetActive(true);
				RectTransform component2 = GameObject.Find("UI Root/Overlay Canvas/In Game/Game Menu/button-1-bg").GetComponent<RectTransform>();
				Vector3 localPosition = ((Transform)component2).localPosition;
				localPosition.x += 35f;
				localPosition.y -= 20f;
				for (int i = 0; i < 3; i++)
				{
					RectTransform val = Object.Instantiate<RectTransform>(component2, timeTextGo.transform.parent);
					((Transform)val).localScale = new Vector3(0.35f, 0.35f, 0.35f);
					((Transform)val).localPosition = localPosition;
					localPosition.x += 22f;
					UIButton component3 = ((Component)val).GetComponent<UIButton>();
					component3.OnPointerDown((PointerEventData)null);
					component3.OnPointerEnter((PointerEventData)null);
					component3.data = i;
					component3.onClick += OnSpeedButtonClick;
					Sprite sprite = null;
					switch (i)
					{
					case 0:
					{
						sprite = ((Component)((Component)UIRoot.instance.uiGame.researchQueue.pauseButton).gameObject.transform.Find("icon")).GetComponent<Image>().sprite;
						ref TipSettings tips3 = ref component3.tips;
						string tipTitle = (((Object)val).name = Localization.Translate("Pause"));
						tips3.tipTitle = tipTitle;
						component3.tips.tipText = Localization.Translate("Toggle tactical pause mode");
						break;
					}
					case 1:
					{
						sprite = ((Component)((Component)UIRoot.instance.uiGame.researchQueue.resumeButton).gameObject.transform.Find("icon")).GetComponent<Image>().sprite;
						ref TipSettings tips2 = ref component3.tips;
						string tipTitle = (((Object)val).name = Localization.Translate("Resume"));
						tips2.tipTitle = tipTitle;
						component3.tips.tipText = Localization.Translate("Reset game speed back to 1x");
						break;
					}
					case 2:
					{
						sprite = Resources.Load<Sprite>("ui/textures/sprites/test/next-icon-2");
						ref TipSettings tips = ref component3.tips;
						string tipTitle = (((Object)val).name = Localization.Translate("SpeedUp"));
						tips.tipTitle = tipTitle;
						component3.tips.tipText = string.Format(Localization.Translate("Left click to increase game speed\nRight click to set to max ({0}x)"), BulletTimePlugin.MaxSpeedupScale.Value);
						component3.onRightClick += OnSpeedupButtonRightClcik;
						break;
					}
					}
					((Component)((Transform)val).Find("button-1/icon")).GetComponent<Image>().sprite = sprite;
					speedBtnGo[i] = ((Component)val).gameObject;
				}
				infoTextGo = Object.Instantiate<GameObject>(timeTextGo, timeTextGo.transform.parent);
				((Object)infoTextGo).name = "pause info-text";
				infoTextGo.GetComponent<Text>().text = Localization.Translate("Pause");
				((Behaviour)infoTextGo.GetComponent<Text>()).enabled = true;
				infoTextGo.SetActive(false);
				GameObject obj3 = GameObject.Find("UI Root/Overlay Canvas/In Game/Game Menu/real-time-text");
				GameObject obj4 = Object.Instantiate<GameObject>(obj3, obj3.transform.parent);
				((Object)obj4).name = "speed ratio-text";
				Transform transform = obj4.transform;
				transform.localPosition += new Vector3(0f, 15f);
				obj4.SetActive(true);
				speedRatioText = obj4.GetComponent<Text>();
			}
			speedBtnGo[2].SetActive(!NebulaCompat.IsClient);
			SetSpeedRatioText();
		}
		catch (Exception ex2)
		{
			Log.Warn("Game Menu button UI fail! error:\n" + ex2);
		}
	}

	private static void OnSpeedButtonClick(int mode)
	{
		switch (mode)
		{
		case 0:
			OnKeyPause();
			break;
		case 1:
			FPSController.SetFixUPS(0.0);
			if (GameStateManager.Pause)
			{
				OnKeyPause();
			}
			break;
		case 2:
			if (GameStateManager.Pause)
			{
				OnKeyPause();
			}
			else if (FPSController.instance.fixUPS == 0.0)
			{
				FPSController.SetFixUPS(120.0);
			}
			else if (FPSController.instance.fixUPS < 60.0 * (double)BulletTimePlugin.MaxSpeedupScale.Value)
			{
				FPSController.SetFixUPS(FPSController.instance.fixUPS + 60.0);
			}
			break;
		}
		SetSpeedRatioText();
	}

	private static void OnSpeedupButtonRightClcik(int _)
	{
		if (GameStateManager.Pause)
		{
			OnKeyPause();
		}
		FPSController.SetFixUPS(60.0 * (double)BulletTimePlugin.MaxSpeedupScale.Value);
		SetSpeedRatioText();
	}

	private static void SetSpeedRatioText()
	{
		if ((Object)(object)speedRatioText == (Object)null)
		{
			return;
		}
		if (NebulaCompat.IsClient)
		{
			speedRatioText.text = "";
			return;
		}
		float num = (float)FPSController.instance.fixUPS / 60f;
		if (num == 0f)
		{
			speedRatioText.text = "1 x";
		}
		else if ((double)(num - (float)(int)num) < 0.05)
		{
			speedRatioText.text = (int)num + " x";
		}
		else
		{
			speedRatioText.text = $"{num:0.0} x";
		}
	}

	public static void OnPauseModeChange(bool pause)
	{
		if ((Object)(object)infoTextGo != (Object)null)
		{
			timeTextGo.SetActive(!pause);
			infoTextGo.SetActive(pause);
			if ((Object)(object)sliderText != (Object)null)
			{
				sliderText.text = (pause ? Localization.Translate("Pause") : $"{(int)slider.value}%");
			}
			SetSpeedRatioText();
		}
		SetHotkeyPauseMode(active: false);
	}

	private static void OnSliderChange(float value)
	{
		if (value == 0f)
		{
			GameStateManager.ManualPause = true;
			sliderText.text = Localization.Translate("pause");
			if (!GameStateManager.Pause && NebulaCompat.IsMultiplayerActive)
			{
				NebulaCompat.SendPacket(PauseEvent.Pause);
			}
		}
		else
		{
			GameStateManager.ManualPause = false;
			sliderText.text = $"{(int)value}%";
			if (GameStateManager.Pause && NebulaCompat.IsMultiplayerActive)
			{
				NebulaCompat.SendPacket(PauseEvent.Resume);
			}
			ShowStatus("");
		}
		GameStateManager.SetSpeedRatio(value / 100f);
		SetHotkeyPauseMode(active: false);
	}

	public static void SetHotkeyPauseMode(bool active)
	{
		//IL_002e: 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)
		GameStateManager.HotkeyPause = active;
		if (active)
		{
			if (NebulaCompat.IsMultiplayerActive && !NebulaCompat.IsClient)
			{
				NebulaCompat.SendPacket(PauseEvent.Pause);
			}
			Player mainPlayer = GameMain.mainPlayer;
			GameStateManager.PlayerPosition = ((mainPlayer != null) ? mainPlayer.position : Vector3.zero);
			SkillSystem_Patch.Enable = GameStateManager.EnableMechaFunc;
			ShowStatus(BulletTimePlugin.StatusTextPause.Value);
		}
		else
		{
			SkillSystem_Patch.Enable = false;
		}
	}

	public static void OnKeyPause()
	{
		if (NebulaCompat.IsClient)
		{
			if (GameStateManager.Interactable)
			{
				if (!GameStateManager.Pause)
				{
					NebulaCompat.SendPacket(PauseEvent.Pause);
				}
				else
				{
					NebulaCompat.SendPacket(PauseEvent.Resume);
				}
			}
		}
		else if (!GameStateManager.Pause)
		{
			if (UIRoot.instance.uiGame.autoSave.showTime == 0f)
			{
				GameStateManager.ManualPause = true;
				GameStateManager.SetPauseMode(value: true);
				SetHotkeyPauseMode(active: true);
			}
		}
		else if (GameStateManager.Interactable)
		{
			OnSliderChange(slider.value);
		}
	}

	private static void OnAutosaveToggleChange(bool val)
	{
		GameSave_Patch.Enable(val);
		backgroundSaveToggle.isOn = val;
	}

	public static void ShowStatus(string message)
	{
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)stateMessage == (Object)null)
		{
			GameObject obj = GameObject.Find("UI Root/Overlay Canvas/In Game/Top Tips/Auto Save/content/tip-panel");
			GameObject val = Object.Instantiate<GameObject>(obj, obj.transform.parent.parent);
			val.transform.localPosition = new Vector3(-35f, val.transform.localPosition.y + (float)BulletTimePlugin.StatusTextHeightOffset.Value, 0f);
			Object.Destroy((Object)(object)((Component)val.transform.Find("bg")).gameObject);
			Object.Destroy((Object)(object)((Component)val.transform.Find("icon")).gameObject);
			Object.Destroy((Object)(object)((Component)val.transform.Find("glow-1")).gameObject);
			Object.Destroy((Object)(object)((Component)val.transform.Find("achiev-ban-text")).gameObject);
			stateMessage = ((Component)val.transform.Find("text")).GetComponent<Text>();
		}
		((Component)((Component)stateMessage).transform.GetParent()).gameObject.SetActive(message != "");
		stateMessage.text = message;
		CurrentStatus = message;
	}
}
internal class GameSave_Patch
{
	public static bool isBlocked = false;

	public static bool isEnabled = false;

	private static Harmony harmony = null;

	private static readonly AutoResetEvent autoEvent = new AutoResetEvent(initialState: false);

	public static void Enable(bool enable)
	{
		if (NebulaCompat.NebulaIsInstalled)
		{
			isEnabled = enable;
			return;
		}
		if (enable && harmony == null)
		{
			Log.Debug("Patch GameSave_Patch");
			harmony = Harmony.CreateAndPatchAll(typeof(GameSave_Patch), (string)null);
			isEnabled = true;
		}
		if (!enable && harmony != null)
		{
			Log.Debug("Unpatch GameSave_Patch");
			harmony.UnpatchSelf();
			harmony = null;
			isEnabled = false;
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(UIGame), "_OnUpdate")]
	private static void UIGame_Prefix(UIGame __instance)
	{
		if (!GameMain.isRunning || __instance.willClose || GameStateManager.Interactable || isBlocked)
		{
			return;
		}
		bool num = ((ManualBehaviour)__instance.dysonEditor).active || (GameStateManager.LockFactory && __instance.isAnyFunctionWindowActive);
		bool flag = ((ManualBehaviour)__instance.statWindow).active || ((ManualBehaviour)__instance.replicator).active || ((ManualBehaviour)__instance.mechaWindow).active || ((ManualBehaviour)__instance.blueprintBrowser).active;
		if (!num || flag)
		{
			return;
		}
		if (((ManualBehaviour)__instance.dysonEditor).active)
		{
			if (string.IsNullOrEmpty(IngameUI.CurrentStatus))
			{
				ShowMessage(Localization.Translate("Read-Only"), Localization.Translate("Can't interact with game world during auto-save\nPlease wait or press ESC to close the window"));
			}
			else
			{
				ShowMessage(Localization.Translate("Read-Only"), Localization.Translate("Can't interact with game world when player is loading the game"));
			}
		}
		else
		{
			ShowMessage();
		}
		isBlocked = true;
	}

	private static void ShowMessage(string title = null, string message = null)
	{
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		UIDialog obj = UIDialog.CreateDialog("Prefabs/MessageBox VE");
		UIMessageBox val = (UIMessageBox)(object)((obj is UIMessageBox) ? obj : null);
		if (title != null && message != null)
		{
			val.m_TitleText.text = title;
			val.m_MessageText.text = message;
			((Component)((Component)val.m_Button1).transform.parent).gameObject.SetActive(false);
			((Component)((Component)val.m_Button2).transform.parent).gameObject.SetActive(false);
			((Component)((Component)val.m_Button3).transform.parent).gameObject.SetActive(false);
			((Component)val.m_IconImage).gameObject.SetActive(false);
		}
		else
		{
			((Component)val.m_WindowTrans).gameObject.SetActive(false);
			((Graphic)((Component)val).gameObject.GetComponent<Image>()).color = Color.clear;
		}
		UIMessageBox.PushMessage(val);
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(UIMessageBox), "CloseTopMessage")]
	private static void CloseTopMessage_Postfix(ref bool __result)
	{
		if (isBlocked)
		{
			__result = false;
			isBlocked = false;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	private static void BuildConfirm_Postfix(ref InputValue __result)
	{
		if (GameStateManager.LockFactory)
		{
			__result.onUp = false;
			__result.onDown = false;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	private static void InstantConstruct_Postfix(ref bool __result)
	{
		if (GameStateManager.LockFactory)
		{
			__result = false;
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(UIAutoSave), "_OnLateUpdate")]
	private static bool AutoSaveGuard(UIAutoSave __instance)
	{
		if (__instance.showTime == 0f && GameStateManager.Pause)
		{
			return false;
		}
		return true;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(UIAutoSave), "_OnLateUpdate")]
	private static void OverwriteSaveText(UIAutoSave __instance)
	{
		if (GameStateManager.IsSaving)
		{
			__instance.saveText.text = Localization.Translate("Saving...");
			__instance.showTime = 1.8f;
		}
	}

	[HarmonyTranspiler]
	[HarmonyPatch(typeof(UIAutoSave), "_OnLateUpdate")]
	public static IEnumerable<CodeInstruction> RemoveGC_Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		return new CodeMatcher(instructions, (ILGenerator)null).End().MatchBack(false, (CodeMatch[])(object)new CodeMatch[1]
		{
			new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(GameSave), "AutoSave", (Type[])null, (Type[])null), (string)null)
		}).SetOperandAndAdvance((object)AccessTools.Method(typeof(GameSave_Patch), "AsyncAutoSave", (Type[])null, (Type[])null))
			.InstructionEnumeration();
	}

	private static bool AsyncAutoSave()
	{
		if (!isEnabled)
		{
			return GameSave.AutoSave();
		}
		GameCamera.CaptureSaveScreenShot();
		bool tmp = GameStateManager.Pause;
		bool isFullscreenPaused = GameMain.isFullscreenPaused;
		if (NebulaCompat.IsMultiplayerActive)
		{
			GameMain.isFullscreenPaused = true;
			NebulaCompat.SetPacketProcessing(enable: false);
		}
		GameStateManager.SetPauseMode(value: true);
		GameStateManager.SetInteractable(value: false);
		GameStateManager.IsSaving = true;
		ThreadingHelper.Instance.StartAsyncInvoke((Func<Action>)delegate
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			HighStopwatch val = new HighStopwatch();
			val.Begin();
			Thread.Sleep((int)(1000.0 / FPSController.currentUPS));
			Log.Info($"Background Autosave start. UPS: {FPSController.currentUPS:F2}");
			bool result = GameSave.AutoSave();
			Log.Info($"Background Autosave end. Duration: {val.duration}s");
			return delegate
			{
				GC.Collect();
				GameStateManager.IsSaving = false;
				GameStateManager.SetInteractable(value: true);
				GameStateManager.SetPauseMode(tmp);
				GameMain.isFullscreenPaused = isFullscreenPaused;
				if (NebulaCompat.IsMultiplayerActive)
				{
					NebulaCompat.SetPacketProcessing(enable: true);
				}
				UIRoot.instance.uiGame.autoSave.saveText.text = (result ? Localization.Translate("保存成功") : Localization.Translate("保存失败"));
				UIRoot.instance.uiGame.autoSave.contentTweener.Play1To0();
			};
		});
		return false;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(GameCamera), "CaptureSaveScreenShot")]
	private static bool CaptureSaveScreenShot_Prefix()
	{
		if (!GameStateManager.Interactable)
		{
			return false;
		}
		return true;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(DysonSwarm), "Export")]
	private static bool Export_Prefix(DysonSwarm __instance, BinaryWriter w)
	{
		if (!GameStateManager.Interactable && ThreadingHelper.Instance.InvokeRequired)
		{
			autoEvent.Reset();
			ThreadingHelper.Instance.StartSyncInvoke((Action)delegate
			{
				__instance.Export(w);
				autoEvent.Set();
			});
			autoEvent.WaitOne(-1);
			return false;
		}
		return true;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(SpaceSector), "EndSave")]
	private static bool SpaceSectorEndSave_Prefix(SpaceSector __instance)
	{
		if (!GameStateManager.Interactable && ThreadingHelper.Instance.InvokeRequired)
		{
			autoEvent.Reset();
			ThreadingHelper.Instance.StartSyncInvoke((Action)delegate
			{
				__instance.EndSave();
				autoEvent.Set();
			});
			autoEvent.WaitOne(-1);
			return false;
		}
		return true;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Player), "Export")]
	private static void Player_Prefix()
	{
		if (!GameStateManager.Interactable)
		{
			Monitor.Enter(GameMain.data.mainPlayer);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "Export")]
	private static void Player_Postfix()
	{
		if (!GameStateManager.Interactable)
		{
			Monitor.Exit(GameMain.data.mainPlayer);
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(PlanetFactory), "Export")]
	private static void PlanetFactory_Prefix(PlanetFactory __instance)
	{
		if (!GameStateManager.Interactable && __instance.planetId == GameMain.localPlanet?.id)
		{
			GameStateManager.SetLockFactory(value: true);
			Thread.Sleep((int)Maths.Clamp(1000.0 / FPSController.currentUPS, 17.0, 1000.0));
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(PlanetFactory), "Export")]
	private static void PlanetFactory_Postfix(PlanetFactory __instance)
	{
		if (!GameStateManager.Interactable && __instance.planetId == GameMain.localPlanet?.id)
		{
			GameStateManager.SetLockFactory(value: false);
		}
	}
}
internal class GameMain_Patch
{
	private static bool pasueThisFrame;

	[HarmonyPrefix]
	[HarmonyPatch(typeof(GameMain), "FixedUpdate")]
	private static bool FixedUpdate_Prefix(GameMain __instance)
	{
		if ((!GameMain.isRunning || GameMain.isPaused) && !NebulaCompat.IsMultiplayerActive)
		{
			return true;
		}
		pasueThisFrame = GameStateManager.PauseInThisFrame();
		if (!pasueThisFrame)
		{
			return true;
		}
		if (GameStateManager.AdvanceTick)
		{
			__instance.timei++;
			__instance.timei_once++;
		}
		__instance.timef = (double)__instance.timei * (1.0 / 60.0);
		__instance.timef_once = (double)__instance.timei_once * (1.0 / 60.0);
		GameData data = GameMain.data;
		PerformanceMonitor.BeginLogicFrame();
		PerformanceMonitor.BeginSample((ECpuWorkEntry)2);
		PerformanceMonitor.BeginSample((ECpuWorkEntry)3);
		bool num = !__instance.isMenuDemo && data.DetermineLocalPlanet();
		data.DetermineRelative();
		__instance.SetStarmapReferences(GameMain.data);
		if (num)
		{
			GameCamera.instance.FrameLogic();
		}
		VFInput.UpdateGameStates();
		Player mainPlayer = GameMain.mainPlayer;
		if (mainPlayer != null && mainPlayer.controller.actionSail.fastTravelling)
		{
			GameMain.universeSimulator.GameTick(__instance.timef);
			data.DetermineRelative();
			PerformanceMonitor.EndSample((ECpuWorkEntry)3);
		}
		else
		{
			UniverseSimulatorGameTick();
			PerformanceMonitor.EndSample((ECpuWorkEntry)3);
			PerformanceMonitor.BeginSample((ECpuWorkEntry)31);
			data.mainPlayer.packageUtility.Count();
			PerformanceMonitor.EndSample((ECpuWorkEntry)31);
			if (GameMain.localPlanet != null && GameMain.localPlanet.factoryLoaded)
			{
				PerformanceMonitor.BeginSample((ECpuWorkEntry)42);
				GameMain.localPlanet.factory.cargoTraffic.ClearStates();
				GameMain.localPlanet.physics.GameTick();
				PerformanceMonitor.EndSample((ECpuWorkEntry)42);
			}
			if (data.spaceSector != null)
			{
				PerformanceMonitor.BeginSample((ECpuWorkEntry)42);
				if (!DSPGame.IsMenuDemo)
				{
					data.spaceSector.physics.GameTick();
				}
				PerformanceMonitor.EndSample((ECpuWorkEntry)42);
			}
			if (data.guideMission != null)
			{
				PerformanceMonitor.BeginSample((ECpuWorkEntry)4);
				data.guideMission.GameTick();
				PerformanceMonitor.EndSample((ECpuWorkEntry)4);
			}
			if (GameMain.mainPlayer != null)
			{
				PerformanceMonitor.BeginSample((ECpuWorkEntry)7);
				data.mainPlayer.ApplyGamePauseState(false);
				PlayerGameTick(__instance.timei);
				data.DetermineRelative();
				PerformanceMonitor.EndSample((ECpuWorkEntry)7);
			}
			if (data.spaceSector != null)
			{
				if (GameStateManager.Interactable)
				{
					SkillSystem_Patch.GameTick(data.spaceSector.skillSystem);
				}
				PerformanceMonitor.BeginSample((ECpuWorkEntry)42);
				data.spaceSector.model.PostGameTick();
				if (!DSPGame.IsMenuDemo)
				{
					data.spaceSector.physics.PostGameTick();
				}
				PerformanceMonitor.EndSample((ECpuWorkEntry)42);
			}
			if (data.localPlanet != null && data.localPlanet.factoryLoaded)
			{
				PerformanceMonitor.BeginSample((ECpuWorkEntry)43);
				data.localPlanet.audio.GameTick();
				PerformanceMonitor.EndSample((ECpuWorkEntry)43);
			}
		}
		PerformanceMonitor.EndSample((ECpuWorkEntry)2);
		PerformanceMonitor.EndLogicFrame();
		return false;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameMain), "FixedUpdate")]
	private static void FixedUpdate_Postfix()
	{
		if (pasueThisFrame)
		{
			pasueThisFrame = false;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameMain), "Begin")]
	private static void Begin_Postfix()
	{
		if (!GameMain.instance.isMenuDemo)
		{
			IngameUI.Init();
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(GameMain), "End")]
	private static void End_Postfix()
	{
		IngameUI.Dispose();
		GameStateManager.SetSpeedRatio(1f);
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(GameSave), "SaveCurrentGame")]
	private static void SaveCurrentGame_Prefix()
	{
		if (GameStateManager.StoredGameTick != 0L)
		{
			GameMain.gameTick = GameStateManager.StoredGameTick;
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(PlayerAnimator), "GamePauseLogic")]
	private static bool GamePauseLogic_Prefix(PlayerAnimator __instance, ref bool __result)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if (!GameStateManager.Pause)
		{
			return true;
		}
		if (GameStateManager.HotkeyPause && !GameStateManager.EnableMechaFunc)
		{
			__instance.PauseAllAnimations();
			__instance.motorBone.localPosition = Vector3.zero;
			__result = true;
			return false;
		}
		__result = false;
		return false;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(ABN_MechaPosition), "OnGameTick")]
	private static bool ABN_MechaPosition_Prefix()
	{
		return false;
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(UIStarmap), "StartFastTravelToPlanet")]
	private static bool StartFastTravelToPlanet_Prefix()
	{
		if (GameMain.isFullscreenPaused)
		{
			UIRealtimeTip.Popup("Can't teleport to another planet during BulletTime pause!", true, 0);
			return false;
		}
		return true;
	}

	private static void UniverseSimulatorGameTick()
	{
		//IL_001b: 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_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		UniverseSimulator universeSimulator = GameMain.universeSimulator;
		((Component)universeSimulator.backgroundStars).transform.position = ((Component)Camera.main).transform.position;
		if (GameMain.localPlanet != null)
		{
			((Component)universeSimulator.backgroundStars).transform.rotation = Quaternion.Inverse(GameMain.localPlanet.runtimeRotation);
		}
		else
		{
			((Component)universeSimulator.backgroundStars).transform.rotation = Quaternion.identity;
		}
		Vector3 position = GameMain.mainPlayer.position;
		VectorLF3 uPosition = GameMain.mainPlayer.uPosition;
		Vector3 position2 = ((Component)GameCamera.main).transform.position;
		Quaternion rotation = ((Component)GameCamera.main).transform.rotation;
		for (int i = 0; i < universeSimulator.starSimulators.Length; i++)
		{
			universeSimulator.starSimulators[i].UpdateUniversalPosition(position, uPosition, position2, rotation);
		}
		if (universeSimulator.planetSimulators == null)
		{
			return;
		}
		for (int j = 0; j < universeSimulator.planetSimulators.Length; j++)
		{
			if ((Object)(object)universeSimulator.planetSimulators[j] != (Object)null)
			{
				universeSimulator.planetSimulators[j].UpdateUniversalPosition(uPosition, position2);
			}
		}
	}

	private static void PlayerGameTick(long time)
	{
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		Player mainPlayer = GameMain.data.mainPlayer;
		if (mainPlayer == null)
		{
			return;
		}
		if (GameStateManager.HotkeyPause)
		{
			if (GameStateManager.EnableMechaFunc)
			{
				mainPlayer.GameTick(time);
				return;
			}
			mainPlayer.controller.SetCommandStateHeader();
			mainPlayer.controller.UpdateCommandState();
			mainPlayer.controller.GetInput();
			mainPlayer.controller.HandleBaseInput();
			((PlayerAction)mainPlayer.controller.actionRts).GameTick(time);
			((PlayerAction)mainPlayer.controller.actionBuild).GameTick(time);
			((PlayerAction)mainPlayer.controller.actionInspect).GameTick(time);
			mainPlayer.controller.ClearForce();
			mainPlayer.position = GameStateManager.PlayerPosition;
			mainPlayer.gizmo.GameTick();
			mainPlayer.orders.GameTick(time);
		}
		else if (GameStateManager.Interactable)
		{
			mainPlayer.GameTick(time);
		}
		else
		{
			Monitor.Enter(mainPlayer);
			mainPlayer.mecha.GenerateEnergy(1.0 / 60.0);
			if (mainPlayer.controller.cmd.raycast != null)
			{
				mainPlayer.controller.cmd.raycast.castVege.id = 0;
				mainPlayer.controller.cmd.raycast.castVein.id = 0;
			}
			mainPlayer.controller.GameTick(time);
			mainPlayer.gizmo.GameTick();
			mainPlayer.orders.GameTick(time);
			mainPlayer.mecha.forge.GameTick(time, 1f / 60f);
			mainPlayer.mecha.UpdateSkillColliders();
			Monitor.Exit(mainPlayer);
		}
	}
}
public static class GameStateManager
{
	private static float timer;

	public static bool Pause { get; private set; }

	public static bool ManualPause { get; set; }

	public static bool HotkeyPause { get; set; }

	public static bool EnableMechaFunc { get; set; }

	public static bool IsSaving { get; set; }

	public static bool AdvanceTick { get; private set; } = true;


	public static long StoredGameTick { get; private set; }

	public static bool Interactable { get; private set; } = true;


	public static bool LockFactory { get; private set; } = false;


	public static float SkipRatio { get; private set; }

	public static Vector3 PlayerPosition { get; set; }

	public static void SetSpeedRatio(float value)
	{
		if (value == 0f)
		{
			SetPauseMode(value: true);
			SkipRatio = 1f;
			return;
		}
		if (Pause)
		{
			SetPauseMode(value: false);
		}
		SkipRatio = 1f - value;
		timer = 0f;
	}

	public static void SetPauseMode(bool value)
	{
		if (value)
		{
			Pause = true;
			if (StoredGameTick == 0L)
			{
				StoredGameTick = GameMain.gameTick;
				Log.Debug($"Enter pause mode, gameTick = {StoredGameTick}");
				if (NebulaCompat.IsClient)
				{
					FPSController.SetFixUPS(0.0);
				}
			}
		}
		else
		{
			Pause = false;
			if (StoredGameTick != 0L)
			{
				Log.Debug($"Exit pause mode, duration: {GameMain.gameTick - StoredGameTick} ticks.");
				GameMain.gameTick = StoredGameTick;
				StoredGameTick = 0L;
			}
			GameMain.isFullscreenPaused = false;
			SetLockFactory(value: false);
		}
		IngameUI.OnPauseModeChange(value);
	}

	public static bool PauseInThisFrame()
	{
		bool result = Pause;
		AdvanceTick = GameMain.data.guideComplete && Interactable;
		if (HotkeyPause && !EnableMechaFunc)
		{
			AdvanceTick = false;
		}
		if (!Pause)
		{
			timer += SkipRatio;
			if (timer >= 1f)
			{
				timer -= 1f;
				result = true;
				AdvanceTick = false;
			}
		}
		return result;
	}

	public static void SetInteractable(bool value)
	{
		Interactable = value;
		if (Interactable && GameSave_Patch.isBlocked)
		{
			GameSave_Patch.isBlocked = false;
			UIMessageBox.CloseTopMessage();
		}
	}

	public static void SetLockFactory(bool value)
	{
		LockFactory = value;
		if (!LockFactory && !((ManualBehaviour)UIRoot.instance.uiGame.dysonEditor).active && GameSave_Patch.isBlocked)
		{
			GameSave_Patch.isBlocked = false;
			UIMessageBox.CloseTopMessage();
		}
	}

	public static void SetSyncingLock(bool isLock)
	{
		SetInteractable(!isLock);
		SetLockFactory(isLock);
		Log.Debug("SetSyncingLock: " + isLock);
	}
}
[BepInPlugin("com.starfi5h.plugin.BulletTime", "BulletTime", "1.5.2")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class BulletTimePlugin : BaseUnityPlugin
{
	public const string GUID = "com.starfi5h.plugin.BulletTime";

	public const string NAME = "BulletTime";

	public const string VERSION = "1.5.2";

	public static ConfigEntry<bool> EnableBackgroundAutosave;

	public static ConfigEntry<bool> EnableFastLoading;

	public static ConfigEntry<bool> RemoveGC;

	public static ConfigEntry<float> StartingSpeed;

	public static ConfigEntry<KeyboardShortcut> KeyAutosave;

	public static ConfigEntry<KeyCode> KeyPause;

	public static ConfigEntry<int> StatusTextHeightOffset;

	public static ConfigEntry<string> StatusTextPause;

	public static ConfigEntry<bool> EnableMechaFunc;

	public static ConfigEntry<int> MaxSpeedupScale;

	private static Harmony harmony;

	private void LoadConfig()
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Expected O, but got Unknown
		KeyAutosave = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkey", "KeyAutosave", new KeyboardShortcut((KeyCode)291, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Keyboard shortcut for auto-save\n自动存档的热键组合");
		KeyPause = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Hotkey", "KeyPause", (KeyCode)19, "Hotkey for toggling special pause mode\n特殊时停模式的热键");
		EnableMechaFunc = ((BaseUnityPlugin)this).Config.Bind<bool>("Pause", "EnableMechaFunc", false, "Enable mecha function in hotkey pause mode\n在热键暂停模式下启用机甲功能");
		EnableBackgroundAutosave = ((BaseUnityPlugin)this).Config.Bind<bool>("Save", "EnableBackgroundAutosave", false, "Do auto-save in background thread\n在背景执行自动存档");
		EnableFastLoading = ((BaseUnityPlugin)this).Config.Bind<bool>("Speed", "EnableFastLoading", true, "Increase main menu loading speed\n加快载入主选单");
		RemoveGC = ((BaseUnityPlugin)this).Config.Bind<bool>("Speed", "RemoveGC", true, "Remove force garbage collection of build tools\n移除建筑工具的强制内存回收");
		StartingSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Speed", "StartingSpeed", 100f, new ConfigDescription("Game speed when the game begin (0-100)\n游戏开始时的游戏速度 (0-100)", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
		StatusTextHeightOffset = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "StatusTextHeightOffset", 100, "Height of Status text relative to auto save text\n状态提示相对于自动保存提示的高度");
		StatusTextPause = ((BaseUnityPlugin)this).Config.Bind<string>("UI", "StatusTextPause", "Bullet Time", "Status text when in pause mode\n暂停时的状态提示文字");
		MaxSpeedupScale = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "MaxSpeedupScale", 10, "Maximum game speed multiplier for speedup button\n加速按钮的最大游戏速度倍率");
		if (MaxSpeedupScale.Value <= 0)
		{
			MaxSpeedupScale.Value = 1;
		}
		GameStateManager.EnableMechaFunc = EnableMechaFunc.Value;
	}

	public void Start()
	{
		//IL_0000: 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_003f: Expected O, but got Unknown
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		if (GameConfig.gameVersion.Major == 0 && GameConfig.gameVersion.Minor < 10)
		{
			throw new Exception("BulletTime 1.5.2 only support 0.10.x game version!\nPlease roll back to BulletTime 1.2.14 for 0.9.x game version");
		}
		Log.Init(((BaseUnityPlugin)this).Logger);
		harmony = new Harmony("com.starfi5h.plugin.BulletTime");
		LoadConfig();
		try
		{
			if (Chainloader.PluginInfos.ContainsKey("dsp.nebula-multiplayer"))
			{
				NebulaCompat.Init(harmony);
			}
			harmony.PatchAll(typeof(GameMain_Patch));
			harmony.PatchAll(typeof(IngameUI));
			if (NebulaCompat.NebulaIsInstalled)
			{
				harmony.PatchAll(typeof(GameSave_Patch));
				GameSave_Patch.Enable(enable: true);
			}
			else if (EnableBackgroundAutosave.Value)
			{
				GameSave_Patch.Enable(enable: true);
			}
			if (EnableFastLoading.Value)
			{
				try
				{
					harmony.PatchAll(typeof(GameLoader_Patch));
				}
				catch
				{
					Log.Warn("Fast loading patch didn't success!");
				}
			}
			if (RemoveGC.Value)
			{
				try
				{
					harmony.PatchAll(typeof(BuildTool_Patch));
					return;
				}
				catch
				{
					Log.Warn("BuildTool no GC patch didn't success!");
					return;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)ex);
			throw ex;
		}
	}

	public void Update()
	{
		//IL_0005: 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_0020: Unknown result type (might be due to invalid IL or missing references)
		if (Input.GetKeyDown(KeyPause.Value))
		{
			IngameUI.OnKeyPause();
		}
		KeyboardShortcut value = KeyAutosave.Value;
		if (((KeyboardShortcut)(ref value)).IsDown() && UIRoot.instance.uiGame.autoSave.showTime == 0f)
		{
			UIAutoSave.lastSaveTick = 0L;
			Log.Debug("Trigger auto save by hotkey");
		}
	}

	public void OnDestroy()
	{
		harmony.UnpatchSelf();
		harmony = null;
		IngameUI.Dispose();
		GameSave_Patch.Enable(enable: false);
		if (Chainloader.PluginInfos.ContainsKey("dsp.nebula-multiplayer"))
		{
			NebulaCompat.Dispose();
		}
	}
}
public static class Log
{
	private static ManualLogSource _logger;

	private static int count;

	public static void Init(ManualLogSource logger)
	{
		_logger = logger;
	}

	public static void Error(object obj)
	{
		_logger.LogError(obj);
	}

	public static void Warn(object obj)
	{
		_logger.LogWarning(obj);
	}

	public static void Info(object obj)
	{
		_logger.LogInfo(obj);
	}

	public static void Debug(object obj)
	{
		_logger.LogDebug(obj);
	}

	[Conditional("DEBUG")]
	public static void Dev(object obj)
	{
		_logger.LogDebug(obj);
	}

	public static void Print(int period, object obj)
	{
		if (count++ % period == 0)
		{
			_logger.LogDebug(obj);
		}
	}
}