Decompiled source of CruiserJumpPractice v0.2.0

com.aoirint.CruiserJumpPractice.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CruiserJumpPractice.Core.Handlers;
using CruiserJumpPractice.Core.Ports;
using CruiserJumpPractice.Core.Presentation;
using CruiserJumpPractice.Core.Snapshots;
using CruiserJumpPractice.Core.State;
using CruiserJumpPractice.Core.UseCases;
using CruiserJumpPractice.Core.UseCases.Client;
using CruiserJumpPractice.Core.UseCases.Server;
using CruiserJumpPractice.Core.Validation;
using CruiserJumpPractice.Interop;
using CruiserJumpPractice.Interop.Game;
using CruiserJumpPractice.Interop.Game.Adapters;
using CruiserJumpPractice.Interop.Game.Behaviours;
using CruiserJumpPractice.Interop.Game.Patches;
using CruiserJumpPractice.Interop.InputUtils;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.aoirint.CruiserJumpPractice")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0+e58abd29d5b04d7eefcb7d5ab4e9587839a48858")]
[assembly: AssemblyProduct("CruiserJumpPractice")]
[assembly: AssemblyTitle("com.aoirint.CruiserJumpPractice")]
[assembly: AssemblyVersion("0.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CruiserJumpPractice
{
	[BepInPlugin("com.aoirint.CruiserJumpPractice", "CruiserJumpPractice", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("Lethal Company.exe")]
	public class CruiserJumpPractice : BaseUnityPlugin
	{
		private static PluginController? controller;

		internal static PluginController Controller => controller;

		private void Awake()
		{
			BepInExPluginLogger bepInExPluginLogger = new BepInExPluginLogger(((BaseUnityPlugin)this).Logger);
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "ValidationLogging", false, "Enable structured validation logs for release validation and troubleshooting.");
			IValidationLogger validationLogger;
			if (!val.Value)
			{
				IValidationLogger instance = DisabledValidationLogger.Instance;
				validationLogger = instance;
			}
			else
			{
				IValidationLogger instance = new BepInExValidationLogger(bepInExPluginLogger, DateTime.UtcNow);
				validationLogger = instance;
			}
			IValidationLogger validationLogger2 = validationLogger;
			validationLogger2.Record(ValidationLogRecord.PluginLoaded("0.2.0", val.Value));
			controller = PluginController.Create(bepInExPluginLogger, validationLogger2);
			HarmonyPatchInstaller.Install();
			bepInExPluginLogger.LogInfo("Plugin CruiserJumpPractice v0.2.0 is loaded!");
		}
	}
	internal sealed class PluginController
	{
		private readonly IGameInterop gameInterop;

		private readonly IValidationLogger validationLogger;

		private readonly FrameHandler frameHandler;

		private readonly StartupHandler startupHandler;

		private readonly BaseGameAppliedStateValidationHandler baseGameAppliedStateValidationHandler;

		private readonly SaveCruiserStateUseCase saveCruiserStateUseCase;

		private readonly LoadCruiserStateUseCase loadCruiserStateUseCase;

		private readonly PresentSaveCruiserStateResultUseCase presentSaveCruiserStateResultUseCase;

		private readonly PresentLoadCruiserStateResultUseCase presentLoadCruiserStateResultUseCase;

		private PluginController(IGameInterop gameInterop, IValidationLogger validationLogger, FrameHandler frameHandler, StartupHandler startupHandler, BaseGameAppliedStateValidationHandler baseGameAppliedStateValidationHandler, SaveCruiserStateUseCase saveCruiserStateUseCase, LoadCruiserStateUseCase loadCruiserStateUseCase, PresentSaveCruiserStateResultUseCase presentSaveCruiserStateResultUseCase, PresentLoadCruiserStateResultUseCase presentLoadCruiserStateResultUseCase)
		{
			this.gameInterop = gameInterop;
			this.validationLogger = validationLogger;
			this.frameHandler = frameHandler;
			this.startupHandler = startupHandler;
			this.baseGameAppliedStateValidationHandler = baseGameAppliedStateValidationHandler;
			this.saveCruiserStateUseCase = saveCruiserStateUseCase;
			this.loadCruiserStateUseCase = loadCruiserStateUseCase;
			this.presentSaveCruiserStateResultUseCase = presentSaveCruiserStateResultUseCase;
			this.presentLoadCruiserStateResultUseCase = presentLoadCruiserStateResultUseCase;
		}

		public static PluginController Create(IPluginLogger logger, IValidationLogger validationLogger)
		{
			InputUtilsPracticeInput practiceInput = new InputUtilsPracticeInput(new InputUtilsActions());
			IGameInterop gameInterop = new GameInterop(logger, validationLogger);
			CruiserStateStore cruiserStateStore = new CruiserStateStore();
			BaseGameAppliedStateValidationStore stateStore = new BaseGameAppliedStateValidationStore();
			validationLogger.Record(ValidationLogRecord.StateStoreCreated());
			SaveCruiserStateUseCase saveCruiserStateUseCase = new SaveCruiserStateUseCase(gameInterop, cruiserStateStore, logger, validationLogger);
			LoadCruiserStateUseCase loadCruiserStateUseCase = new LoadCruiserStateUseCase(gameInterop, cruiserStateStore, logger, validationLogger);
			RequestSaveCruiserStateUseCase requestSaveCruiserStateUseCase = new RequestSaveCruiserStateUseCase(gameInterop, validationLogger);
			RequestLoadCruiserStateUseCase requestLoadCruiserStateUseCase = new RequestLoadCruiserStateUseCase(gameInterop, validationLogger);
			ToggleMagnetUseCase toggleMagnetUseCase = new ToggleMagnetUseCase(gameInterop, validationLogger);
			PresentSaveCruiserStateResultUseCase presentSaveCruiserStateResultUseCase = new PresentSaveCruiserStateResultUseCase(gameInterop, logger);
			PresentLoadCruiserStateResultUseCase presentLoadCruiserStateResultUseCase = new PresentLoadCruiserStateResultUseCase(gameInterop, logger);
			FrameHandler frameHandler = new FrameHandler(gameInterop, practiceInput, validationLogger, requestSaveCruiserStateUseCase, requestLoadCruiserStateUseCase, toggleMagnetUseCase);
			validationLogger.Record(ValidationLogRecord.ControllerCreated());
			return new PluginController(gameInterop, validationLogger, frameHandler, new StartupHandler(gameInterop, validationLogger), new BaseGameAppliedStateValidationHandler(gameInterop, validationLogger, stateStore), saveCruiserStateUseCase, loadCruiserStateUseCase, presentSaveCruiserStateResultUseCase, presentLoadCruiserStateResultUseCase);
		}

		public void HandleStartup()
		{
			startupHandler.HandleStartup();
		}

		public void HandleFrame()
		{
			frameHandler.HandleFrame();
		}

		public SaveCruiserStateResult SaveCruiserState()
		{
			return saveCruiserStateUseCase.Execute();
		}

		public LoadCruiserStateResult LoadCruiserState()
		{
			return loadCruiserStateUseCase.Execute();
		}

		public void PresentSaveCruiserStateResult(SaveCruiserStateResult result)
		{
			presentSaveCruiserStateResultUseCase.Execute(result);
		}

		public void PresentLoadCruiserStateResult(LoadCruiserStateResult result)
		{
			presentLoadCruiserStateResultUseCase.Execute(result);
		}

		public void RecordSaveServerRpcReceived()
		{
			validationLogger.Record(ValidationLogRecord.SaveServerRpcReceived(GetRole()));
		}

		public void RecordSaveClientRpcReceived(SaveCruiserStateResult result)
		{
			validationLogger.Record(ValidationLogRecord.SaveClientRpcReceived(GetRole(), result));
		}

		public void RecordLoadServerRpcReceived()
		{
			validationLogger.Record(ValidationLogRecord.LoadServerRpcReceived(GetRole()));
		}

		public void RecordLoadClientRpcReceived(LoadCruiserStateResult result)
		{
			validationLogger.Record(ValidationLogRecord.LoadClientRpcReceived(GetRole(), result));
		}

		public void HandleBaseGameEngineOilClientRpcEntered()
		{
			baseGameAppliedStateValidationHandler.EnterEngineOilClientRpc();
		}

		public void HandleBaseGameEngineOilClientRpcExited()
		{
			baseGameAppliedStateValidationHandler.ExitEngineOilClientRpc();
		}

		public void HandleBaseGameEngineOilLocalPreApply()
		{
			baseGameAppliedStateValidationHandler.HandleEngineOilLocalPreApply();
		}

		public void HandleBaseGameEngineOilLocalApplied()
		{
			baseGameAppliedStateValidationHandler.HandleEngineOilLocalApplied();
		}

		public void HandleBaseGameTurboClientRpcEntered()
		{
			baseGameAppliedStateValidationHandler.EnterTurboClientRpc();
		}

		public void HandleBaseGameTurboClientRpcExited()
		{
			baseGameAppliedStateValidationHandler.ExitTurboClientRpc();
		}

		public void HandleBaseGameTurboLocalPreApply()
		{
			baseGameAppliedStateValidationHandler.HandleTurboLocalPreApply();
		}

		public void HandleBaseGameTurboLocalApplied()
		{
			baseGameAppliedStateValidationHandler.HandleTurboLocalApplied();
		}

		public void HandleBaseGameShipMagnetLocalPreApply()
		{
			baseGameAppliedStateValidationHandler.HandleShipMagnetLocalPreApply();
		}

		public void HandleBaseGameShipMagnetLocalApplied()
		{
			baseGameAppliedStateValidationHandler.HandleShipMagnetLocalApplied();
		}

		public void HandleBaseGameShipMagnetClientRpcPreApply()
		{
			baseGameAppliedStateValidationHandler.HandleShipMagnetClientRpcPreApply();
		}

		public void HandleBaseGameShipMagnetClientRpcApplied()
		{
			baseGameAppliedStateValidationHandler.HandleShipMagnetClientRpcApplied();
		}

		private ValidationLogRole GetRole()
		{
			if (!gameInterop.IsHost())
			{
				return ValidationLogRole.Client;
			}
			return ValidationLogRole.Host;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "com.aoirint.CruiserJumpPractice";

		public const string PLUGIN_NAME = "CruiserJumpPractice";

		public const string PLUGIN_VERSION = "0.2.0";
	}
}
namespace CruiserJumpPractice.Interop
{
	internal sealed class BepInExPluginLogger : IPluginLogger
	{
		private readonly ManualLogSource logger;

		public BepInExPluginLogger(ManualLogSource logger)
		{
			this.logger = logger;
		}

		public void LogDebug(string message)
		{
			logger.LogDebug((object)message);
		}

		public void LogInfo(string message)
		{
			logger.LogInfo((object)message);
		}

		public void LogError(string message)
		{
			logger.LogError((object)message);
		}
	}
	internal sealed class BepInExValidationLogger : IValidationLogger
	{
		private const int SchemaVersion = 1;

		private const string Prefix = "[CJP_VALIDATION] ";

		private readonly IPluginLogger logger;

		private readonly string runId;

		private int sequence;

		public BepInExValidationLogger(IPluginLogger logger, DateTime startupTimeUtc)
		{
			this.logger = logger;
			runId = CreateRunId(startupTimeUtc);
		}

		public void Record(ValidationLogRecord record)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["schema"] = 1,
				["ts"] = FormatTimestamp(DateTime.UtcNow),
				["run"] = runId,
				["seq"] = ++sequence,
				["event"] = record.EventName
			};
			if (record.Fields != null)
			{
				foreach (KeyValuePair<string, object> field in record.Fields)
				{
					dictionary[field.Key] = field.Value;
				}
			}
			logger.LogInfo("[CJP_VALIDATION] " + JsonConvert.SerializeObject((object)dictionary, (Formatting)0));
		}

		private static string CreateRunId(DateTime startupTimeUtc)
		{
			string text = startupTimeUtc.ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
			string text2 = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture).Substring(0, 6);
			return text + "-" + text2;
		}

		private static string FormatTimestamp(DateTime timestampUtc)
		{
			return timestampUtc.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture);
		}
	}
}
namespace CruiserJumpPractice.Interop.InputUtils
{
	internal sealed class InputUtilsActions : LcInputActions
	{
		[InputAction(/*Could not decode attribute arguments.*/)]
		public InputAction? LoadCruiserKey { get; set; }

