Decompiled source of BulletTime v1.5.5

BulletTime.dll

Decompiled a week 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 BulletTime.Nebula;
using HarmonyLib;
using NebulaAPI;
using NebulaAPI.GameState;
using NebulaAPI.Networking;
using NebulaAPI.Packets;
using NebulaModel;
using NebulaModel.DataStructures.Chat;
using NebulaWorld;
using NebulaWorld.GameStates;
using NebulaWorld.MonoBehaviours.Local.Chat;
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.5.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;
		}
	}
	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>();
				}
				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:
				if (GameStateManager.Pause)
				{
					OnKeyPause();
					break;
				}
				FPSController.SetFixUPS(0.0);
				if (NebulaCompat.IsMultiplayerActive && NebulaCompat.SyncUps)
				{
					if (NebulaCompat.IsClient)
					{
						NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedReset);
					}
					else
					{
						NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedSet);
					}
				}
				break;
			case 2:
				if (GameStateManager.Pause)
				{
					OnKeyPause();
				}
				else if (FPSController.instance.fixUPS == 0.0)
				{
					FPSController.SetFixUPS(120.0);
					if (NebulaCompat.IsMultiplayerActive && NebulaCompat.SyncUps)
					{
						if (NebulaCompat.IsClient)
						{
							NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedUp);
						}
						else
						{
							NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedSet);
						}
					}
				}
				else
				{
					if (!(FPSController.instance.fixUPS < 60.0 * (double)BulletTimePlugin.MaxSpeedupScale.Value))
					{
						break;
					}
					FPSController.SetFixUPS(FPSController.instance.fixUPS + 60.0);
					if (NebulaCompat.IsMultiplayerActive && NebulaCompat.SyncUps)
					{
						if (NebulaCompat.IsClient)
						{
							NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedUp);
						}
						else
						{
							NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedSet);
						}
					}
				}
				break;
			}
			SetSpeedRatioText();
		}

		private static void OnSpeedupButtonRightClcik(int _)
		{
			if (GameStateManager.Pause)
			{
				OnKeyPause();
			}
			FPSController.SetFixUPS(60.0 * (double)BulletTimePlugin.MaxSpeedupScale.Value);
			SetSpeedRatioText();
			if (NebulaCompat.IsMultiplayerActive && NebulaCompat.SyncUps)
			{
				if (NebulaCompat.IsClient)
				{
					NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedMax);
				}
				else
				{
					NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.SpeedSet);
				}
			}
		}

		public 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);
						NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.Pause);
					}
					else
					{
						NebulaCompat.SendPacket(PauseEvent.Resume);
						NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.Resume);
					}
				}
			}
			else if (!GameStateManager.Pause)
			{
				if (UIRoot.instance.uiGame.autoSave.showTime == 0f)
				{
					GameStateManager.ManualPause = true;
					GameStateManager.SetPauseMode(value: true);
					SetHotkeyPauseMode(active: true);
					if (NebulaCompat.IsMultiplayerActive)
					{
						NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.Pause);
					}
				}
			}
			else if (GameStateManager.Interactable)
			{
				OnSliderChange(slider.value);
				if (NebulaCompat.IsMultiplayerActive)
				{
					NebulaCompat.SendPlayerActionPacket(EPlayerSpeedAction.Resume);
				}
			}
		}

		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();
					GlobalObject.SaveOpCounter();
				};
			});
			return false;
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(GameSave), "AutoSave")]
		public static IEnumerable<CodeInstruction> AutoSave_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(GlobalObject), "SaveOpCounter", (Type[])null, (Type[])null), (string)null)
			}).SetAndAdvance(OpCodes.Nop, (object)null)
				.InstructionEnumeration();
		}

		[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.5")]
	[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.5";

		public static ConfigEntry<bool> EnableBackgroundAutosave;

		public static ConfigEntry<bool> EnableHotkeyAutosave;

		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_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: 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在背景执行自动存档");
			EnableHotkeyAutosave = ((BaseUnityPlugin)this).Config.Bind<bool>("Save", "EnableHotkeyAutosave", false, "Enable hotkey to trigger autosave\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.5 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(KeyPause.Value))
			{
				IngameUI.OnKeyPause();
			}
			if (EnableHotkeyAutosave.Value)
			{
				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);
			}
		}
	}
}
namespace BulletTime.Nebula
{
	internal class PlayerSpeedActionPacket
	{
		public string Username { get; set; }

		public EPlayerSpeedAction Action { get; set; }

		public float Value { get; set; }

		public PlayerSpeedActionPacket()
		{
		}

