using System;
using System.Diagnostics;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FreeGhost.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Mathematics;
using UnityEngine;
[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("FreeGhost")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+84ec76758957d76d30d805fef40e47fd2badda54")]
[assembly: AssemblyProduct("FreeGhost")]
[assembly: AssemblyTitle("FreeGhost")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace FreeGhost
{
internal sealed class FreeGhostController
{
private readonly ModConfig _config;
private readonly ManualLogSource _logger;
private PatchCapabilities _capabilities;
private Func<Vector3, Vector3>? _directionToLook;
private bool _sessionActive;
private bool _freeModeActive;
private bool _sprintToggleActive;
private bool _crouchToggleActive;
private bool _syncFailureLogged;
private bool _hasLastEncodedLook;
private Vector2 _lastEncodedLook;
private bool _hasLastValidOriginal;
private CharacterSyncData _lastValidOriginal;
private Vector3 _freeModeOrigin;
public Vector3 FreePosition { get; private set; }
public FreeGhostController(ModConfig config, ManualLogSource logger)
{
_config = config;
_logger = logger;
}
public void SetCapabilities(PatchCapabilities capabilities)
{
_capabilities = capabilities;
}
public bool TryBindDirectionConverter()
{
MethodInfo methodInfo = AccessTools.Method(typeof(HelperFunctions), "DirectionToLook", new Type[1] { typeof(Vector3) }, (Type[])null);
if (methodInfo == null || methodInfo.ReturnType != typeof(Vector3))
{
_logger.LogError((object)"HelperFunctions.DirectionToLook(Vector3) was not found for the PEAK 2.0.a baseline.");
return false;
}
try
{
_directionToLook = (Func<Vector3, Vector3>)Delegate.CreateDelegate(typeof(Func<Vector3, Vector3>), methodInfo);
return true;
}
catch (Exception arg)
{
_logger.LogError((object)$"Could not bind PEAK's direction-to-look converter: {arg}");
return false;
}
}
public void TickLifecycle()
{
if (_sessionActive)
{
Character localCharacter = Character.localCharacter;
if (!_config.Enabled.Value || !_capabilities.LocalMode || (Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.data == (Object)null || !localCharacter.data.dead || !localCharacter.data.fullyPassedOut || (Object)(object)localCharacter.Ghost == (Object)null)
{
Reset("local ghost lifecycle ended");
}
}
}
public void AfterVanillaSpectate(MainCameraMovement cameraMovement)
{
//IL_0084: 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)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
Character localCharacter = Character.localCharacter;
if (!CanStartGhostSession(localCharacter))
{
if (_sessionActive)
{
Reset("vanilla spectating ended");
}
return;
}
if (!_sessionActive)
{
if (!EnterFreeMode(((Component)cameraMovement).transform, "ghost session started"))
{
return;
}
_sessionActive = true;
}
GUIManager instance = GUIManager.instance;
bool flag = Time.timeScale > 0f && (Object)(object)instance != (Object)null && !instance.windowBlockingInput && !instance.wheelActive;
if (flag && Input.GetKeyDown(_config.ModeToggleShortcut.Value))
{
if (_freeModeActive)
{
ExitFreeMode(localCharacter, ((Component)cameraMovement).transform);
return;
}
if (!EnterFreeMode(((Component)cameraMovement).transform, "mode toggle"))
{
return;
}
}
if (_freeModeActive)
{
if (flag)
{
HandleToggleInputs(localCharacter.input);
ApplyMovement(((Component)cameraMovement).transform, localCharacter.input);
}
((Component)cameraMovement).transform.position = FreePosition;
ApplyGhostTransform(localCharacter.Ghost, ((Component)cameraMovement).transform.rotation);
}
}
public bool TryApplyLocalGhost(PlayerGhost ghost)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
if (!_freeModeActive || !_capabilities.LocalMode)
{
return false;
}
Character localCharacter = Character.localCharacter;
if ((Object)(object)localCharacter == (Object)null || (Object)(object)localCharacter.Ghost == (Object)null || (Object)(object)localCharacter.Ghost != (Object)(object)ghost)
{
return false;
}
ApplyGhostTransform(ghost, ((Component)ghost).transform.rotation);
return true;
}
public void TryRewriteSyncData(CharacterSyncer syncer, ref CharacterSyncData data)
{
//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: 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_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0133: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_0112: Unknown result type (might be due to invalid IL or missing references)
//IL_0185: Unknown result type (might be due to invalid IL or missing references)
//IL_018a: Unknown result type (might be due to invalid IL or missing references)
//IL_0164: Unknown result type (might be due to invalid IL or missing references)
if (!_freeModeActive || !_capabilities.VanillaSync || !_config.SyncToVanillaClients.Value)
{
return;
}
Character localCharacter = Character.localCharacter;
Character component = ((Component)syncer).GetComponent<Character>();
if ((Object)(object)localCharacter == (Object)null || (Object)(object)component == (Object)null || (Object)(object)component != (Object)(object)localCharacter || !localCharacter.data.dead || (Object)(object)localCharacter.Ghost == (Object)null)
{
return;
}
CharacterSyncData val = data;
if (IsValidOriginalSync(val))
{
_lastValidOriginal = val;
_hasLastValidOriginal = true;
}
Character target = localCharacter.Ghost.m_target;
if ((Object)(object)target == (Object)null || !IsFinite(FreePosition) || _directionToLook == null)
{
RestoreSafeOriginal(ref data, val);
return;
}
try
{
Vector3 targetCenter = ToCore(target.Center);
Vector3 desiredPosition = ToCore(FreePosition);
Vector2? previousLook = (_hasLastEncodedLook ? new Vector2?(_lastEncodedLook) : ((Vector2?)null));
if (!GhostPositionCodec.TrySolveDirection(targetCenter, desiredPosition, out var direction))
{
SyncFailedOnce("The desired ghost position could not be encoded safely.");
RestoreSafeOriginal(ref data, val);
return;
}
Vector3 val2 = _directionToLook(ToUnity(direction));
Vector2 seedLook = new Vector2(val2.x, val2.y);
if (!GhostPositionCodec.TryEncodeLook(targetCenter, desiredPosition, seedLook, previousLook, out var encoding))
{
SyncFailedOnce("PEAK's direction-to-look conversion produced an unsafe result.");
RestoreSafeOriginal(ref data, val);
return;
}
data.lookValues = new float2(encoding.LookValues.X, encoding.LookValues.Y);
data.spectateZoom = encoding.Zoom;
_lastEncodedLook = encoding.LookValues;
_hasLastEncodedLook = true;
_syncFailureLogged = false;
}
catch (Exception ex)
{
SyncFailedOnce("Ghost position encoding failed: " + ex.Message);
RestoreSafeOriginal(ref data, val);
}
}
public void Reset(string reason)
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
if (_sessionActive)
{
_logger.LogDebug((object)("Free ghost mode reset: " + reason + "."));
}
_sessionActive = false;
_freeModeActive = false;
_sprintToggleActive = false;
_crouchToggleActive = false;
_hasLastEncodedLook = false;
_hasLastValidOriginal = false;
_syncFailureLogged = false;
FreePosition = Vector3.zero;
_freeModeOrigin = Vector3.zero;
}
private bool CanStartGhostSession(Character? local)
{
if (_capabilities.LocalMode && _config.Enabled.Value && (Object)(object)local != (Object)null && (Object)(object)local.data != (Object)null && local.data.dead && local.data.fullyPassedOut)
{
return (Object)(object)local.Ghost != (Object)null;
}
return false;
}
private void HandleToggleInputs(CharacterInput input)
{
if (input.sprintToggleWasPressed)
{
_sprintToggleActive = !_sprintToggleActive;
}
if (input.crouchToggleWasPressed)
{
_crouchToggleActive = !_crouchToggleActive;
}
}
private void ApplyMovement(Transform cameraTransform, CharacterInput input)
{
//IL_0001: 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_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: 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_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: 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_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: Unknown result type (might be due to invalid IL or missing references)
//IL_0141: Unknown result type (might be due to invalid IL or missing references)
//IL_014b: Unknown result type (might be due to invalid IL or missing references)
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
Vector3 forward = cameraTransform.forward;
forward.y = 0f;
if (((Vector3)(ref forward)).sqrMagnitude < 1E-08f)
{
forward = Vector3.forward;
}
else
{
((Vector3)(ref forward)).Normalize();
}
Vector3 val = Vector3.Cross(Vector3.up, forward);
Vector3 normalized = ((Vector3)(ref val)).normalized;
Vector2 val2 = Vector2.ClampMagnitude(input.movementInput, 1f);
float num = (input.jumpIsPressed ? 1f : 0f) - ((input.crouchIsPressed || _crouchToggleActive) ? 1f : 0f);
Vector3 val3 = forward * val2.y + normalized * val2.x + Vector3.up * num;
val3 = Vector3.ClampMagnitude(val3, 1f);
Vector3 val4 = FreePosition;
if (((Vector3)(ref val3)).sqrMagnitude <= 1E-08f)
{
_sprintToggleActive = false;
}
else
{
bool flag = input.sprintIsPressed || _sprintToggleActive;
float num2 = _config.SafeBaseSpeed * (flag ? _config.SafeSprintMultiplier : 1f);
val4 += val3 * num2 * Time.deltaTime;
}
if (IsFinite(val4) && FreeFlightMath.TryClampToRadius(ToCore(_freeModeOrigin), ToCore(val4), _config.SafeMaxDistance, out var clampedPosition))
{
FreePosition = ToUnity(clampedPosition);
}
}
private bool EnterFreeMode(Transform cameraTransform, string reason)
{
//IL_0001: 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_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
Vector3 position = cameraTransform.position;
if (!IsFinite(position))
{
return false;
}
FreePosition = position;
_freeModeOrigin = position;
_freeModeActive = true;
_sprintToggleActive = false;
_crouchToggleActive = false;
_hasLastEncodedLook = false;
_syncFailureLogged = false;
_logger.LogDebug((object)("Free ghost mode activated: " + reason + "."));
return true;
}
private void ExitFreeMode(Character local, Transform cameraTransform)
{
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
_freeModeActive = false;
_sprintToggleActive = false;
_crouchToggleActive = false;
_hasLastEncodedLook = false;
_syncFailureLogged = false;
if ((Object)(object)local.Ghost != (Object)null)
{
((Component)local.Ghost).transform.position = cameraTransform.position;
}
_logger.LogDebug((object)"Free ghost mode disabled; vanilla spectating resumed.");
}
private void ApplyGhostTransform(PlayerGhost ghost, Quaternion rotation)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
if (IsFinite(FreePosition))
{
((Component)ghost).transform.SetPositionAndRotation(FreePosition, rotation);
}
}
private void RestoreSafeOriginal(ref CharacterSyncData data, CharacterSyncData original)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (!IsValidOriginalSync(original))
{
if (_hasLastValidOriginal)
{
data.lookValues = _lastValidOriginal.lookValues;
data.spectateZoom = _lastValidOriginal.spectateZoom;
}
else
{
data.lookValues = new float2(0f, 0f);
data.spectateZoom = 2f;
}
}
}
private void SyncFailedOnce(string message)
{
if (!_syncFailureLogged)
{
_syncFailureLogged = true;
_logger.LogWarning((object)(message + " Falling back to valid vanilla sync values."));
}
}
private static bool IsValidOriginalSync(CharacterSyncData data)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: 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_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
if (IsFinite(data.lookValues.x) && IsFinite(data.lookValues.y) && IsFinite(data.spectateZoom) && Math.Abs(data.lookValues.x) <= 65504f && Math.Abs(data.lookValues.y) <= 65504f)
{
return Math.Abs(data.spectateZoom) <= 65504f;
}
return false;
}
private static Vector3 ToCore(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 Vector3(value.x, value.y, value.z);
}
private static Vector3 ToUnity(Vector3 value)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
return new Vector3(value.X, value.Y, value.Z);
}
private static bool IsFinite(Vector3 value)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
if (IsFinite(value.x) && IsFinite(value.y))
{
return IsFinite(value.z);
}
return false;
}
private static bool IsFinite(float value)
{
if (!float.IsNaN(value))
{
return !float.IsInfinity(value);
}
return false;
}
}
internal sealed class ModConfig
{
private const float DefaultBaseSpeed = 8f;
private const float DefaultSprintMultiplier = 2.5f;
private const float DefaultMaxDistance = 1000f;
public ConfigEntry<bool> Enabled { get; }
public ConfigEntry<float> BaseSpeed { get; }
public ConfigEntry<float> SprintMultiplier { get; }
public ConfigEntry<float> MaxDistance { get; }
public ConfigEntry<KeyCode> ModeToggleShortcut { get; }
public ConfigEntry<bool> SyncToVanillaClients { get; }
public float SafeBaseSpeed => SafeRange(BaseSpeed.Value, 0.1f, 1000f, 8f);
public float SafeSprintMultiplier => SafeRange(SprintMultiplier.Value, 1f, 20f, 2.5f);
public float SafeMaxDistance => SafeRange(MaxDistance.Value, 10f, 10000f, 1000f);
public ModConfig(ConfigFile config)
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Expected O, but got Unknown
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Expected O, but got Unknown
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Expected O, but got Unknown
Enabled = config.Bind<bool>("General", "Enabled", true, "Enable first-person free ghost mode after the local scout dies.");
BaseSpeed = config.Bind<float>("Movement", "BaseSpeed", 8f, new ConfigDescription("Base free-flight speed in meters per second.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1000f), Array.Empty<object>()));
SprintMultiplier = config.Bind<float>("Movement", "SprintMultiplier", 2.5f, new ConfigDescription("Multiplier applied while the game's sprint input is active.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
MaxDistance = config.Bind<float>("Movement", "MaxDistance", 1000f, new ConfigDescription("Maximum free-flight distance in meters from the position where free mode was entered.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 10000f), Array.Empty<object>()));
ModeToggleShortcut = config.Bind<KeyCode>("Movement", "ModeToggleShortcut", (KeyCode)114, "Key used to toggle between free ghost movement and PEAK's vanilla spectate camera.");
SyncToVanillaClients = config.Bind<bool>("Networking", "SyncToVanillaClients", true, "Encode the free ghost position into vanilla lookValues and spectateZoom fields.");
}
private static float SafeRange(float value, float min, float max, float fallback)
{
if (float.IsNaN(value) || float.IsInfinity(value))
{
return fallback;
}
return Mathf.Clamp(value, min, max);
}
}
internal readonly struct PatchCapabilities
{
public bool LocalMode { get; }
public bool VanillaSync { get; }
public PatchCapabilities(bool localMode, bool vanillaSync)
{
LocalMode = localMode;
VanillaSync = vanillaSync;
}
}
internal static class PatchInstaller
{
public static PatchCapabilities Install(Harmony harmony, ManualLogSource logger, FreeGhostController controller)
{
bool num = TryPatch(harmony, AccessTools.Method(typeof(MainCameraMovement), "Spectate", (Type[])null, (Type[])null), null, AccessTools.Method(typeof(PatchCallbacks), "CameraSpectatePostfix", (Type[])null, (Type[])null), "MainCameraMovement.Spectate", logger);
bool flag = TryPatch(harmony, AccessTools.Method(typeof(PlayerGhost), "Update", (Type[])null, (Type[])null), AccessTools.Method(typeof(PatchCallbacks), "PlayerGhostUpdatePrefix", (Type[])null, (Type[])null), null, "PlayerGhost.Update", logger);
bool num2 = num && flag;
if (!num2)
{
logger.LogError((object)"Free ghost camera/model control is disabled because a required PEAK method was not found or patched.");
}
bool flag2 = controller.TryBindDirectionConverter() && TryPatch(harmony, AccessTools.Method(typeof(CharacterSyncer), "GetDataToWrite", (Type[])null, (Type[])null), null, AccessTools.Method(typeof(PatchCallbacks), "CharacterSyncPostfix", (Type[])null, (Type[])null), "CharacterSyncer.GetDataToWrite", logger);
if (!flag2)
{
logger.LogError((object)"Vanilla-client ghost position sync is disabled; local free ghost mode can still operate.");
}
return new PatchCapabilities(num2, flag2);
}
private static bool TryPatch(Harmony harmony, MethodInfo? original, MethodInfo? prefix = null, MethodInfo? postfix = null, string? description = null, ManualLogSource? logger = null)
{
//IL_0046: 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)
if (original == null || (prefix == null && postfix == null))
{
if (logger != null)
{
logger.LogError((object)("Required patch target is missing: " + description + "."));
}
return false;
}
try
{
harmony.Patch((MethodBase)original, (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
return true;
}
catch (Exception arg)
{
if (logger != null)
{
logger.LogError((object)$"Failed to patch {description}: {arg}");
}
return false;
}
}
}
internal static class PatchCallbacks
{
public static void CameraSpectatePostfix(MainCameraMovement __instance)
{
Plugin.Instance?.Controller.AfterVanillaSpectate(__instance);
}
public static bool PlayerGhostUpdatePrefix(PlayerGhost __instance)
{
FreeGhostController freeGhostController = Plugin.Instance?.Controller;
if (freeGhostController != null)
{
return !freeGhostController.TryApplyLocalGhost(__instance);
}
return true;
}
public static void CharacterSyncPostfix(CharacterSyncer __instance, ref CharacterSyncData __result)
{
Plugin.Instance?.Controller.TryRewriteSyncData(__instance, ref __result);
}
}
[BepInPlugin("com.github.lllei.FreeGhost", "FreeGhost", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
public const string PluginGuid = "com.github.lllei.FreeGhost";
public const string PluginName = "FreeGhost";
private Harmony? _harmony;
internal static Plugin? Instance { get; private set; }
internal ModConfig Settings { get; private set; }
internal FreeGhostController Controller { get; private set; }
private void Awake()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
Instance = this;
Settings = new ModConfig(((BaseUnityPlugin)this).Config);
Controller = new FreeGhostController(Settings, ((BaseUnityPlugin)this).Logger);
_harmony = new Harmony("com.github.lllei.FreeGhost");
PatchCapabilities capabilities = PatchInstaller.Install(_harmony, ((BaseUnityPlugin)this).Logger, Controller);
Controller.SetCapabilities(capabilities);
((BaseUnityPlugin)this).Logger.LogInfo((object)("FreeGhost 1.0.0 loaded for PEAK 2.0.a baseline. " + $"Local mode: {capabilities.LocalMode}; vanilla sync: {capabilities.VanillaSync}."));
}
private void Update()
{
Controller?.TickLifecycle();
}
private void OnDestroy()
{
Controller?.Reset("plugin destroyed");
Harmony? harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
Instance = null;
}
}
internal static class BuildInfo
{
internal const string Version = "1.0.0";
}
}
namespace FreeGhost.Core
{
public static class FreeFlightMath
{
public static bool TryClampToRadius(Vector3 origin, Vector3 desiredPosition, float maxDistance, out Vector3 clampedPosition)
{
clampedPosition = origin;
if (!GhostPositionCodec.IsFinite(origin) || !GhostPositionCodec.IsFinite(desiredPosition) || !GhostPositionCodec.IsFinite(maxDistance) || maxDistance < 0f)
{
return false;
}
Vector3 vector = desiredPosition - origin;
float num = vector.LengthSquared();
float num2 = maxDistance * maxDistance;
if (!GhostPositionCodec.IsFinite(num) || !GhostPositionCodec.IsFinite(num2))
{
return false;
}
if (num <= num2)
{
clampedPosition = desiredPosition;
return true;
}
float num3 = MathF.Sqrt(num);
clampedPosition = origin + vector * (maxDistance / num3);
return GhostPositionCodec.IsFinite(clampedPosition);
}
}
public readonly struct GhostEncoding
{
public Vector2 LookValues { get; }
public float Zoom { get; }
public Vector3 DecodedPosition { get; }
public float Error { get; }
public GhostEncoding(Vector2 lookValues, float zoom, Vector3 decodedPosition, float error)
{
LookValues = lookValues;
Zoom = zoom;
DecodedPosition = decodedPosition;
Error = error;
}
}
public static class GhostPositionCodec
{
public const float GhostUpOffset = 0.5f;
private const float Epsilon = 1E-08f;
private const float RadToDeg = 180f / MathF.PI;
private const float DegToRad = MathF.PI / 180f;
public static bool TrySolveDirection(Vector3 targetCenter, Vector3 desiredPosition, out Vector3 direction)
{
direction = Vector3.Zero;
if (!IsFinite(targetCenter) || !IsFinite(desiredPosition))
{
return false;
}
if (!TryNormalize(-(desiredPosition - targetCenter - Vector3.UnitY * 0.5f), out direction))
{
direction = Vector3.UnitZ;
}
return IsFinite(direction);
}
public static bool TryEncodeLook(Vector3 targetCenter, Vector3 desiredPosition, Vector2 seedLook, Vector2? previousLook, out GhostEncoding encoding)
{
encoding = default(GhostEncoding);
if (!IsFinite(targetCenter) || !IsFinite(desiredPosition) || !IsFinite(seedLook))
{
return false;
}
if (previousLook.HasValue)
{
seedLook.X = UnwrapYaw(seedLook.X, previousLook.Value.X);
}
seedLook.Y = MathF.Max(-90f, MathF.Min(90f, seedLook.Y));
return TryEvaluateQuantizedLook(targetCenter, desiredPosition, seedLook, out encoding);
}
public static Vector3 Decode(Vector3 targetCenter, Vector2 lookValues, float zoom)
{
Vector3 vector = DirectionFromLook(lookValues);
return targetCenter - vector * zoom + Vector3.UnitY * 0.5f;
}
public static Vector3 DirectionFromLook(Vector2 lookValues)
{
float x = lookValues.X * (MathF.PI / 180f);
float x2 = lookValues.Y * (MathF.PI / 180f);
float num = MathF.Cos(x2);
return Vector3.Normalize(new Vector3(MathF.Sin(x) * num, MathF.Sin(x2), MathF.Cos(x) * num));
}
public static Vector2 DirectionToLook(Vector3 direction)
{
if (!TryNormalize(direction, out var normalized))
{
return Vector2.Zero;
}
float x = MathF.Atan2(normalized.X, normalized.Z) * (180f / MathF.PI);
float y = MathF.Asin(MathF.Max(-1f, MathF.Min(1f, normalized.Y))) * (180f / MathF.PI);
return new Vector2(x, y);
}
public static float UnwrapYaw(float yaw, float reference)
{
if (!IsFinite(yaw) || !IsFinite(reference))
{
return yaw;
}
return yaw + 360f * MathF.Round((reference - yaw) / 360f);
}
public static bool IsFinite(float value)
{
if (!float.IsNaN(value))
{
return !float.IsInfinity(value);
}
return false;
}
public static bool IsFinite(Vector2 value)
{
if (IsFinite(value.X))
{
return IsFinite(value.Y);
}
return false;
}
public static bool IsFinite(Vector3 value)
{
if (IsFinite(value.X) && IsFinite(value.Y))
{
return IsFinite(value.Z);
}
return false;
}
private static bool TryEvaluateQuantizedLook(Vector3 targetCenter, Vector3 desiredPosition, Vector2 lookValues, out GhostEncoding encoding)
{
encoding = default(GhostEncoding);
Vector2 vector = new Vector2(HalfPrecision.Quantize(lookValues.X), HalfPrecision.Quantize(lookValues.Y));
if (!IsFinite(vector))
{
return false;
}
Vector3 vector2 = DirectionFromLook(vector);
Vector3 vector3 = desiredPosition - targetCenter - Vector3.UnitY * 0.5f;
float num = HalfPrecision.Quantize(MathF.Max(0f, 0f - Vector3.Dot(vector3, vector2)));
if (!IsFinite(vector2) || !IsFinite(num))
{
return false;
}
Vector3 vector4 = targetCenter - vector2 * num + Vector3.UnitY * 0.5f;
float num2 = Vector3.Distance(vector4, desiredPosition);
if (!IsFinite(vector4) || !IsFinite(num2))
{
return false;
}
encoding = new GhostEncoding(vector, num, vector4, num2);
return true;
}
private static bool TryNormalize(Vector3 value, out Vector3 normalized)
{
float num = value.LengthSquared();
if (!IsFinite(num) || num <= 1E-16f)
{
normalized = Vector3.Zero;
return false;
}
normalized = value / MathF.Sqrt(num);
return IsFinite(normalized);
}
}
public static class HalfPrecision
{
public const float MaxFinite = 65504f;
public static float Quantize(float value)
{
return ToSingle(ToHalfBits(value));
}
public static ushort ToHalfBits(float value)
{
int num = BitConverter.SingleToInt32Bits(value);
uint num2 = (uint)((num >>> 16) & 0x8000);
uint num3 = (uint)((num >>> 23) & 0xFF);
uint num4 = (uint)(num & 0x7FFFFF);
if (num3 == 255)
{
if (num4 == 0)
{
return (ushort)(num2 | 0x7C00);
}
uint num5 = num4 >> 13;
return (ushort)(num2 | 0x7C00 | num5 | ((num5 == 0) ? 1u : 0u));
}
int num6 = (int)(num3 - 127 + 15);
if (num6 >= 31)
{
return (ushort)(num2 | 0x7C00);
}
if (num6 <= 0)
{
if (num6 < -10)
{
return (ushort)num2;
}
num4 |= 0x800000;
int num7 = 14 - num6;
uint num8 = num4 >> num7;
uint num9 = num4 & (uint)((1 << num7) - 1);
uint num10 = (uint)(1 << num7 - 1);
if (num9 > num10 || (num9 == num10 && (num8 & 1) != 0))
{
num8++;
}
return (ushort)(num2 | num8);
}
uint num11 = num4 >> 13;
uint num12 = num4 & 0x1FFF;
if (num12 > 4096 || (num12 == 4096 && (num11 & 1) != 0))
{
num11++;
if (num11 == 1024)
{
num11 = 0u;
num6++;
if (num6 >= 31)
{
return (ushort)(num2 | 0x7C00);
}
}
}
return (ushort)(num2 | (uint)(num6 << 10) | num11);
}
public static float ToSingle(ushort value)
{
uint num = (uint)((value & 0x8000) << 16);
uint num2 = (uint)((value >> 10) & 0x1F);
uint num3 = (uint)(value & 0x3FF);
uint value2;
switch (num2)
{
case 0u:
{
if (num3 == 0)
{
value2 = num;
break;
}
int num4 = -14;
while ((num3 & 0x400) == 0)
{
num3 <<= 1;
num4--;
}
num3 &= 0x3FF;
value2 = num | (uint)(num4 + 127 << 23) | (num3 << 13);
break;
}
case 31u:
value2 = num | 0x7F800000 | (num3 << 13);
break;
default:
value2 = num | (num2 + 112 << 23) | (num3 << 13);
break;
}
return BitConverter.Int32BitsToSingle((int)value2);
}
}
}