		[InputAction(/*Could not decode attribute arguments.*/)]
		public InputAction? SaveCruiserKey { get; set; }

		[InputAction(/*Could not decode attribute arguments.*/)]
		public InputAction? ToggleMagnetKey { get; set; }
	}
	internal sealed class InputUtilsPracticeInput : IPracticeInput
	{
		private readonly InputUtilsActions inputActions;

		public bool SaveCruiserTriggered
		{
			get
			{
				InputAction? saveCruiserKey = inputActions.SaveCruiserKey;
				if (saveCruiserKey == null)
				{
					return false;
				}
				return saveCruiserKey.triggered;
			}
		}

		public bool LoadCruiserTriggered
		{
			get
			{
				InputAction? loadCruiserKey = inputActions.LoadCruiserKey;
				if (loadCruiserKey == null)
				{
					return false;
				}
				return loadCruiserKey.triggered;
			}
		}

		public bool ToggleMagnetTriggered
		{
			get
			{
				InputAction? toggleMagnetKey = inputActions.ToggleMagnetKey;
				if (toggleMagnetKey == null)
				{
					return false;
				}
				return toggleMagnetKey.triggered;
			}
		}

		public InputUtilsPracticeInput(InputUtilsActions inputActions)
		{
			this.inputActions = inputActions;
		}
	}
}
namespace CruiserJumpPractice.Interop.Game
{
	internal sealed class GameInterop : IGameInterop
	{
		private readonly NetworkAdapter networkInterop;

		private readonly IValidationLogger validationLogger;

		private readonly PlayerAdapter playerInterop;

		private readonly HudAdapter hudInterop;

		private readonly RpcSurrogateAdapter rpcSurrogateInterop;

		private readonly CruiserAdapter cruiserInterop;

		private readonly ShipMagnetAdapter shipMagnetInterop;

		public GameInterop(IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.validationLogger = validationLogger;
			GameObjectAdapter gameObjects = new GameObjectAdapter(logger);
			networkInterop = new NetworkAdapter(logger, gameObjects);
			playerInterop = new PlayerAdapter(logger, gameObjects);
			hudInterop = new HudAdapter(logger, gameObjects);
			rpcSurrogateInterop = new RpcSurrogateAdapter(logger, gameObjects, validationLogger);
			cruiserInterop = new CruiserAdapter(logger, gameObjects);
			shipMagnetInterop = new ShipMagnetAdapter(logger, gameObjects);
		}

		public bool IsHost()
		{
			return networkInterop.IsHost();
		}

		public LocalPlayerBusyState GetLocalPlayerBusyState()
		{
			return playerInterop.GetLocalPlayerBusyState();
		}

		public void DisplayTip(HudTipMessage message)
		{
			validationLogger.Record(ValidationLogRecord.HudTip(GetRole(), message));
			hudInterop.DisplayTip(message.HeaderText, message.BodyText);
		}

		public RpcSurrogateSpawnResult SpawnRpcSurrogate()
		{
			return rpcSurrogateInterop.SpawnRpcSurrogate();
		}

		public void RequestSaveCruiserState()
		{
			rpcSurrogateInterop.GetRpcSurrogateBehaviour().SaveCruiserStateServerRpc();
		}

		public void RequestLoadCruiserState()
		{
			rpcSurrogateInterop.GetRpcSurrogateBehaviour().LoadCruiserStateServerRpc();
		}

		public bool CruiserExists()
		{
			return (Object)(object)cruiserInterop.FindCruiser() != (Object)null;
		}

		public CruiserSnapshot? CaptureCruiser()
		{
			VehicleController val = cruiserInterop.FindCruiser();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return cruiserInterop.CaptureCruiser(val);
		}

		public int? GetCruiserCarHP()
		{
			VehicleController val = cruiserInterop.FindCruiser();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return CruiserAdapter.GetCarHP(val);
		}

		public int? GetCruiserTurboBoosts()
		{
			VehicleController val = cruiserInterop.FindCruiser();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return CruiserAdapter.GetTurboBoosts(val);
		}

		public CruiserRestoreObservation RestoreCruiser(CruiserSnapshot snapshot)
		{
			VehicleController val = cruiserInterop.FindCruiser();
			if ((Object)(object)val == (Object)null)
			{
				throw new GameInteropException("No cruiser found.");
			}
			return cruiserInterop.RestoreCruiser(val, snapshot);
		}

		public bool IsCruiserMagnetedToShip()
		{
			VehicleController val = cruiserInterop.FindCruiser();
			if ((Object)(object)val == (Object)null)
			{
				throw new GameInteropException("No cruiser found.");
			}
			return cruiserInterop.IsCruiserMagnetedToShip(val);
		}

		public bool IsShipMagnetOn()
		{
			return shipMagnetInterop.IsShipMagnetOn();
		}

		public void ToggleShipMagnet()
		{
			shipMagnetInterop.ToggleShipMagnet();
		}

		private ValidationLogRole GetRole()
		{
			if (!IsHost())
			{
				return ValidationLogRole.Client;
			}
			return ValidationLogRole.Host;
		}
	}
	internal sealed class GameInteropException : Exception
	{
		public GameInteropException(string message)
			: base(message)
		{
		}
	}
}
namespace CruiserJumpPractice.Interop.Game.Patches
{
	internal static class HarmonyPatchInstaller
	{
		private static readonly Harmony harmony = new Harmony("com.aoirint.CruiserJumpPractice");

		public static void Install()
		{
			harmony.PatchAll(typeof(HarmonyPatchInstaller).Assembly);
		}
	}
	[HarmonyPatch(typeof(HUDManager))]
	internal class HUDManagerPatch
	{
		[HarmonyPatch("Awake")]
		[HarmonyPostfix]
		public static void AwakePostfix()
		{
			CruiserJumpPractice.Controller.HandleStartup();
		}

		[HarmonyPatch("Update")]
		[HarmonyPostfix]
		public static void UpdatePostfix()
		{
			CruiserJumpPractice.Controller.HandleFrame();
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class StartOfRoundPatch
	{
		[HarmonyPatch("SetMagnetOn", new Type[] { typeof(bool) })]
		[HarmonyPrefix]
		public static void SetMagnetOnPrefix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameShipMagnetLocalPreApply();
			});
		}