		public PlayerSpeedActionPacket(EPlayerSpeedAction action, string username)
		{
			Action = action;
			Username = username;
		}
	}
	public enum EPlayerSpeedAction
	{
		Deny,
		Pause,
		Resume,
		SpeedSet,
		SpeedUp,
		SpeedMax,
		SpeedReset
	}
	[RegisterPacketProcessor]
	internal class PlayerActionPacketProcessor : BasePacketProcessor<PlayerSpeedActionPacket>
	{
		public override void ProcessPacket(PlayerSpeedActionPacket packet, INebulaConnection conn)
		{
			NebulaCompat.IsIncomingPacket = true;
			if (base.IsHost)
			{
				double num = FPSController.instance.fixUPS;
				switch (packet.Action)
				{
				case EPlayerSpeedAction.SpeedUp:
					num = ((num == 0.0) ? 120.0 : (num + 60.0));
					break;
				case EPlayerSpeedAction.SpeedMax:
					num = 60.0 * (double)BulletTimePlugin.MaxSpeedupScale.Value;
					break;
				case EPlayerSpeedAction.SpeedReset:
					num = 0.0;
					break;
				}
				if (num != FPSController.instance.fixUPS)
				{
					packet.Action = EPlayerSpeedAction.SpeedSet;
					packet.Value = (float)((num == 0.0) ? 1.0 : (num / 60.0));
					FPSController.SetFixUPS(num);
					IngameUI.SetSpeedRatioText();
				}
				Multiplayer.Session.Network.SendPacket<PlayerSpeedActionPacket>(packet);
			}
			ShowPacket(packet);
			NebulaCompat.IsIncomingPacket = false;
		}

		public static void ShowPacket(PlayerSpeedActionPacket packet)
		{
			switch (packet.Action)
			{
			case EPlayerSpeedAction.Deny:
				NebulaCompat.ShowMessageInChat(Localization.Translate("The server rejects the request."));
				break;
			case EPlayerSpeedAction.Pause:
				NebulaCompat.ShowMessageInChat(string.Format(Localization.Translate("{0} pause the game"), packet.Username));
				break;
			case EPlayerSpeedAction.Resume:
				NebulaCompat.ShowMessageInChat(string.Format(Localization.Translate("{0} resume the game"), packet.Username));
				break;
			case EPlayerSpeedAction.SpeedSet:
				NebulaCompat.ShowMessageInChat(string.Format(Localization.Translate("{0} set game speed = {1:F1}"), packet.Username, packet.Value));
				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 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)
		{
			NebulaCompat.IsIncomingPacket = true;
			NebulaPatch.SetProgessMode(enable: false);
			switch (packet.Event)
			{
			case PauseEvent.Resume:
				if (base.IsHost)
				{
					Multiplayer.Session.World.HidePingIndicator();
					if (!GameStateManager.Interactable || !GameStateManager.Pause)
					{
						return;
					}
					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)
					{
						return;
					}
					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();
					Multiplayer.Session.World.HidePingIndicator();
				}
				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);
					Multiplayer.Session.World.HidePingIndicator();
				}
				break;
			}
			NebulaCompat.IsIncomingPacket = false;
		}
	}
	public static 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;
		}
	}
	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 SyncUps { get; private set; }

		public static bool IsIncomingPacket { get; 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;
			SyncUps = Config.Options.SyncUps;
			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 SendPlayerActionPacket(EPlayerSpeedAction action)
		{
			if (IsIncomingPacket)
			{
				return;
			}
			string username = NebulaModAPI.MultiplayerSession.LocalPlayer.Data.Username;
			PlayerSpeedActionPacket playerSpeedActionPacket = new PlayerSpeedActionPacket(action, username);
			if (!IsClient)
			{
				if (action == EPlayerSpeedAction.SpeedSet)
				{
					double fixUPS = FPSController.instance.fixUPS;
					playerSpeedActionPacket.Value = (float)((fixUPS == 0.0) ? 1.0 : (fixUPS / 60.0));
				}
				PlayerActionPacketProcessor.ShowPacket(playerSpeedActionPacket);
			}
			NebulaModAPI.MultiplayerSession.Network.SendPacket<PlayerSpeedActionPacket>(playerSpeedActionPacket);
		}

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

		public static void ShowMessageInChat(string message)
		{
			if (Config.Options.EnableTimestamp)
			{
				message = $"[{DateTime.Now:HH:mm}] " + message;
			}
			ChatManager instance = ChatManager.Instance;
			if (instance != null)
			{
				instance.SendChatMessage(message, (ChatMessageType)1);
			}
		}
	}
}