		[HarmonyPatch("SetMagnetOn", new Type[] { typeof(bool) })]
		[HarmonyPostfix]
		public static void SetMagnetOnPostfix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameShipMagnetLocalApplied();
			});
		}

		[HarmonyPatch("SetMagnetOnClientRpc", new Type[] { typeof(bool) })]
		[HarmonyPrefix]
		public static void SetMagnetOnClientRpcPrefix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameShipMagnetClientRpcPreApply();
			});
		}

		[HarmonyPatch("SetMagnetOnClientRpc", new Type[] { typeof(bool) })]
		[HarmonyPostfix]
		public static void SetMagnetOnClientRpcPostfix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameShipMagnetClientRpcApplied();
			});
		}

		private static void TryNotifyAppliedStateValidation(Action notify)
		{
			try
			{
				notify();
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(VehicleController))]
	internal static class VehicleControllerPatch
	{
		[HarmonyPatch("AddEngineOilClientRpc", new Type[]
		{
			typeof(int),
			typeof(int)
		})]
		[HarmonyPrefix]
		public static void AddEngineOilClientRpcPrefix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameEngineOilClientRpcEntered();
			});
		}

		[HarmonyPatch("AddEngineOilClientRpc", new Type[]
		{
			typeof(int),
			typeof(int)
		})]
		[HarmonyFinalizer]
		public static void AddEngineOilClientRpcFinalizer()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameEngineOilClientRpcExited();
			});
		}

		[HarmonyPatch("AddEngineOilOnLocalClient", new Type[] { typeof(int) })]
		[HarmonyPrefix]
		public static void AddEngineOilOnLocalClientPrefix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameEngineOilLocalPreApply();
			});
		}

		[HarmonyPatch("AddEngineOilOnLocalClient", new Type[] { typeof(int) })]
		[HarmonyPostfix]
		public static void AddEngineOilOnLocalClientPostfix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameEngineOilLocalApplied();
			});
		}

		[HarmonyPatch("AddTurboBoostClientRpc", new Type[]
		{
			typeof(int),
			typeof(int)
		})]
		[HarmonyPrefix]
		public static void AddTurboBoostClientRpcPrefix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameTurboClientRpcEntered();
			});
		}

		[HarmonyPatch("AddTurboBoostClientRpc", new Type[]
		{
			typeof(int),
			typeof(int)
		})]
		[HarmonyFinalizer]
		public static void AddTurboBoostClientRpcFinalizer()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameTurboClientRpcExited();
			});
		}

		[HarmonyPatch("AddTurboBoostOnLocalClient", new Type[] { typeof(int) })]
		[HarmonyPrefix]
		public static void AddTurboBoostOnLocalClientPrefix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameTurboLocalPreApply();
			});
		}

		[HarmonyPatch("AddTurboBoostOnLocalClient", new Type[] { typeof(int) })]
		[HarmonyPostfix]
		public static void AddTurboBoostOnLocalClientPostfix()
		{
			TryNotifyAppliedStateValidation(delegate
			{
				CruiserJumpPractice.Controller.HandleBaseGameTurboLocalApplied();
			});
		}

		private static void TryNotifyAppliedStateValidation(Action notify)
		{
			try
			{
				notify();
			}
			catch
			{
			}
		}
	}
}
namespace CruiserJumpPractice.Interop.Game.Behaviours
{
	internal class RpcSurrogateBehaviour : NetworkBehaviour
	{
		[ServerRpc(RequireOwnership = true)]
		public void SaveCruiserStateServerRpc()
		{
			CruiserJumpPractice.Controller.RecordSaveServerRpcReceived();
			SaveCruiserStateResult result = CruiserJumpPractice.Controller.SaveCruiserState();
			SaveCruiserStateDoneClientRpc(result);
		}

		[ClientRpc]
		public void SaveCruiserStateDoneClientRpc(SaveCruiserStateResult result)
		{
			CruiserJumpPractice.Controller.RecordSaveClientRpcReceived(result);
			CruiserJumpPractice.Controller.PresentSaveCruiserStateResult(result);
		}

		[ServerRpc(RequireOwnership = true)]
		public void LoadCruiserStateServerRpc()
		{
			CruiserJumpPractice.Controller.RecordLoadServerRpcReceived();
			LoadCruiserStateResult result = CruiserJumpPractice.Controller.LoadCruiserState();
			LoadCruiserStateDoneClientRpc(result);
		}

		[ClientRpc]
		public void LoadCruiserStateDoneClientRpc(LoadCruiserStateResult result)
		{
			CruiserJumpPractice.Controller.RecordLoadClientRpcReceived(result);
			CruiserJumpPractice.Controller.PresentLoadCruiserStateResult(result);
		}
	}
}
namespace CruiserJumpPractice.Interop.Game.Adapters
{
	internal sealed class CruiserAdapter
	{
		private static readonly FieldInfo? turboBoostsField = typeof(VehicleController).GetField("turboBoosts", BindingFlags.Instance | BindingFlags.NonPublic);

		private readonly IPluginLogger logger;

		private readonly GameObjectAdapter gameObjects;

		public CruiserAdapter(IPluginLogger logger, GameObjectAdapter gameObjects)
		{
			this.logger = logger;
			this.gameObjects = gameObjects;
		}

		public VehicleController? FindCruiser()
		{
			try
			{
				VehicleController[] array = Object.FindObjectsOfType<VehicleController>();
				if (array == null)
				{
					logger.LogError("Failed to find VehicleController objects.");
					return null;
				}
				if (array.Length == 0)
				{
					logger.LogInfo("No VehicleController objects found.");
					return null;
				}
				return array[0];
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting cruiser: {arg}");
				throw new GameInteropException($"Exception while getting cruiser: {arg}");
			}
		}

		public CruiserSnapshot CaptureCruiser(VehicleController cruiser)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return new CruiserSnapshot(FromUnityVector3(((Component)cruiser).transform.position), FromUnityVector3(((Component)cruiser).transform.eulerAngles), cruiser.moveInputVector.x, cruiser.EngineRPM, cruiser.carHP, GetTurboBoosts(cruiser));
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while capturing cruiser state: {arg}");
				throw new GameInteropException($"Exception while capturing cruiser state: {arg}");
			}
		}

		public CruiserRestoreObservation RestoreCruiser(VehicleController cruiser, CruiserSnapshot snapshot)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			int localPlayerId = gameObjects.GetLocalPlayerId();
			try
			{
				Vector3Value beforeCarPosition = FromUnityVector3(((Component)cruiser).transform.position);
				int carHP = cruiser.carHP;
				int turboBoosts = GetTurboBoosts(cruiser);
				((Component)cruiser).transform.position = ToUnityVector3(snapshot.CarPosition);
				((Component)cruiser).transform.eulerAngles = ToUnityVector3(snapshot.CarRotation);
				cruiser.moveInputVector.x = snapshot.SteeringInput;
				cruiser.EngineRPM = snapshot.EngineRPM;
				cruiser.AddEngineOilOnLocalClient(snapshot.CarHP);
				cruiser.AddEngineOilServerRpc(localPlayerId, snapshot.CarHP);
				cruiser.AddTurboBoostOnLocalClient(snapshot.TurboBoosts);
				cruiser.AddTurboBoostServerRpc(localPlayerId, snapshot.TurboBoosts);
				return new CruiserRestoreObservation(snapshot.CarPosition, snapshot.CarRotation, beforeCarPosition, FromUnityVector3(((Component)cruiser).transform.position), snapshot.CarHP, carHP, cruiser.carHP, snapshot.TurboBoosts, turboBoosts, GetTurboBoosts(cruiser));
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while restoring cruiser state: {arg}");
				throw new GameInteropException($"Exception while restoring cruiser state: {arg}");
			}
		}

		public bool IsCruiserMagnetedToShip(VehicleController cruiser)
		{
			try
			{
				return cruiser.magnetedToShip;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting 'magnetedToShip': {arg}");
				throw new GameInteropException($"Exception while getting 'magnetedToShip': {arg}");
			}
		}

		internal static int GetCarHP(VehicleController cruiser)
		{
			return cruiser.carHP;
		}

		internal static int GetTurboBoosts(VehicleController cruiser)
		{
			if (turboBoostsField == null)
			{
				throw new GameInteropException("Failed to get 'turboBoosts' field from VehicleController.");
			}
			object value = turboBoostsField.GetValue(cruiser);
			if (value is int)
			{
				return (int)value;
			}
			throw new GameInteropException("'turboBoosts' field is not of type int.");
		}

		private static Vector3Value FromUnityVector3(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3Value(value.x, value.y, value.z);
		}

		private static Vector3 ToUnityVector3(Vector3Value value)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(value.X, value.Y, value.Z);
		}
	}
	internal sealed class GameObjectAdapter
	{
		private readonly IPluginLogger logger;

		public GameObjectAdapter(IPluginLogger logger)
		{
			this.logger = logger;
		}

		public HUDManager GetHUDManager()
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					throw new GameInteropException("HUDManager.Instance is null.");
				}
				return instance;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting HUDManager: {arg}");
				throw new GameInteropException($"Exception while getting HUDManager: {arg}");
			}
		}

		public NetworkManager GetNetworkManager()
		{
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton == (Object)null)
				{
					throw new GameInteropException("NetworkManager.Singleton is null.");
				}
				return singleton;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting NetworkManager: {arg}");
				throw new GameInteropException($"Exception while getting NetworkManager: {arg}");
			}
		}

		public PlayerControllerB GetLocalPlayer()
		{
			try
			{
				GameNetworkManager instance = GameNetworkManager.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					throw new GameInteropException("GameNetworkManager.Instance is null.");
				}
				PlayerControllerB localPlayerController = instance.localPlayerController;
				if ((Object)(object)localPlayerController == (Object)null)
				{
					throw new GameInteropException("localPlayerController is null.");
				}
				return localPlayerController;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting local player: {arg}");
				throw new GameInteropException($"Exception while getting local player: {arg}");
			}
		}

		public int GetLocalPlayerId()
		{
			try
			{
				return (int)GetLocalPlayer().playerClientId;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting local player ID: {arg}");
				throw new GameInteropException($"Exception while getting local player ID: {arg}");
			}
		}

		public StartOfRound GetStartOfRound()
		{
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					throw new GameInteropException("StartOfRound.Instance is null.");
				}
				return instance;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting StartOfRound: {arg}");
				throw new GameInteropException($"Exception while getting StartOfRound: {arg}");
			}
		}
	}
	internal sealed class HudAdapter
	{
		private readonly IPluginLogger logger;

		private readonly GameObjectAdapter gameObjects;

		public HudAdapter(IPluginLogger logger, GameObjectAdapter gameObjects)
		{
			this.logger = logger;
			this.gameObjects = gameObjects;
		}

		public void DisplayTip(string headerText, string bodyText)
		{
			HUDManager hUDManager = gameObjects.GetHUDManager();
			try
			{
				hUDManager.DisplayTip(headerText, bodyText, false, false, "LC_Tip1");
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while displaying tip: {arg}");
				throw new GameInteropException($"Exception while displaying tip: {arg}");
			}
		}
	}
	internal sealed class NetworkAdapter
	{
		private readonly IPluginLogger logger;

		private readonly GameObjectAdapter gameObjects;

		public NetworkAdapter(IPluginLogger logger, GameObjectAdapter gameObjects)
		{
			this.logger = logger;
			this.gameObjects = gameObjects;
		}

		public bool IsHost()
		{
			try
			{
				return gameObjects.GetNetworkManager().IsHost;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting 'IsHost': {arg}");
				throw new GameInteropException($"Exception while getting 'IsHost': {arg}");
			}
		}
	}
	internal sealed class PlayerAdapter
	{
		private readonly IPluginLogger logger;

		private readonly GameObjectAdapter gameObjects;

		public PlayerAdapter(IPluginLogger logger, GameObjectAdapter gameObjects)
		{
			this.logger = logger;
			this.gameObjects = gameObjects;
		}

		public LocalPlayerBusyState GetLocalPlayerBusyState()
		{
			PlayerControllerB localPlayer = gameObjects.GetLocalPlayer();
			try
			{
				QuickMenuManager quickMenuManager = localPlayer.quickMenuManager;
				if ((Object)(object)quickMenuManager == (Object)null)
				{
					throw new GameInteropException("quickMenuManager is null.");
				}
				return new LocalPlayerBusyState(quickMenuManager.isMenuOpen, localPlayer.inTerminalMenu, localPlayer.isTypingChat);
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting local player status: {arg}");
				throw new GameInteropException($"Exception while getting local player status: {arg}");
			}
		}
	}
	internal sealed class RpcSurrogateAdapter
	{
		private readonly IPluginLogger logger;

		private readonly GameObjectAdapter gameObjects;

		private readonly IValidationLogger validationLogger;

		private RpcSurrogateBehaviour? cachedRpcSurrogateBehaviour;

		public RpcSurrogateAdapter(IPluginLogger logger, GameObjectAdapter gameObjects, IValidationLogger validationLogger)
		{
			this.logger = logger;
			this.gameObjects = gameObjects;
			this.validationLogger = validationLogger;
		}

		public RpcSurrogateSpawnResult SpawnRpcSurrogate()
		{
			try
			{
				GameObject gameObject = ((Component)gameObjects.GetHUDManager()).gameObject;
				if ((Object)(object)gameObject == (Object)null)
				{
					logger.LogError("HUDManager.gameObject is null.");
					return RpcSurrogateSpawnResult.Missing;
				}
				RpcSurrogateBehaviour component = gameObject.GetComponent<RpcSurrogateBehaviour>();
				if ((Object)(object)component != (Object)null)
				{
					cachedRpcSurrogateBehaviour = component;
					logger.LogDebug("RPC surrogate already exists on HUDManager.");
					return RpcSurrogateSpawnResult.Reused;
				}
				cachedRpcSurrogateBehaviour = gameObject.AddComponent<RpcSurrogateBehaviour>();
				logger.LogInfo("Spawned RPC surrogate on HUDManager.");
				return RpcSurrogateSpawnResult.Added;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while spawning RPC surrogate: {arg}");
				return RpcSurrogateSpawnResult.Error;
			}
		}

		public RpcSurrogateBehaviour GetRpcSurrogateBehaviour()
		{
			if ((Object)(object)cachedRpcSurrogateBehaviour != (Object)null)
			{
				RecordResolved(ValidationLogRpcSurrogateResolveSource.Cache, ValidationLogRpcSurrogateResolveResult.Success);
				return cachedRpcSurrogateBehaviour;
			}
			try
			{
				RpcSurrogateBehaviour component = ((Component)gameObjects.GetHUDManager()).GetComponent<RpcSurrogateBehaviour>();
				if ((Object)(object)component == (Object)null)
				{
					throw new GameInteropException("RpcSurrogateBehaviour component not found on HUDManager instance.");
				}
				cachedRpcSurrogateBehaviour = component;
				RecordResolved(ValidationLogRpcSurrogateResolveSource.Lookup, ValidationLogRpcSurrogateResolveResult.Success);
				return component;
			}
			catch (Exception arg)
			{
				RecordResolved(ValidationLogRpcSurrogateResolveSource.Lookup, ValidationLogRpcSurrogateResolveResult.Error);
				logger.LogError($"Exception while getting RpcSurrogateBehaviour: {arg}");
				throw new GameInteropException($"Exception while getting RpcSurrogateBehaviour: {arg}");
			}
		}

		private void RecordResolved(ValidationLogRpcSurrogateResolveSource source, ValidationLogRpcSurrogateResolveResult result)
		{
			validationLogger.Record(ValidationLogRecord.RpcSurrogateResolved(source, result));
		}
	}
	internal sealed class ShipMagnetAdapter
	{
		private readonly IPluginLogger logger;

		private readonly GameObjectAdapter gameObjects;

		public ShipMagnetAdapter(IPluginLogger logger, GameObjectAdapter gameObjects)
		{
			this.logger = logger;
			this.gameObjects = gameObjects;
		}

		public bool IsShipMagnetOn()
		{
			try
			{
				return gameObjects.GetStartOfRound().magnetOn;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while getting 'magnetOn': {arg}");
				throw new GameInteropException($"Exception while getting 'magnetOn': {arg}");
			}
		}

		public void ToggleShipMagnet()
		{
			try
			{
				AnimatedObjectTrigger magnetLever = gameObjects.GetStartOfRound().magnetLever;
				if ((Object)(object)magnetLever == (Object)null)
				{
					throw new GameInteropException("StartOfRound.magnetLever is null.");
				}
				magnetLever.TriggerAnimation(gameObjects.GetLocalPlayer());
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while toggling magnet: {arg}");
				throw new GameInteropException($"Exception while toggling magnet: {arg}");
			}
		}
	}
}
namespace CruiserJumpPractice.Core.Validation
{
	internal sealed class DisabledValidationLogger : IValidationLogger
	{
		public static DisabledValidationLogger Instance { get; } = new DisabledValidationLogger();


		private DisabledValidationLogger()
		{
		}

		public void Record(ValidationLogRecord record)
		{
		}
	}
	internal enum ValidationLogRole
	{
		Host,
		Client
	}
	internal enum ValidationLogInputAction
	{
		Save,
		Load,
		ToggleMagnet
	}
	internal enum ValidationLogRpcSurrogateResolveSource
	{
		Cache,
		Lookup
	}
	internal enum ValidationLogRpcSurrogateResolveResult
	{
		Success,
		Error
	}
	internal enum ValidationLogBaseGameApplySource
	{
		LocalApply,
		ClientRpcApply,
		Unknown
	}
	internal sealed class ValidationLogRecord
	{
		public string EventName { get; }

		public Dictionary<string, object?>? Fields { get; }

		private ValidationLogRecord(string eventName, Dictionary<string, object?>? fields = null)
		{
			EventName = eventName;
			Fields = fields;
		}

		public static ValidationLogRecord PluginLoaded(string version, bool validationLogging)
		{
			return new ValidationLogRecord("plugin_loaded", new Dictionary<string, object>
			{
				["version"] = version,
				["validation_logging"] = validationLogging
			});
		}

		public static ValidationLogRecord StateStoreCreated()
		{
			return new ValidationLogRecord("state_store_created");
		}

		public static ValidationLogRecord ControllerCreated()
		{
			return new ValidationLogRecord("controller_created");
		}

		public static ValidationLogRecord HudStartup(RpcSurrogateSpawnResult surrogateResult)
		{
			return new ValidationLogRecord("hud_startup", new Dictionary<string, object> { ["surrogate"] = ToSurrogateResultToken(surrogateResult) });
		}

		public static ValidationLogRecord InputTriggered(ValidationLogInputAction action, ValidationLogRole role)
		{
			return new ValidationLogRecord("input_triggered", new Dictionary<string, object>
			{
				["action"] = ToValidationActionToken(action),
				["role"] = ToValidationRoleToken(role),
				["busy"] = false
			});
		}

		public static ValidationLogRecord InputSuppressed(ValidationLogInputAction action, ValidationLogRole role, LocalPlayerBusyState busyState)
		{
			return new ValidationLogRecord("input_suppressed", new Dictionary<string, object>
			{
				["action"] = ToValidationActionToken(action),
				["role"] = ToValidationRoleToken(role),
				["reason"] = busyState.GetBusyReasonToken() ?? "unknown",
				["menu"] = busyState.IsMenuOpen,
				["terminal"] = busyState.IsInTerminal,
				["chat"] = busyState.IsTypingChat
			});
		}

		public static ValidationLogRecord RequestSaveResult(ValidationLogRole role, RequestSaveCruiserStateResult result)
		{
			return Result("request_save_result", role, ToValidationResultToken(result));
		}

		public static ValidationLogRecord RequestLoadResult(ValidationLogRole role, RequestLoadCruiserStateResult result)
		{
			return Result("request_load_result", role, ToValidationResultToken(result));
		}

		public static ValidationLogRecord ToggleMagnetResultEvent(ValidationLogRole role, ToggleMagnetResult result)
		{
			return Result("toggle_magnet_result", role, ToValidationResultToken(result));
		}

		public static ValidationLogRecord MagnetToggle(MagnetToggleObservation observation)
		{
			return new ValidationLogRecord("magnet_toggle", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(ValidationLogRole.Host),
				["before"] = ToValidationStateToken(observation.BeforeState),
				["expected_after"] = ToValidationStateToken(observation.ExpectedAfterState),
				["observed_after"] = ToValidationStateToken(observation.ObservedAfterState)
			});
		}

		public static ValidationLogRecord SaveServerRpcReceived(ValidationLogRole role)
		{
			return Role("save_server_rpc_received", role);
		}

		public static ValidationLogRecord SaveClientRpcReceived(ValidationLogRole role, SaveCruiserStateResult result)
		{
			return Result("save_client_rpc_received", role, ToValidationResultToken(result));
		}

		public static ValidationLogRecord LoadServerRpcReceived(ValidationLogRole role)
		{
			return Role("load_server_rpc_received", role);
		}

		public static ValidationLogRecord LoadClientRpcReceived(ValidationLogRole role, LoadCruiserStateResult result)
		{
			return Result("load_client_rpc_received", role, ToValidationResultToken(result));
		}

		public static ValidationLogRecord SaveNoCruiserFound()
		{
			return new ValidationLogRecord("save_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(ValidationLogRole.Host),
				["result"] = ToValidationResultToken(SaveCruiserStateResult.NoCruiserFound),
				["cruiser_found"] = false
			});
		}

		public static ValidationLogRecord SaveUnexpectedState()
		{
			return Result("save_result", ValidationLogRole.Host, "unexpected_state");
		}

		public static ValidationLogRecord SaveSuccess(CruiserSnapshot cruiserState)
		{
			return new ValidationLogRecord("save_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(ValidationLogRole.Host),
				["result"] = ToValidationResultToken(SaveCruiserStateResult.Success),
				["cruiser_found"] = true,
				["pos"] = Vector3(cruiserState.CarPosition, 1),
				["rot"] = Vector3(cruiserState.CarRotation, 1),
				["hp"] = cruiserState.CarHP,
				["turbo"] = cruiserState.TurboBoosts,
				["steering"] = Number(cruiserState.SteeringInput, 2),
				["rpm"] = Number(cruiserState.EngineRPM, 2)
			});
		}

		public static ValidationLogRecord LoadNoCruiserFound(bool savedState)
		{
			return LoadResult(ToValidationResultToken(LoadCruiserStateResult.NoCruiserFound), cruiserFound: false, savedState, "unknown");
		}

		public static ValidationLogRecord LoadNoSavedState()
		{
			return LoadResult(ToValidationResultToken(LoadCruiserStateResult.NoSavedState), cruiserFound: true, savedState: false, "unknown");
		}

		public static ValidationLogRecord LoadMagnetedToShip()
		{
			return LoadResult(ToValidationResultToken(LoadCruiserStateResult.MagnetedToShip), cruiserFound: true, savedState: true, true);
		}

		public static ValidationLogRecord LoadSuccess()
		{
			return LoadResult(ToValidationResultToken(LoadCruiserStateResult.Success), cruiserFound: true, savedState: true, false);
		}

		public static ValidationLogRecord LoadUnexpectedState()
		{
			return Result("load_result", ValidationLogRole.Host, "unexpected_state");
		}

		public static ValidationLogRecord RestoreApplied(CruiserRestoreObservation observation)
		{
			return new ValidationLogRecord("restore_applied", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(ValidationLogRole.Host),
				["saved_pos"] = Vector3(observation.SavedCarPosition, 1),
				["saved_rot"] = Vector3(observation.SavedCarRotation, 1),
				["before_pos"] = Vector3(observation.BeforeCarPosition, 1),
				["after_pos"] = Vector3(observation.AfterCarPosition, 1),
				["saved_hp"] = observation.SavedCarHP,
				["before_hp"] = observation.BeforeCarHP,
				["after_hp"] = observation.AfterCarHP,
				["saved_turbo"] = observation.SavedTurboBoosts,
				["before_turbo"] = observation.BeforeTurboBoosts,
				["after_turbo"] = observation.AfterTurboBoosts
			});
		}

		public static ValidationLogRecord BaseGameEngineOilApplied(ValidationLogRole role, int? beforeCarHP, int? afterCarHP, ValidationLogBaseGameApplySource source)
		{
			return new ValidationLogRecord("base_game_engine_oil_applied", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["before_hp"] = beforeCarHP,
				["after_hp"] = afterCarHP,
				["source"] = ToBaseGameApplySourceToken(source)
			});
		}

		public static ValidationLogRecord BaseGameTurboApplied(ValidationLogRole role, int? beforeTurbo, int? afterTurbo, ValidationLogBaseGameApplySource source)
		{
			return new ValidationLogRecord("base_game_turbo_applied", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["before_turbo"] = beforeTurbo,
				["after_turbo"] = afterTurbo,
				["source"] = ToBaseGameApplySourceToken(source)
			});
		}

		public static ValidationLogRecord BaseGameShipMagnetApplied(ValidationLogRole role, bool? before, bool after, ValidationLogBaseGameApplySource source)
		{
			return new ValidationLogRecord("base_game_ship_magnet_applied", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["before"] = before,
				["after"] = after,
				["source"] = ToBaseGameApplySourceToken(source)
			});
		}

		public static ValidationLogRecord HudTip(ValidationLogRole role, HudTipMessage message)
		{
			return new ValidationLogRecord("hud_tip", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["message"] = message.Token
			});
		}

		public static ValidationLogRecord RpcSurrogateResolved(ValidationLogRpcSurrogateResolveSource source, ValidationLogRpcSurrogateResolveResult result)
		{
			return new ValidationLogRecord("rpc_surrogate_resolved", new Dictionary<string, object>
			{
				["source"] = ToRpcSurrogateResolveSourceToken(source),
				["result"] = ToRpcSurrogateResolveResultToken(result)
			});
		}

		private static ValidationLogRecord LoadResult(string result, bool cruiserFound, bool savedState, object? magneted)
		{
			return new ValidationLogRecord("load_result", new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(ValidationLogRole.Host),
				["result"] = result,
				["cruiser_found"] = cruiserFound,
				["saved_state"] = savedState,
				["magneted"] = magneted
			});
		}

		private static ValidationLogRecord Result(string eventName, ValidationLogRole role, string result)
		{
			return new ValidationLogRecord(eventName, new Dictionary<string, object>
			{
				["role"] = ToValidationRoleToken(role),
				["result"] = result
			});
		}

		private static ValidationLogRecord Role(string eventName, ValidationLogRole role)
		{
			return new ValidationLogRecord(eventName, new Dictionary<string, object> { ["role"] = ToValidationRoleToken(role) });
		}

		private static object? Number(float value, int decimalPlaces)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return null;
			}
			return Math.Round(value, decimalPlaces, MidpointRounding.AwayFromZero);
		}

		private static object?[] Vector3(Vector3Value value, int decimalPlaces)
		{
			return new object[3]
			{
				Number(value.X, decimalPlaces),
				Number(value.Y, decimalPlaces),
				Number(value.Z, decimalPlaces)
			};
		}

		private static string ToSurrogateResultToken(RpcSurrogateSpawnResult result)
		{
			return result switch
			{
				RpcSurrogateSpawnResult.Added => "added", 
				RpcSurrogateSpawnResult.Reused => "reused", 
				RpcSurrogateSpawnResult.Missing => "missing", 
				RpcSurrogateSpawnResult.Error => "error", 
				_ => "error", 
			};
		}

		private static string ToValidationRoleToken(ValidationLogRole role)
		{
			return role switch
			{
				ValidationLogRole.Host => "host", 
				ValidationLogRole.Client => "client", 
				_ => "client", 
			};
		}

		private static string ToValidationActionToken(ValidationLogInputAction action)
		{
			return action switch
			{
				ValidationLogInputAction.Save => "save", 
				ValidationLogInputAction.Load => "load", 
				ValidationLogInputAction.ToggleMagnet => "toggle_magnet", 
				_ => "toggle_magnet", 
			};
		}

		private static string ToRpcSurrogateResolveSourceToken(ValidationLogRpcSurrogateResolveSource source)
		{
			return source switch
			{
				ValidationLogRpcSurrogateResolveSource.Cache => "cache", 
				ValidationLogRpcSurrogateResolveSource.Lookup => "lookup", 
				_ => "lookup", 
			};
		}

		private static string ToRpcSurrogateResolveResultToken(ValidationLogRpcSurrogateResolveResult result)
		{
			return result switch
			{
				ValidationLogRpcSurrogateResolveResult.Success => "success", 
				ValidationLogRpcSurrogateResolveResult.Error => "error", 
				_ => "error", 
			};
		}

		private static string ToBaseGameApplySourceToken(ValidationLogBaseGameApplySource source)
		{
			return source switch
			{
				ValidationLogBaseGameApplySource.LocalApply => "local_apply", 
				ValidationLogBaseGameApplySource.ClientRpcApply => "client_rpc_apply", 
				ValidationLogBaseGameApplySource.Unknown => "unknown", 
				_ => "unknown", 
			};
		}

		private static string ToValidationResultToken(SaveCruiserStateResult result)
		{
			return result switch
			{
				SaveCruiserStateResult.Success => "success", 
				SaveCruiserStateResult.NoCruiserFound => "no_cruiser_found", 
				SaveCruiserStateResult.UnexpectedState => "unexpected_state", 
				_ => "unexpected_state", 
			};
		}

		private static string ToValidationResultToken(LoadCruiserStateResult result)
		{
			return result switch
			{
				LoadCruiserStateResult.Success => "success", 
				LoadCruiserStateResult.NoCruiserFound => "no_cruiser_found", 
				LoadCruiserStateResult.NoSavedState => "no_saved_state", 
				LoadCruiserStateResult.MagnetedToShip => "magneted_to_ship", 
				LoadCruiserStateResult.UnexpectedState => "unexpected_state", 
				_ => "unexpected_state", 
			};
		}

		private static string ToValidationResultToken(RequestSaveCruiserStateResult result)
		{
			return result switch
			{
				RequestSaveCruiserStateResult.Success => "success", 
				RequestSaveCruiserStateResult.HostOnly => "host_only", 
				_ => "host_only", 
			};
		}

		private static string ToValidationResultToken(RequestLoadCruiserStateResult result)
		{
			return result switch
			{
				RequestLoadCruiserStateResult.Success => "success", 
				RequestLoadCruiserStateResult.HostOnly => "host_only", 
				_ => "host_only", 
			};
		}

		private static string ToValidationResultToken(ToggleMagnetResult result)
		{
			return result switch
			{
				ToggleMagnetResult.MagnetOn => "magnet_on", 
				ToggleMagnetResult.MagnetOff => "magnet_off", 
				ToggleMagnetResult.HostOnly => "host_only", 
				_ => "host_only", 
			};
		}

		private static string ToValidationStateToken(MagnetState state)
		{
			return state switch
			{
				MagnetState.On => "on", 
				MagnetState.Off => "off", 
				MagnetState.Unknown => "unknown", 
				_ => "unknown", 
			};
		}
	}
}
namespace CruiserJumpPractice.Core.UseCases
{
	internal enum RequestSaveCruiserStateResult
	{
		Success,
		HostOnly
	}
	internal enum RequestLoadCruiserStateResult
	{
		Success,
		HostOnly
	}
	internal enum SaveCruiserStateResult
	{
		Success,
		NoCruiserFound,
		UnexpectedState
	}
	internal enum LoadCruiserStateResult
	{
		Success,
		NoCruiserFound,
		NoSavedState,
		MagnetedToShip,
		UnexpectedState
	}
	internal enum ToggleMagnetResult
	{
		HostOnly,
		MagnetOn,
		MagnetOff
	}
}
namespace CruiserJumpPractice.Core.UseCases.Server
{
	internal sealed class LoadCruiserStateUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly CruiserStateStore cruiserStateStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public LoadCruiserStateUseCase(IGameInterop gameInterop, CruiserStateStore cruiserStateStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.cruiserStateStore = cruiserStateStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public LoadCruiserStateResult Execute()
		{
			try
			{
				if (!gameInterop.CruiserExists())
				{
					logger.LogInfo("No cruiser found.");
					validationLogger.Record(ValidationLogRecord.LoadNoCruiserFound(cruiserStateStore.SavedCruiserState != null));
					return LoadCruiserStateResult.NoCruiserFound;
				}
				CruiserSnapshot savedCruiserState = cruiserStateStore.SavedCruiserState;
				if (savedCruiserState == null)
				{
					logger.LogInfo("No saved cruiser state found.");
					validationLogger.Record(ValidationLogRecord.LoadNoSavedState());
					return LoadCruiserStateResult.NoSavedState;
				}
				if (gameInterop.IsCruiserMagnetedToShip())
				{
					logger.LogInfo("Cruiser is currently magneted to the ship. Cannot load state.");
					validationLogger.Record(ValidationLogRecord.LoadMagnetedToShip());
					return LoadCruiserStateResult.MagnetedToShip;
				}
				CruiserRestoreObservation observation = gameInterop.RestoreCruiser(savedCruiserState);
				RecordRestoreApplied(observation);
				validationLogger.Record(ValidationLogRecord.LoadSuccess());
				return LoadCruiserStateResult.Success;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while loading cruiser state: {arg}");
				validationLogger.Record(ValidationLogRecord.LoadUnexpectedState());
				return LoadCruiserStateResult.UnexpectedState;
			}
		}

		private void RecordRestoreApplied(CruiserRestoreObservation observation)
		{
			validationLogger.Record(ValidationLogRecord.RestoreApplied(observation));
		}
	}
	internal sealed class SaveCruiserStateUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly CruiserStateStore cruiserStateStore;

		private readonly IPluginLogger logger;

		private readonly IValidationLogger validationLogger;

		public SaveCruiserStateUseCase(IGameInterop gameInterop, CruiserStateStore cruiserStateStore, IPluginLogger logger, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.cruiserStateStore = cruiserStateStore;
			this.logger = logger;
			this.validationLogger = validationLogger;
		}

		public SaveCruiserStateResult Execute()
		{
			try
			{
				CruiserSnapshot cruiserSnapshot = gameInterop.CaptureCruiser();
				if (cruiserSnapshot == null)
				{
					logger.LogInfo("No cruiser found.");
					validationLogger.Record(ValidationLogRecord.SaveNoCruiserFound());
					return SaveCruiserStateResult.NoCruiserFound;
				}
				cruiserStateStore.SavedCruiserState = cruiserSnapshot;
				RecordSaveSuccess(cruiserSnapshot);
				return SaveCruiserStateResult.Success;
			}
			catch (Exception arg)
			{
				logger.LogError($"Exception while saving cruiser state: {arg}");
				validationLogger.Record(ValidationLogRecord.SaveUnexpectedState());
				return SaveCruiserStateResult.UnexpectedState;
			}
		}

		private void RecordSaveSuccess(CruiserSnapshot cruiserState)
		{
			validationLogger.Record(ValidationLogRecord.SaveSuccess(cruiserState));
		}
	}
}
namespace CruiserJumpPractice.Core.UseCases.Client
{
	internal enum MagnetState
	{
		Unknown,
		On,
		Off
	}
	internal sealed class MagnetToggleObservation
	{
		public MagnetState BeforeState { get; }

		public MagnetState ExpectedAfterState { get; }

		public MagnetState ObservedAfterState { get; }

		private MagnetToggleObservation(MagnetState beforeState, MagnetState expectedAfterState, MagnetState observedAfterState)
		{
			BeforeState = beforeState;
			ExpectedAfterState = expectedAfterState;
			ObservedAfterState = observedAfterState;
		}

		public static MagnetToggleObservation FromBeforeState(bool beforeIsOn)
		{
			int beforeState = (beforeIsOn ? 1 : 2);
			MagnetState expectedAfterState = ((!beforeIsOn) ? MagnetState.On : MagnetState.Off);
			return new MagnetToggleObservation((MagnetState)beforeState, expectedAfterState, MagnetState.Unknown);
		}
	}
	internal sealed class PresentLoadCruiserStateResultUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly IPluginLogger logger;

		public PresentLoadCruiserStateResultUseCase(IGameInterop gameInterop, IPluginLogger logger)
		{
			this.gameInterop = gameInterop;
			this.logger = logger;
		}

		public void Execute(LoadCruiserStateResult result)
		{
			switch (result)
			{
			case LoadCruiserStateResult.Success:
				DisplayTip(HudTipMessage.LoadSuccess);
				break;
			case LoadCruiserStateResult.NoCruiserFound:
				DisplayTip(HudTipMessage.LoadNoCruiser);
				break;
			case LoadCruiserStateResult.NoSavedState:
				DisplayTip(HudTipMessage.LoadNoSavedState);
				break;
			case LoadCruiserStateResult.MagnetedToShip:
				DisplayTip(HudTipMessage.LoadMagnetedToShip);
				break;
			default:
				logger.LogError($"Unknown LoadCruiserStateResult: {result}");
				break;
			}
		}

		private void DisplayTip(HudTipMessage message)
		{
			gameInterop.DisplayTip(message);
		}
	}
	internal sealed class PresentSaveCruiserStateResultUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly IPluginLogger logger;

		public PresentSaveCruiserStateResultUseCase(IGameInterop gameInterop, IPluginLogger logger)
		{
			this.gameInterop = gameInterop;
			this.logger = logger;
		}

		public void Execute(SaveCruiserStateResult result)
		{
			switch (result)
			{
			case SaveCruiserStateResult.Success:
				DisplayTip(HudTipMessage.SaveSuccess);
				break;
			case SaveCruiserStateResult.NoCruiserFound:
				DisplayTip(HudTipMessage.SaveNoCruiser);
				break;
			default:
				logger.LogError($"Unknown SaveCruiserStateResult: {result}");
				break;
			}
		}

		private void DisplayTip(HudTipMessage message)
		{
			gameInterop.DisplayTip(message);
		}
	}
	internal sealed class RequestLoadCruiserStateUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly IValidationLogger validationLogger;

		public RequestLoadCruiserStateUseCase(IGameInterop gameInterop, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.validationLogger = validationLogger;
		}

		public RequestLoadCruiserStateResult Execute()
		{
			if (!gameInterop.IsHost())
			{
				gameInterop.DisplayTip(HudTipMessage.LoadHostOnly);
				RecordResult(ValidationLogRole.Client, RequestLoadCruiserStateResult.HostOnly);
				return RequestLoadCruiserStateResult.HostOnly;
			}
			RecordResult(ValidationLogRole.Host, RequestLoadCruiserStateResult.Success);
			gameInterop.RequestLoadCruiserState();
			return RequestLoadCruiserStateResult.Success;
		}

		private void RecordResult(ValidationLogRole role, RequestLoadCruiserStateResult result)
		{
			validationLogger.Record(ValidationLogRecord.RequestLoadResult(role, result));
		}
	}
	internal sealed class RequestSaveCruiserStateUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly IValidationLogger validationLogger;

		public RequestSaveCruiserStateUseCase(IGameInterop gameInterop, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.validationLogger = validationLogger;
		}

		public RequestSaveCruiserStateResult Execute()
		{
			if (!gameInterop.IsHost())
			{
				gameInterop.DisplayTip(HudTipMessage.SaveHostOnly);
				RecordResult(ValidationLogRole.Client, RequestSaveCruiserStateResult.HostOnly);
				return RequestSaveCruiserStateResult.HostOnly;
			}
			RecordResult(ValidationLogRole.Host, RequestSaveCruiserStateResult.Success);
			gameInterop.RequestSaveCruiserState();
			return RequestSaveCruiserStateResult.Success;
		}

		private void RecordResult(ValidationLogRole role, RequestSaveCruiserStateResult result)
		{
			validationLogger.Record(ValidationLogRecord.RequestSaveResult(role, result));
		}
	}
	internal sealed class ToggleMagnetUseCase
	{
		private readonly IGameInterop gameInterop;

		private readonly IValidationLogger validationLogger;

		public ToggleMagnetUseCase(IGameInterop gameInterop, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.validationLogger = validationLogger;
		}

		public ToggleMagnetResult Execute()
		{
			if (!gameInterop.IsHost())
			{
				gameInterop.DisplayTip(HudTipMessage.MagnetHostOnly);
				RecordResult(ValidationLogRole.Client, ToggleMagnetResult.HostOnly);
				return ToggleMagnetResult.HostOnly;
			}
			MagnetToggleObservation magnetToggleObservation = MagnetToggleObservation.FromBeforeState(gameInterop.IsShipMagnetOn());
			gameInterop.ToggleShipMagnet();
			validationLogger.Record(ValidationLogRecord.MagnetToggle(magnetToggleObservation));
			ToggleMagnetResult toggleMagnetResult = ((magnetToggleObservation.ExpectedAfterState == MagnetState.On) ? ToggleMagnetResult.MagnetOn : ToggleMagnetResult.MagnetOff);
			validationLogger.Record(ValidationLogRecord.ToggleMagnetResultEvent(ValidationLogRole.Host, toggleMagnetResult));
			HudTipMessage message = ((toggleMagnetResult == ToggleMagnetResult.MagnetOn) ? HudTipMessage.MagnetOn : HudTipMessage.MagnetOff);
			gameInterop.DisplayTip(message);
			return toggleMagnetResult;
		}

		private void RecordResult(ValidationLogRole role, ToggleMagnetResult result)
		{
			validationLogger.Record(ValidationLogRecord.ToggleMagnetResultEvent(role, result));
		}
	}
}
namespace CruiserJumpPractice.Core.State
{
	internal sealed class BaseGameAppliedStateValidationStore
	{
		private int engineOilClientRpcDepth;

		private int turboClientRpcDepth;

		private int? preEngineOilLocalApplyCarHP;

		private int? preTurboLocalApplyBoosts;

		private bool? preMagnetLocalApplyState;

		private bool? preMagnetClientRpcApplyState;

		public bool IsEngineOilClientRpcApplyActive => engineOilClientRpcDepth > 0;

		public bool IsTurboClientRpcApplyActive => turboClientRpcDepth > 0;

		public int? PreEngineOilLocalApplyCarHP => preEngineOilLocalApplyCarHP;

		public int? PreTurboLocalApplyBoosts => preTurboLocalApplyBoosts;

		public bool? PreMagnetLocalApplyState => preMagnetLocalApplyState;

		public bool? PreMagnetClientRpcApplyState => preMagnetClientRpcApplyState;

		public void SetPreEngineOilLocalApplyCarHP(int? value)
		{
			preEngineOilLocalApplyCarHP = value;
		}

		public void SetPreTurboLocalApplyBoosts(int? value)
		{
			preTurboLocalApplyBoosts = value;
		}

		public void SetPreMagnetLocalApplyState(bool? value)
		{
			preMagnetLocalApplyState = value;
		}

		public void SetPreMagnetClientRpcApplyState(bool? value)
		{
			preMagnetClientRpcApplyState = value;
		}

		public void EnterEngineOilClientRpc()
		{
			engineOilClientRpcDepth++;
		}

		public void ExitEngineOilClientRpc()
		{
			if (engineOilClientRpcDepth > 0)
			{
				engineOilClientRpcDepth--;
			}
		}

		public void EnterTurboClientRpc()
		{
			turboClientRpcDepth++;
		}

		public void ExitTurboClientRpc()
		{
			if (turboClientRpcDepth > 0)
			{
				turboClientRpcDepth--;
			}
		}
	}
	internal sealed class CruiserStateStore
	{
		public CruiserSnapshot? SavedCruiserState { get; set; }
	}
	internal readonly struct LocalPlayerBusyState
	{
		public const string MenuReasonToken = "menu";

		public const string TerminalReasonToken = "terminal";

		public const string ChatReasonToken = "chat";

		public const string MultipleReasonToken = "multiple";

		public bool IsMenuOpen { get; }

		public bool IsInTerminal { get; }

		public bool IsTypingChat { get; }

		public bool IsBusy
		{
			get
			{
				if (!IsMenuOpen && !IsInTerminal)
				{
					return IsTypingChat;
				}
				return true;
			}
		}

		public LocalPlayerBusyState(bool isMenuOpen, bool isInTerminal, bool isTypingChat)
		{
			IsMenuOpen = isMenuOpen;
			IsInTerminal = isInTerminal;
			IsTypingChat = isTypingChat;
		}

		public string? GetBusyReasonToken()
		{
			if (0 + (IsMenuOpen ? 1 : 0) + (IsInTerminal ? 1 : 0) + (IsTypingChat ? 1 : 0) > 1)
			{
				return "multiple";
			}
			if (IsMenuOpen)
			{
				return "menu";
			}
			if (IsInTerminal)
			{
				return "terminal";
			}
			if (IsTypingChat)
			{
				return "chat";
			}
			return null;
		}
	}
}
namespace CruiserJumpPractice.Core.Snapshots
{
	internal sealed class CruiserRestoreObservation
	{
		public Vector3Value SavedCarPosition { get; }

		public Vector3Value SavedCarRotation { get; }

		public Vector3Value BeforeCarPosition { get; }

		public Vector3Value AfterCarPosition { get; }

		public int SavedCarHP { get; }

		public int BeforeCarHP { get; }

		public int AfterCarHP { get; }

		public int SavedTurboBoosts { get; }

		public int BeforeTurboBoosts { get; }

		public int AfterTurboBoosts { get; }

		public CruiserRestoreObservation(Vector3Value savedCarPosition, Vector3Value savedCarRotation, Vector3Value beforeCarPosition, Vector3Value afterCarPosition, int savedCarHP, int beforeCarHP, int afterCarHP, int savedTurboBoosts, int beforeTurboBoosts, int afterTurboBoosts)
		{
			SavedCarPosition = savedCarPosition;
			SavedCarRotation = savedCarRotation;
			BeforeCarPosition = beforeCarPosition;
			AfterCarPosition = afterCarPosition;
			SavedCarHP = savedCarHP;
			BeforeCarHP = beforeCarHP;
			AfterCarHP = afterCarHP;
			SavedTurboBoosts = savedTurboBoosts;
			BeforeTurboBoosts = beforeTurboBoosts;
			AfterTurboBoosts = afterTurboBoosts;
		}
	}
	internal sealed class CruiserSnapshot
	{
		public Vector3Value CarPosition { get; }

		public Vector3Value CarRotation { get; }

		public float SteeringInput { get; }

		public float EngineRPM { get; }

		public int CarHP { get; }

		public int TurboBoosts { get; }

		public CruiserSnapshot(Vector3Value carPosition, Vector3Value carRotation, float steeringInput, float engineRPM, int carHP, int turboBoosts)
		{
			CarPosition = carPosition;
			CarRotation = carRotation;
			SteeringInput = steeringInput;
			EngineRPM = engineRPM;
			CarHP = carHP;
			TurboBoosts = turboBoosts;
		}
	}
	internal readonly struct Vector3Value
	{
		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public Vector3Value(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}
	}
}
namespace CruiserJumpPractice.Core.Presentation
{
	internal sealed class HudTipMessage
	{
		private const string DefaultHeaderText = "CruiserJumpPractice";

		public static readonly HudTipMessage SaveSuccess = new HudTipMessage("save_success", "CruiserJumpPractice", "Cruiser state saved.");

		public static readonly HudTipMessage SaveNoCruiser = new HudTipMessage("save_no_cruiser", "CruiserJumpPractice", "No cruiser found to save.");

		public static readonly HudTipMessage SaveHostOnly = new HudTipMessage("save_host_only", "CruiserJumpPractice", "Only the host can save the cruiser state.");

		public static readonly HudTipMessage LoadSuccess = new HudTipMessage("load_success", "CruiserJumpPractice", "Cruiser state loaded.");

		public static readonly HudTipMessage LoadNoCruiser = new HudTipMessage("load_no_cruiser", "CruiserJumpPractice", "No cruiser found to load.");

		public static readonly HudTipMessage LoadNoSavedState = new HudTipMessage("load_no_saved_state", "CruiserJumpPractice", "No saved cruiser state to load.");

		public static readonly HudTipMessage LoadMagnetedToShip = new HudTipMessage("load_magneted_to_ship", "CruiserJumpPractice", "Cannot load cruiser state while magneted to ship.");

		public static readonly HudTipMessage LoadHostOnly = new HudTipMessage("load_host_only", "CruiserJumpPractice", "Only the host can load the cruiser state.");

		public static readonly HudTipMessage MagnetHostOnly = new HudTipMessage("magnet_host_only", "CruiserJumpPractice", "Only the host can toggle the magnet.");

		public static readonly HudTipMessage MagnetOn = new HudTipMessage("magnet_on", "CruiserJumpPractice", "Magnet is now ON.");

		public static readonly HudTipMessage MagnetOff = new HudTipMessage("magnet_off", "CruiserJumpPractice", "Magnet is now OFF.");

		public string Token { get; }

		public string HeaderText { get; }

		public string BodyText { get; }

		private HudTipMessage(string token, string headerText, string bodyText)
		{
			Token = token;
			HeaderText = headerText;
			BodyText = bodyText;
		}
	}
}
namespace CruiserJumpPractice.Core.Ports
{
	internal interface IGameInterop
	{
		bool IsHost();

		LocalPlayerBusyState GetLocalPlayerBusyState();

		void DisplayTip(HudTipMessage message);

		RpcSurrogateSpawnResult SpawnRpcSurrogate();

		void RequestSaveCruiserState();

		void RequestLoadCruiserState();

		bool CruiserExists();

		CruiserSnapshot? CaptureCruiser();

		int? GetCruiserCarHP();

		int? GetCruiserTurboBoosts();

		CruiserRestoreObservation RestoreCruiser(CruiserSnapshot snapshot);

		bool IsCruiserMagnetedToShip();

		bool IsShipMagnetOn();

		void ToggleShipMagnet();
	}
	internal enum RpcSurrogateSpawnResult
	{
		Added,
		Reused,
		Missing,
		Error
	}
	internal interface IPluginLogger
	{
		void LogDebug(string message);

		void LogInfo(string message);

		void LogError(string message);
	}
	internal interface IPracticeInput
	{
		bool SaveCruiserTriggered { get; }

		bool LoadCruiserTriggered { get; }

		bool ToggleMagnetTriggered { get; }
	}
	internal interface IValidationLogger
	{
		void Record(ValidationLogRecord record);
	}
}
namespace CruiserJumpPractice.Core.Handlers
{
	internal sealed class BaseGameAppliedStateValidationHandler
	{
		private readonly IGameInterop gameInterop;

		private readonly IValidationLogger validationLogger;

		private readonly BaseGameAppliedStateValidationStore stateStore;

		public BaseGameAppliedStateValidationHandler(IGameInterop gameInterop, IValidationLogger validationLogger, BaseGameAppliedStateValidationStore stateStore)
		{
			this.gameInterop = gameInterop;
			this.validationLogger = validationLogger;
			this.stateStore = stateStore;
		}

		public void EnterEngineOilClientRpc()
		{
			stateStore.EnterEngineOilClientRpc();
		}

		public void ExitEngineOilClientRpc()
		{
			stateStore.ExitEngineOilClientRpc();
		}

		public void HandleEngineOilLocalPreApply()
		{
			stateStore.SetPreEngineOilLocalApplyCarHP(gameInterop.GetCruiserCarHP());
		}

		public void HandleEngineOilLocalApplied()
		{
			if (stateStore.IsEngineOilClientRpcApplyActive)
			{
				validationLogger.Record(ValidationLogRecord.BaseGameEngineOilApplied(GetRole(), stateStore.PreEngineOilLocalApplyCarHP, gameInterop.GetCruiserCarHP(), ValidationLogBaseGameApplySource.ClientRpcApply));
			}
		}

		public void EnterTurboClientRpc()
		{
			stateStore.EnterTurboClientRpc();
		}

		public void ExitTurboClientRpc()
		{
			stateStore.ExitTurboClientRpc();
		}

		public void HandleTurboLocalPreApply()
		{
			stateStore.SetPreTurboLocalApplyBoosts(gameInterop.GetCruiserTurboBoosts());
		}

		public void HandleTurboLocalApplied()
		{
			if (stateStore.IsTurboClientRpcApplyActive)
			{
				validationLogger.Record(ValidationLogRecord.BaseGameTurboApplied(GetRole(), stateStore.PreTurboLocalApplyBoosts, gameInterop.GetCruiserTurboBoosts(), ValidationLogBaseGameApplySource.ClientRpcApply));
			}
		}

		public void HandleShipMagnetLocalPreApply()
		{
			stateStore.SetPreMagnetLocalApplyState(gameInterop.IsShipMagnetOn());
		}

		public void HandleShipMagnetLocalApplied()
		{
			HandleShipMagnetApplied(stateStore.PreMagnetLocalApplyState, gameInterop.IsShipMagnetOn(), ValidationLogBaseGameApplySource.LocalApply);
		}

		public void HandleShipMagnetClientRpcPreApply()
		{
			stateStore.SetPreMagnetClientRpcApplyState(gameInterop.IsShipMagnetOn());
		}

		public void HandleShipMagnetClientRpcApplied()
		{
			HandleShipMagnetApplied(stateStore.PreMagnetClientRpcApplyState, gameInterop.IsShipMagnetOn(), ValidationLogBaseGameApplySource.ClientRpcApply);
		}

		private void HandleShipMagnetApplied(bool? before, bool after, ValidationLogBaseGameApplySource source)
		{
			validationLogger.Record(ValidationLogRecord.BaseGameShipMagnetApplied(GetRole(), before, after, source));
		}

		private ValidationLogRole GetRole()
		{
			if (!gameInterop.IsHost())
			{
				return ValidationLogRole.Client;
			}
			return ValidationLogRole.Host;
		}
	}
	internal sealed class FrameHandler
	{
		private readonly IGameInterop gameInterop;

		private readonly IPracticeInput practiceInput;

		private readonly IValidationLogger validationLogger;

		private readonly RequestSaveCruiserStateUseCase requestSaveCruiserStateUseCase;

		private readonly RequestLoadCruiserStateUseCase requestLoadCruiserStateUseCase;

		private readonly ToggleMagnetUseCase toggleMagnetUseCase;

		public FrameHandler(IGameInterop gameInterop, IPracticeInput practiceInput, IValidationLogger validationLogger, RequestSaveCruiserStateUseCase requestSaveCruiserStateUseCase, RequestLoadCruiserStateUseCase requestLoadCruiserStateUseCase, ToggleMagnetUseCase toggleMagnetUseCase)
		{
			this.gameInterop = gameInterop;
			this.practiceInput = practiceInput;
			this.validationLogger = validationLogger;
			this.requestSaveCruiserStateUseCase = requestSaveCruiserStateUseCase;
			this.requestLoadCruiserStateUseCase = requestLoadCruiserStateUseCase;
			this.toggleMagnetUseCase = toggleMagnetUseCase;
		}

		public void HandleFrame()
		{
			bool saveCruiserTriggered = practiceInput.SaveCruiserTriggered;
			bool loadCruiserTriggered = practiceInput.LoadCruiserTriggered;
			bool toggleMagnetTriggered = practiceInput.ToggleMagnetTriggered;
			if (!saveCruiserTriggered && !loadCruiserTriggered && !toggleMagnetTriggered)
			{
				return;
			}
			LocalPlayerBusyState localPlayerBusyState = gameInterop.GetLocalPlayerBusyState();
			if (localPlayerBusyState.IsBusy)
			{
				RecordSuppressedInput(saveCruiserTriggered, loadCruiserTriggered, toggleMagnetTriggered, localPlayerBusyState);
				return;
			}
			if (saveCruiserTriggered)
			{
				RecordTriggeredInput(ValidationLogInputAction.Save);
				requestSaveCruiserStateUseCase.Execute();
			}
			if (loadCruiserTriggered)
			{
				RecordTriggeredInput(ValidationLogInputAction.Load);
				requestLoadCruiserStateUseCase.Execute();
			}
			if (toggleMagnetTriggered)
			{
				RecordTriggeredInput(ValidationLogInputAction.ToggleMagnet);
				toggleMagnetUseCase.Execute();
			}
		}

		private void RecordSuppressedInput(bool saveTriggered, bool loadTriggered, bool toggleMagnetTriggered, LocalPlayerBusyState busyState)
		{
			if (saveTriggered)
			{
				RecordSuppressedInput(ValidationLogInputAction.Save, busyState);
			}
			if (loadTriggered)
			{
				RecordSuppressedInput(ValidationLogInputAction.Load, busyState);
			}
			if (toggleMagnetTriggered)
			{
				RecordSuppressedInput(ValidationLogInputAction.ToggleMagnet, busyState);
			}
		}

		private void RecordTriggeredInput(ValidationLogInputAction action)
		{
			validationLogger.Record(ValidationLogRecord.InputTriggered(action, GetRole()));
		}

		private void RecordSuppressedInput(ValidationLogInputAction action, LocalPlayerBusyState busyState)
		{
			validationLogger.Record(ValidationLogRecord.InputSuppressed(action, GetRole(), busyState));
		}

		private ValidationLogRole GetRole()
		{
			if (!gameInterop.IsHost())
			{
				return ValidationLogRole.Client;
			}
			return ValidationLogRole.Host;
		}
	}
	internal sealed class StartupHandler
	{
		private readonly IGameInterop gameInterop;

		private readonly IValidationLogger validationLogger;

		public StartupHandler(IGameInterop gameInterop, IValidationLogger validationLogger)
		{
			this.gameInterop = gameInterop;
			this.validationLogger = validationLogger;
		}

		public void HandleStartup()
		{
			RpcSurrogateSpawnResult surrogateResult = gameInterop.SpawnRpcSurrogate();
			validationLogger.Record(ValidationLogRecord.HudStartup(surrogateResult));
		}
	}
}