Decompiled source of The All You Need Save Manager Continued v1.2.5
BepInEx/plugins/PeakSaveManagerContinued/PeakSaveManagerContinued.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Peak; using Photon.Pun; using Photon.Realtime; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using Zorro.Core; using Zorro.Core.Serizalization; using Zorro.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("PeakSaveManagerContinued")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.2.5.0")] [assembly: AssemblyInformationalVersion("1.2.5+899267627bec39bd5d3ffc227a62a65517dfbd2f")] [assembly: AssemblyProduct("The All You Need Save Manager Continued")] [assembly: AssemblyTitle("PeakSaveManagerContinued")] [assembly: AssemblyVersion("1.2.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 PEAKSaveManager { internal static class ItemUsesRestoration { internal static OptionableIntItemData Normalize(DataEntryValue value) { OptionableIntItemData val = (OptionableIntItemData)(object)((value is OptionableIntItemData) ? value : null); if (val != null) { return val; } IntItemData val2 = (IntItemData)(object)((value is IntItemData) ? value : null); if (val2 == null) { return null; } return FromLegacy(val2.Value); } internal static void ApplyLegacy(ItemInstanceData data, int? itemUses, bool restoredGenericItemUses) { if (!(data == null || restoredGenericItemUses) && itemUses.HasValue) { if (data.data == null) { data.data = new Dictionary<DataEntryKey, DataEntryValue>(); } data.data[(DataEntryKey)2] = (DataEntryValue)(object)FromLegacy(itemUses.Value); } } private static OptionableIntItemData FromLegacy(int value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0020: Expected O, but got Unknown return new OptionableIntItemData { HasData = (value >= 0), Value = Math.Max(-1, value) }; } } internal static class LandingSelection { internal const float HorizontalLimit = 8f; internal const float VerticalLimit = 8f; internal const float DistanceLimit = 10f; internal const float MinimumNormalY = 0.819152f; internal static bool NeedsSupport(CharacterSnapshot saved) { if (saved != null && saved.isGrounded && !saved.dead && !saved.passedOut && !saved.fullyPassedOut && !saved.isClimbing && !saved.isRopeClimbing) { return !saved.isVineClimbing; } return false; } internal static bool IsSupportSample(float belowTorso, float torsoHeight, float normalY, bool preserveOriginal) { if (normalY >= 0.819152f && belowTorso >= 0.65f) { return belowTorso <= torsoHeight + (preserveOriginal ? 0.15f : 0.3f); } return false; } internal static double DistanceSquared(Vector3Snapshot a, Vector3Snapshot b) { double num = (double)a.x - (double)b.x; double num2 = (double)a.y - (double)b.y; double num3 = (double)a.z - (double)b.z; return num * num + num2 * num2 + num3 * num3; } internal static Vector3Snapshot Closest(Vector3Snapshot original, IEnumerable<Vector3Snapshot> safeCandidates) { Vector3Snapshot result = null; double num = double.PositiveInfinity; foreach (Vector3Snapshot safeCandidate in safeCandidates) { if (safeCandidate != null) { double num2 = (double)safeCandidate.x - (double)original.x; double num3 = (double)safeCandidate.z - (double)original.z; double num4 = DistanceSquared(original, safeCandidate); if (!double.IsNaN(num4) && !double.IsInfinity(num4) && !(num2 * num2 + num3 * num3 > 64.0) && !(Math.Abs((double)safeCandidate.y - (double)original.y) > 8.0) && !(num4 > 100.0) && !(num4 >= num)) { result = safeCandidate; num = num4; } } } return result; } } [BepInPlugin("com.lucasandersen.peakallyouneedsavemanager", "The All You Need Save Manager Continued", "1.2.5")] public sealed class SaveManagerPlugin : BaseUnityPlugin, IOnEventCallback { private sealed class LuggageBatch { public string scene; public long revision; public List<LuggageRestoreState> states = new List<LuggageRestoreState>(); } private sealed class UnityLuggageRestoreTarget : ILuggageRestoreTarget { private readonly Luggage luggage; internal UnityLuggageRestoreTarget(Luggage luggage) { this.luggage = luggage; } public void CancelOpening() { ((MonoBehaviour)luggage).StopAllCoroutines(); } public void ResetSpawnHistory() { SpawnedItemTracker component = ((Component)luggage).GetComponent<SpawnedItemTracker>(); if (!((Object)(object)component == (Object)null)) { SpawnHistoryField.SetValue(component, null); (SpawnedItemsField.GetValue(component) as IList)?.Clear(); SetSpawnHistoryMethod.Invoke(component, new object[1] { false }); } } public void SetOpen(bool open) { LuggageStateField.SetValue(luggage, Enum.ToObject(LuggageStateField.FieldType, open ? 1 : 0)); } public void SetVisual(bool open) { Animator component = ((Component)luggage).GetComponent<Animator>(); if ((Object)(object)component == (Object)null || (Object)(object)component.runtimeAnimatorController == (Object)null) { throw new InvalidOperationException("A restored luggage has no animator."); } if (open && !component.HasState(0, Animator.StringToHash("Luggage_Open"))) { throw new InvalidOperationException("The game's luggage opening animation changed."); } bool fireEvents = component.fireEvents; component.fireEvents = false; try { component.Rebind(); if (open) { component.Play("Luggage_Open", 0, 1f); } component.Update(0f); } finally { component.fireEvents = fireEvents; } } public void SetAvailable(bool available) { while (Luggage.ALL_LUGGAGE.Remove(luggage)) { } if (available) { Luggage.ALL_LUGGAGE.Add(luggage); } luggage.HoverExit(); } public void SetActivatedObject(bool active) { if ((Object)(object)luggage.activateOnOpen != (Object)null) { luggage.activateOnOpen.SetActive(active); } } public void SetRevivalState(bool spent, bool revivedPlayers) { Luggage obj = luggage; RespawnChest val = (RespawnChest)(object)((obj is RespawnChest) ? obj : null); if (val != null) { SetRespawnChestState(val, spent, revivedPlayers); if ((Object)(object)val.skeleton != (Object)null) { val.skeleton.SetActive(!revivedPlayers); } } } } [HarmonyPatch(typeof(Luggage), "OnCharacterRegistered")] private static class RestoredLuggageJoinPatch { private static void Postfix(Luggage __instance, Character __0) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Instance == (Object)null || !PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient || !Instance.restoredLuggage.Contains(__instance) || !((Component)__instance).gameObject.activeInHierarchy) { return; } PhotonView photonView = GetPhotonView((Component)(object)__0); if (((photonView != null) ? photonView.Owner : null) == null || photonView.Owner.IsLocal) { return; } try { LuggageBatch luggageBatch = new LuggageBatch(); Scene activeScene = SceneManager.GetActiveScene(); luggageBatch.scene = ((Scene)(ref activeScene)).name; luggageBatch.revision = ++Instance.luggageRevision; LuggageBatch luggageBatch2 = luggageBatch; luggageBatch2.states.Add(CaptureLiveLuggage(__instance)); SendPlayerLoadEvent(photonView.OwnerActorNr, "luggage", Guid.NewGuid().ToString("N"), 0, PhotonNetwork.ServerTimestamp + 30000, JsonConvert.SerializeObject((object)luggageBatch2)); } catch (Exception ex) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)("Could not sync restored luggage to a joining player: " + ex.Message)); } } } private sealed class OwnerLoadReply { internal int actor; internal int viewId; internal bool completed; internal string error; } private sealed class UnityPlayerLoadTarget : IPlayerLoadTarget, IPlayerLandingTarget { private static readonly FieldInfo WarpRoutineField = AccessTools.Field(typeof(Character), "_warpRoutine"); private static readonly WarpStatusLock StatusLock = new WarpStatusLock(typeof(Character)); private static readonly MethodInfo SetWarpingMethod = AccessTools.PropertySetter(typeof(Character), "warping"); private readonly Character character; private readonly PlayerSnapshot snapshot; private readonly Func<bool> requestIsCurrent; private readonly PhotonView view; private readonly byte? selectedSlot; private readonly List<KeyValuePair<Collider, bool>> collisionBeforeWarp = new List<KeyValuePair<Collider, bool>>(); private Coroutine ownedWarp; private bool warpCompleted; private bool equipmentStarted; public bool IsAvailable { get { if ((Object)(object)character != (Object)null && character.IsLocal && (Object)(object)character.player != (Object)null && (Object)(object)character.data != (Object)null && (Object)(object)character.refs?.ragdoll != (Object)null && (Object)(object)character.refs.items != (Object)null && ((Object)(object)view == (Object)null || view.IsMine) && requestIsCurrent()) { if (ownedWarp != null) { return WarpRoutineField.GetValue(character) == ownedWarp; } return true; } return false; } } public float Realtime => Time.realtimeSinceStartup; public bool IsWarping { get { if ((Object)(object)character != (Object)null) { return character.warping; } return false; } } public bool WarpCompleted => warpCompleted; public bool IsAtDestination { get { //IL_0014: 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) if ((Object)(object)character != (Object)null) { return Vector3.Distance(character.Center, snapshot.position.ToUnity()) <= 0.75f; } return false; } } public bool RequiresStableLanding => LandingSelection.NeedsSupport(snapshot.character); public bool IsLandingStable { get { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (!IsAvailable || !character.data.isGrounded || !IsAtDestination) { return false; } foreach (Bodypart part in character.refs.ragdoll.partList) { if ((Object)(object)part != (Object)null && (Object)(object)part.Rig != (Object)null) { Vector3 linearVelocity = part.Rig.linearVelocity; if (((Vector3)(ref linearVelocity)).sqrMagnitude > 2.25f) { return false; } } } return true; } } public bool IsEquipmentReady { get { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (!IsAvailable) { return false; } Item currentItem = character.data.currentItem; if (!selectedSlot.HasValue) { return (Object)(object)currentItem == (Object)null; } if (equipmentStarted && !((Object)(object)currentItem == (Object)null) && !((Object)(object)currentItem.rig == (Object)null) && !((Object)(object)currentItem.holderCharacter != (Object)(object)character) && currentItem.itemID == snapshot.inventory.heldItemId) { Scene scene = ((Component)currentItem).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { if (HasItemJoint((BodypartType)10, currentItem.rig)) { if (!currentItem.rightHandOnly) { return HasItemJoint((BodypartType)7, currentItem.rig); } return true; } return false; } } return false; } } internal UnityPlayerLoadTarget(Character character, PlayerSnapshot snapshot, Func<bool> requestIsCurrent) { this.character = character; this.snapshot = snapshot; this.requestIsCurrent = requestIsCurrent; view = GetPhotonView((Component)(object)character); selectedSlot = PlayerLoadRestoration.ResolveEquippedSlot(snapshot.inventory, (character.player?.itemSlots?.Length).GetValueOrDefault(), delegate(byte slot) { Player player = character.player; if (player == null) { return (ushort?)null; } ItemSlot itemSlot = player.GetItemSlot(slot); return (itemSlot == null) ? ((ushort?)null) : itemSlot.prefab?.itemID; }); if (snapshot.inventory != null && snapshot.inventory.heldItemId != ushort.MaxValue && !selectedSlot.HasValue) { SaveManagerPlugin instance = Instance; if (instance != null) { ((BaseUnityPlugin)instance).Logger.LogWarning((object)$"Saved held item {snapshot.inventory.heldItemId} is absent from native equippable slots; restoring empty hands."); } } } private bool HasItemJoint(BodypartType part, Rigidbody itemRig) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) foreach (Bodypart part2 in character.refs.ragdoll.partList) { if ((Object)(object)part2 == (Object)null || part2.partType != part || (Object)(object)part2.Rig == (Object)null) { continue; } FixedJoint[] components = ((Component)part2.Rig).GetComponents<FixedJoint>(); for (int i = 0; i < components.Length; i++) { if ((Object)(object)((Joint)components[i]).connectedBody == (Object)(object)itemRig) { return true; } } } return false; } public void ClearHeldItem() { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) CharacterItems items = character.refs.items; Item currentItem = character.data.currentItem; PhotonView val = (((Object)(object)currentItem != (Object)null) ? GetPhotonView((Component)(object)currentItem) : null); if ((Object)(object)currentItem != (Object)null) { if (!((Object)(object)currentItem.rig == (Object)null)) { Scene scene = ((Component)currentItem).gameObject.scene; if (((Scene)(ref scene)).IsValid() && (!PhotonNetwork.InRoom || (!((Object)(object)val == (Object)null) && val.ViewID != 0))) { goto IL_0080; } } items.UnAttachEquippedItem(); } goto IL_0080; IL_0080: items.currentSelectedSlot = Optionable<byte>.None; PhotonView photonView = GetPhotonView((Component)(object)items); if ((Object)(object)photonView != (Object)null) { photonView.RPC("EquipSlotRpc", (RpcTarget)0, new object[2] { -1, -1 }); } else { items.EquipSlotRpc(-1, -1); } } public void ApplyLook() { //IL_0016: 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_0040: Unknown result type (might be due to invalid IL or missing references) ((Component)character).transform.eulerAngles = snapshot.rotation.ToUnity(); character.data.lookValues = snapshot.character.lookValues.ToUnity(); RecalculateLookDirectionsMethod.Invoke(character, null); } public void BeginWarp() { //IL_0099: 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_00bf: Expected O, but got Unknown if (WarpRoutineField == null || !StatusLock.IsSupported || SetWarpingMethod == null) { throw new InvalidOperationException("The game's teleport cleanup API changed."); } Collider[] componentsInChildren = ((Component)character.refs.ragdoll).GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren) { collisionBeforeWarp.Add(new KeyValuePair<Collider, bool>(val, val.enabled)); } character.WarpCompleted += OnWarpCompleted; character.WarpPlayerRPC(snapshot.position.ToUnity(), false); ownedWarp = (Coroutine)WarpRoutineField.GetValue(character); } private void OnWarpCompleted(Character source) { if ((Object)(object)source == (Object)(object)character && ownedWarp != null && WarpRoutineField.GetValue(character) == ownedWarp) { warpCompleted = true; } } public void RestoreLifeState() { CharacterSnapshot characterSnapshot = snapshot.character; character.data.dead = characterSnapshot.dead; character.data.passedOut = characterSnapshot.passedOut; character.data.fullyPassedOut = characterSnapshot.fullyPassedOut; character.data.deathTimer = (characterSnapshot.dead ? 1f : 0f); character.data.SetPetrify(PlayerRestoration.GetSavedPetrify(characterSnapshot.statuses)); character.refs.ragdoll.ToggleCollision(!characterSnapshot.dead); ApplyCharacterStatuses(character, characterSnapshot.statuses); character.SetExtraStamina(characterSnapshot.extraStamina); character.data.currentStamina = characterSnapshot.currentStamina; character.data.PushAll(); } public void HaltMotion() { //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_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_0090: 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) character.refs.ragdoll.HaltBodyVelocity(true); character.data.avarageVelocity = Vector3.zero; character.data.avarageLastFrameVelocity = Vector3.zero; character.data.fallSeconds = 0f; Item currentItem = character.data.currentItem; if ((Object)(object)currentItem != (Object)null && (Object)(object)currentItem.rig != (Object)null && !currentItem.rig.isKinematic) { currentItem.rig.linearVelocity = Vector3.zero; currentItem.rig.angularVelocity = Vector3.zero; } } public void EquipSavedSlot() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (selectedSlot.HasValue) { character.refs.items.EquipSlot(Optionable<byte>.Some(selectedSlot.Value)); character.refs.items.LockFromSwitching(5f); equipmentStarted = true; } } public void Finish(bool succeeded) { if ((Object)(object)character == (Object)null) { return; } character.WarpCompleted -= OnWarpCompleted; if (equipmentStarted && (Object)(object)character.refs?.items != (Object)null) { character.refs.items.lastEquippedSlotTime = Time.time; } if (succeeded || ownedWarp == null || !character.warping || WarpRoutineField.GetValue(character) != ownedWarp) { return; } ((MonoBehaviour)character).StopCoroutine(ownedWarp); SetWarpingMethod.Invoke(character, new object[1] { false }); StatusLock.Release(character); foreach (KeyValuePair<Collider, bool> item in collisionBeforeWarp) { if ((Object)(object)item.Key != (Object)null) { item.Key.enabled = item.Value; } } if ((Object)(object)character.refs?.ragdoll != (Object)null) { HaltMotion(); } } } private sealed class SavedMapLoader : ISavedMapLoader { private MapBundleData bundle; public float Realtime => Time.realtimeSinceStartup; public bool IsLoading { get { if (bundle != null) { return bundle.Loading; } return false; } } public bool CanLoad(string sceneName) { return Application.CanStreamedLevelBeLoaded(sceneName); } public bool BeginBundleLoad(int levelNumber) { MapBundleService service = GameHandler.GetService<MapBundleService>(); bundle = ((service != null) ? service.LoadBundlesNearestIndex(levelNumber, false) : null); return bundle != null; } } private sealed class ScenePreparationRequest { public string session; public string scene; public int seed; public int ascent; public int[] actors; } private sealed class SharedScenePreparation { internal ScenePreparationRequest Request; internal SceneLoadParty Party; internal Room Room; internal string SourceScene; internal int ExpiresAt; internal int PreviousAscent; internal bool Prepared; internal bool Armed; internal bool Busy; internal bool AddedSceneSwitchingStatus; internal Exception LoadError; } private readonly HashSet<Luggage> restoredLuggage = new HashSet<Luggage>(); private readonly HashSet<string> worldRestoreWarnings = new HashSet<string>(); private readonly LuggageRevisions receivedLuggageRevisions = new LuggageRevisions(); private long luggageRevision; private static readonly FieldInfo SpawnHistoryField = AccessTools.Field(typeof(SpawnedItemTracker), "_historyFromSave"); private static readonly FieldInfo SpawnedItemsField = AccessTools.Field(typeof(SpawnedItemTracker), "_spawnedItems"); private static readonly MethodInfo SetSpawnHistoryMethod = AccessTools.PropertySetter(typeof(SpawnedItemTracker), "HasSpawnHistory"); private readonly List<string> landingRestoreWarnings = new List<string>(); private static readonly FieldInfo ConditionalStandableField = AccessTools.Field(typeof(CollisionModifier), "hasStandableRange"); private const byte PlayerLoadEventCode = 173; private const int PlayerLoadProtocol = 6; private const int PlayerLoadRequestLifetimeMs = 30000; private readonly Dictionary<string, OwnerLoadReply> ownerLoadReplies = new Dictionary<string, OwnerLoadReply>(); private readonly HashSet<int> ownerLoadsInProgress = new HashSet<int>(); public const string PluginGuid = "com.lucasandersen.peakallyouneedsavemanager"; public const string PluginName = "The All You Need Save Manager Continued"; public const string PluginVersion = "1.2.5"; private const string SaveFilePattern = "*.json"; private const int UiWindowId = 129193; private const string PauseMenuButtonObjectName = "PeakSaveManagerButton"; private const float PendingSeedLifetimeSeconds = 90f; private static readonly MethodInfo RecalculateLookDirectionsMethod = AccessTools.Method(typeof(Character), "RecalculateLookDirections", (Type[])null, (Type[])null); private static readonly MethodInfo CampfireUpdateLitMethod = AccessTools.Method(typeof(Campfire), "UpdateLit", (Type[])null, (Type[])null); private static readonly MethodInfo CampfireHideLogsMethod = AccessTools.Method(typeof(Campfire), "HideLogs", (Type[])null, (Type[])null); private static readonly FieldInfo LuggageStateField = AccessTools.Field(typeof(Luggage), "state"); private static readonly MethodInfo MenuWindowOpenMethod = AccessTools.Method(typeof(MenuWindow), "Open", (Type[])null, (Type[])null); private static readonly MethodInfo MenuWindowCloseMethod = AccessTools.Method(typeof(MenuWindow), "Close", (Type[])null, (Type[])null); private static readonly MethodInfo RespawnChestSetSpentMethod = AccessTools.PropertySetter(typeof(RespawnChest), "IsSpent"); private static readonly MethodInfo RespawnChestSetRevivedPlayersMethod = AccessTools.PropertySetter(typeof(RespawnChest), "HasRevivedPlayers"); private static readonly MethodInfo RespawnChestGetRevivedPlayersMethod = AccessTools.PropertyGetter(typeof(RespawnChest), "HasRevivedPlayers"); private static readonly FieldInfo RespawnChestRevivedPlayersField = AccessTools.Field(typeof(RespawnChest), "hasRevivedPlayers"); private static readonly MethodInfo RunManagerRpcSyncTimeMethod = AccessTools.Method(typeof(RunManager), "RPC_SyncTime", new Type[2] { typeof(float), typeof(bool) }, (Type[])null); private static readonly FieldInfo RunManagerTimeSinceRunStartedField = AccessTools.Field(typeof(RunManager), "timeSinceRunStarted"); private static readonly FieldInfo RunManagerTimerActiveField = AccessTools.Field(typeof(RunManager), "timerActive"); private static readonly PropertyInfo RopeShooterAmmoProperty = AccessTools.Property(typeof(RopeShooter), "Ammo"); private static readonly MethodInfo RopeShooterSyncRpcMethod = AccessTools.Method(typeof(RopeShooter), "Sync_Rpc", new Type[1] { typeof(bool) }, (Type[])null); private static readonly FieldInfo ItemTotalUsesField = AccessTools.Field(typeof(Item), "totalUses"); private static readonly FieldInfo ItemDataField = AccessTools.Field(typeof(Item), "data"); private static readonly FieldInfo MagicBeanVineCurrentLengthField = AccessTools.Field(typeof(MagicBeanVine), "currentLength"); private static readonly FieldInfo MagicBeanVineInitialLengthField = AccessTools.Field(typeof(MagicBeanVine), "initialLength"); private static readonly FieldInfo MagicBeanVineMaxLengthField = AccessTools.Field(typeof(MagicBeanVine), "maxLength"); private static readonly FieldInfo CloudFungusAlreadyBrokeField = AccessTools.Field(typeof(CloudFungus), "alreadyBroke"); private static readonly FieldInfo CloudFungusTimeAliveField = AccessTools.Field(typeof(CloudFungus), "timeAlive"); private static readonly FieldInfo OptionableIntHasDataField = AccessTools.Field(typeof(OptionableIntItemData), "HasData"); private static readonly FieldInfo OptionableIntValueField = AccessTools.Field(typeof(OptionableIntItemData), "Value"); private static readonly FieldInfo OptionableBoolHasDataField = AccessTools.Field(typeof(OptionableBoolItemData), "HasData"); private static readonly FieldInfo OptionableBoolValueField = AccessTools.Field(typeof(OptionableBoolItemData), "Value"); private static readonly FieldInfo CharacterDataCheckpointFlagsField = AccessTools.Field(typeof(CharacterData), "checkpointFlags"); private static readonly FieldInfo CheckpointFlagStatusesField = AccessTools.Field(typeof(CheckpointFlag), "currentStatuses"); private static readonly FieldInfo CheckpointFlagPlanterField = AccessTools.Field(typeof(CheckpointFlag), "planterCharacter"); private static readonly FieldInfo CharacterItemsCurrentSelectedSlotField = AccessTools.Field(typeof(CharacterItems), "currentSelectedSlot"); private static readonly FieldInfo OptionableByteHasValueField = AccessTools.Field(typeof(Optionable<byte>), "hasValue"); private static readonly FieldInfo OptionableByteValueField = AccessTools.Field(typeof(Optionable<byte>), "value"); private static readonly MethodInfo MirageLuggageSetStateMethod = AccessTools.Method(typeof(MirageLuggage), "setMirageState", new Type[1] { typeof(float) }, (Type[])null); private static readonly FieldInfo MirageLuggageRenderersField = AccessTools.Field(typeof(MirageLuggage), "renderers"); private static readonly MethodInfo DayNightTimeStringGetterMethod = AccessTools.PropertyGetter(typeof(DayNightManager), "timeString"); private static readonly MethodInfo DayNightTimeStringSetterMethod = AccessTools.PropertySetter(typeof(DayNightManager), "timeString"); private static readonly FieldInfo DayNightTimeStringField = AccessTools.Field(typeof(DayNightManager), "timeString"); private static readonly HashSet<STATUSTYPE> SkippedSavedStatuses = new HashSet<STATUSTYPE> { (STATUSTYPE)7 }; private static SaveManagerPlugin Instance; private static int? PendingSeedForLoad; private static float PendingSeedSetRealtime; private Harmony harmony; private ConfigEntry<bool> autoSaveEnabled; private ConfigEntry<float> autoSaveIntervalSeconds; private string saveDirectory; private string preferredSaveDirectory; private string fallbackSaveDirectory; private bool showUi; private bool isLoading; private float lastAutoSaveTime; private bool lastSceneLoadSucceeded = true; private Rect windowRect = new Rect(34f, 34f, 820f, 620f); private Vector2 fileScroll = Vector2.zero; private string newSaveName = ""; private readonly List<SaveListEntry> saveEntries = new List<SaveListEntry>(); private string statusMessage = ""; private Color statusColor = Color.white; private float statusMessageUntil; private bool hasConfirmationPending; private string confirmationTitle = ""; private string confirmationMessage = ""; private Action confirmationAction; private bool stylesBuilt; private GUIStyle windowStyle; private GUIStyle sectionStyle; private GUIStyle titleStyle; private GUIStyle subtitleStyle; private GUIStyle normalLabelStyle; private GUIStyle errorLabelStyle; private GUIStyle softButtonStyle; private GUIStyle dangerButtonStyle; private GUIStyle textFieldStyle; private GUIStyle cardStyle; private GUIStyle cardWarningStyle; private Texture2D overlayTexture; private Texture2D windowTexture; private Texture2D sectionTexture; private Texture2D cardTexture; private Texture2D warningCardTexture; private Texture2D buttonTexture; private Texture2D buttonHoverTexture; private Texture2D dangerButtonTexture; private Texture2D textFieldTexture; private SaveManagerPausePage pauseMenuPage; private UIPageHandler pauseMenuPageHandler; private UIPage pauseMenuMainPage; private Button pauseMenuButtonTemplate; private bool quitPendingSaveDecision; private PauseMenuMainPage quitPendingPage; private bool allowVanillaQuitClick; private const float SceneReadyTimeoutSeconds = 75f; private const int ScenePreparationLifetimeMs = 300000; private SharedScenePreparation sharedScenePreparation; private SceneLoadBarrier sceneLoadBarrier; private IEnumerator SyncLuggageClientsRoutine(string kind, string payload) { if (!PhotonNetwork.InRoom) { yield break; } if (!PhotonNetwork.IsMasterClient) { throw new InvalidOperationException("Only the host can restore luggage."); } List<string> requests = new List<string>(); int expiresAt = PhotonNetwork.ServerTimestamp + 30000; float deadline = Time.realtimeSinceStartup + ((kind == "world-probe") ? 5f : 15f); try { Player[] playerListOthers = PhotonNetwork.PlayerListOthers; foreach (Player val in playerListOthers) { string text = Guid.NewGuid().ToString("N"); ownerLoadReplies.Add(text, new OwnerLoadReply { actor = val.ActorNumber, viewId = 0 }); requests.Add(text); SendPlayerLoadEvent(val.ActorNumber, kind, text, 0, expiresAt, payload); } while (true) { bool flag = true; foreach (string item in requests) { OwnerLoadReply ownerLoadReply = ownerLoadReplies[item]; if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom.GetPlayer(ownerLoadReply.actor, false) == null) { throw new InvalidOperationException("A player left or the host changed during luggage restoration."); } if (ownerLoadReply.completed && !string.IsNullOrEmpty(ownerLoadReply.error)) { throw new InvalidOperationException("A client could not restore luggage: " + ownerLoadReply.error); } flag &= ownerLoadReply.completed; } if (!flag) { if (Time.realtimeSinceStartup >= deadline) { throw new TimeoutException((kind == "world-probe") ? "Every player in the room needs this save-manager version for luggage synchronization." : "A client did not confirm luggage restoration."); } yield return null; continue; } break; } } finally { SaveManagerPlugin saveManagerPlugin = this; foreach (string item2 in requests) { saveManagerPlugin.ownerLoadReplies.Remove(item2); } } } private void HandleLuggageRequest(string kind, string payload, int actor, string request, int expiresAt) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) try { if (kind == "world-probe") { ValidateLuggageApi(); SendPlayerLoadEvent(actor, "reply", request, 0, expiresAt, ""); return; } LuggageBatch luggageBatch = JsonConvert.DeserializeObject<LuggageBatch>(payload); if (luggageBatch?.states != null && luggageBatch.states.Count <= 1024 && luggageBatch.revision > 0) { string scene = luggageBatch.scene; Scene activeScene = SceneManager.GetActiveScene(); if (!(scene != ((Scene)(ref activeScene)).name)) { HashSet<int> hashSet = new HashSet<int>(); foreach (LuggageRestoreState state in luggageBatch.states) { if (state == null || state.viewId <= 0 || !hashSet.Add(state.viewId) || !LuggageRestoration.IsFinite(state.position)) { throw new InvalidOperationException("Invalid or duplicate luggage identity."); } } receivedLuggageRevisions.Receive(actor, luggageBatch.scene, luggageBatch.revision, luggageBatch.states); ((MonoBehaviour)this).StartCoroutine(ReceiveLuggageRoutine(luggageBatch, actor, request, expiresAt)); return; } } throw new InvalidOperationException("Luggage batch belongs to a different or unavailable scene."); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Luggage synchronization failed: " + ex.Message)); SendPlayerLoadEvent(actor, "reply", request, 0, expiresAt, ex.Message); } } private IEnumerator ReceiveLuggageRoutine(LuggageBatch batch, int actor, string request, int expiresAt) { float deadline = Time.realtimeSinceStartup + 10f; string error = null; List<Luggage> targets = new List<Luggage>(); while (true) { try { if (!PhotonNetwork.InRoom) { goto IL_0098; } Player masterClient = PhotonNetwork.MasterClient; if (masterClient == null || masterClient.ActorNumber != actor || expiresAt - PhotonNetwork.ServerTimestamp <= 0) { goto IL_0098; } string scene = batch.scene; Scene activeScene = SceneManager.GetActiveScene(); if (scene != ((Scene)(ref activeScene)).name) { goto IL_0098; } targets.Clear(); foreach (LuggageRestoreState state in batch.states) { if (!receivedLuggageRevisions.IsCurrent(actor, batch.scene, batch.revision, state.viewId)) { targets.Add(null); continue; } PhotonView val = PhotonView.Find(state.viewId); Luggage val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Luggage>() : null); if ((Object)(object)val2 == (Object)null) { break; } if (string.Equals(state.type, ((object)val2).GetType().FullName, StringComparison.Ordinal) && string.Equals(state.name, NormalizeObjectName(((Object)val2).name), StringComparison.Ordinal)) { Vector3 val3 = ((Component)val2).transform.position - state.position.ToUnity(); if (!(((Vector3)(ref val3)).sqrMagnitude > 400f)) { targets.Add(val2); continue; } } throw new InvalidOperationException("A luggage network identity does not match the host."); } if (targets.Count == batch.states.Count) { ValidateLuggageApi(); for (int i = 0; i < targets.Count; i++) { if ((Object)(object)targets[i] != (Object)null) { ApplyRestoredLuggage(targets[i], batch.states[i]); } } break; } if (Time.realtimeSinceStartup >= deadline) { throw new TimeoutException("Luggage network objects did not become ready."); } goto end_IL_0045; IL_0098: throw new InvalidOperationException("Luggage restoration expired or the host/scene changed."); end_IL_0045:; } catch (Exception ex) { error = ex.Message; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Luggage synchronization failed: " + error)); break; } yield return null; } if (PhotonNetwork.InRoom) { SendPlayerLoadEvent(actor, "reply", request, 0, expiresAt, error ?? ""); } } private IEnumerator RestoreLuggageRoutine(List<ContainerSnapshot> containers, List<LuggageSnapshot> legacy, float readyDeadline) { List<LuggageRestoreState> snapshots = new List<LuggageRestoreState>(); if (containers != null && containers.Count > 0) { foreach (ContainerSnapshot container in containers) { if (container != null && IsLuggageContainerSnapshot(container)) { if (container.state < 0 || container.state > 1) { throw new InvalidOperationException("Invalid saved luggage state."); } snapshots.Add(new LuggageRestoreState { type = container.containerType, name = NormalizeObjectName(container.objectName), path = container.objectPath, position = container.position, open = (container.state == 1), activated = (container.activatedObjectActive ?? (container.state == 1)), spent = container.boolA, revivedPlayers = container.boolB }); } } } else if (legacy != null) { foreach (LuggageSnapshot item in legacy) { if (item != null) { if (item.state < 0 || item.state > 1) { throw new InvalidOperationException("Invalid legacy luggage state."); } snapshots.Add(new LuggageRestoreState { type = (item.isRespawnChest ? typeof(RespawnChest).FullName : null), name = NormalizeObjectName(item.objectName), path = item.objectPath, position = item.position, open = (item.state == 1), activated = (item.activatedObjectActive ?? (item.state == 1)), spent = item.respawnChestSpent, revivedPlayers = item.respawnChestRevivedPlayers }); } } } if (snapshots.Count == 0) { yield break; } if (snapshots.Count > 1024) { throw new InvalidOperationException("Too many saved luggage objects."); } ValidateLuggageApi(); LuggageBatch luggageBatch = new LuggageBatch(); Scene activeScene = SceneManager.GetActiveScene(); luggageBatch.scene = ((Scene)(ref activeScene)).name; luggageBatch.revision = ++luggageRevision; LuggageBatch batch = luggageBatch; List<Luggage> targets = new List<Luggage>(); List<Luggage> list; List<LuggageRestoreState> list2; int[] array; while (true) { string scene = batch.scene; activeScene = SceneManager.GetActiveScene(); if (scene != ((Scene)(ref activeScene)).name || (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient)) { throw new InvalidOperationException("The host or scene changed during luggage matching."); } list = (from x in Object.FindObjectsByType<Luggage>((FindObjectsSortMode)0) where (Object)(object)x != (Object)null select x).ToList(); list2 = list.Select(CaptureLiveLuggage).ToList(); array = LuggageRestoration.FindMatches(snapshots, list2, PhotonNetwork.InRoom); if (!array.Any((int index) => index < 0) || !(Time.realtimeSinceStartup < readyDeadline)) { break; } yield return null; } targets.Clear(); batch.states.Clear(); for (int num = 0; num < snapshots.Count; num++) { LuggageRestoreState luggageRestoreState = snapshots[num]; int num2 = array[num]; if (num2 < 0) { string text = "Skipped saved luggage '" + (luggageRestoreState.name ?? "unnamed") + "' at " + $"({luggageRestoreState.position?.x}, {luggageRestoreState.position?.y}, {luggageRestoreState.position?.z}): " + "no unique nearby network object. The map may have changed; player restoration will continue."; if (worldRestoreWarnings.Add(text)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)text); } } else { LuggageRestoreState luggageRestoreState2 = list2[num2]; luggageRestoreState2.open = luggageRestoreState.open; luggageRestoreState2.activated = luggageRestoreState.activated; luggageRestoreState2.spent = luggageRestoreState.spent; luggageRestoreState2.revivedPlayers = luggageRestoreState.revivedPlayers; targets.Add(list[num2]); batch.states.Add(luggageRestoreState2); } } if (targets.Count == 0) { yield break; } restoredLuggage.RemoveWhere((Luggage x) => (Object)(object)x == (Object)null); for (int num3 = 0; num3 < targets.Count; num3++) { ApplyRestoredLuggage(targets[num3], batch.states[num3]); } IEnumerator sync = SyncLuggageClientsRoutine("luggage", JsonConvert.SerializeObject((object)batch)); using (sync as IDisposable) { while (sync.MoveNext()) { yield return sync.Current; } } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Restored {targets.Count} luggage states and confirmed all current clients."); } private static LuggageRestoreState CaptureLiveLuggage(Luggage luggage) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) PhotonView photonView = GetPhotonView((Component)(object)luggage); RespawnChest val = (RespawnChest)(object)((luggage is RespawnChest) ? luggage : null); return new LuggageRestoreState { viewId = (((Object)(object)photonView != (Object)null) ? photonView.ViewID : 0), type = ((object)luggage).GetType().FullName, name = NormalizeObjectName(((Object)luggage).name), path = BuildTransformPath(((Component)luggage).transform), position = new Vector3Snapshot(((Component)luggage).transform.position), open = (GetLuggageState(luggage) == 1), activated = ((Object)(object)luggage.activateOnOpen != (Object)null && luggage.activateOnOpen.activeSelf), spent = ((Object)(object)val != (Object)null && val.IsSpent), revivedPlayers = GetRespawnChestHasRevivedPlayers(val) }; } private void ApplyRestoredLuggage(Luggage luggage, LuggageRestoreState state) { LuggageRestoration.Apply(new UnityLuggageRestoreTarget(luggage), state); restoredLuggage.Add(luggage); } private static void ValidateLuggageApi() { if (LuggageStateField == null || !LuggageStateField.FieldType.IsEnum || SpawnHistoryField == null || SpawnedItemsField == null || SetSpawnHistoryMethod == null) { throw new InvalidOperationException("The game's luggage restoration API changed."); } } private PlayerSnapshot ResolvePlayerLanding(Character character, PlayerSnapshot snapshot) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_0115: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) if (!LandingSelection.NeedsSupport(snapshot?.character)) { return snapshot; } if (snapshot.position == null || double.IsNaN(LandingSelection.DistanceSquared(snapshot.position, snapshot.position))) { throw new InvalidOperationException("Saved landing position is missing or nonfinite."); } if (ConditionalStandableField == null) { throw new InvalidOperationException("Native terrain standability API changed."); } Vector3 val = snapshot.position.ToUnity(); int num = LayerMask.NameToLayer("Map"); if (num < 0) { throw new InvalidOperationException("Cannot check saved landing: Map collision layer is missing."); } Physics.SyncTransforms(); int num2 = 1 << num; float num3 = 0.3f; foreach (Bodypart part in character.refs.ragdoll.partList) { if ((Object)(object)part != (Object)null && (int)part.partType == 0) { num3 = Vector3.Distance(character.Center, part.transform.position); } } float num4 = Mathf.Clamp(character.data.targetHipHeight + num3, 0.9f, 1.4f); if (HasLandingSupport(character, val, num4, num2, preserveOriginal: true)) { return snapshot; } List<Vector3Snapshot> list = new List<Vector3Snapshot>(); for (float num5 = -8f; num5 <= 8f; num5 += 0.5f) { for (float num6 = -8f; num6 <= 8f; num6 += 0.5f) { if (num5 * num5 + num6 * num6 > 64f) { continue; } RaycastHit[] array = Physics.RaycastAll(val + new Vector3(num5, 10f, num6), Vector3.down, 20f, num2, (QueryTriggerInteraction)1); for (int i = 0; i < array.Length; i++) { RaycastHit hit = array[i]; if (IsSafeLandingSurface(character, hit)) { Vector3 val2 = ((RaycastHit)(ref hit)).point + Vector3.up * num4; Vector3Snapshot vector3Snapshot = new Vector3Snapshot(val2); if (LandingSelection.Closest(snapshot.position, new Vector3Snapshot[1] { vector3Snapshot }) != null && HasLandingSupport(character, val2, num4, num2, preserveOriginal: false)) { list.Add(vector3Snapshot); } } } } } Vector3Snapshot vector3Snapshot2 = LandingSelection.Closest(snapshot.position, list); if (vector3Snapshot2 == null) { throw new InvalidOperationException("No stable landing within 8m horizontally / 8m vertically (10m total) of '" + snapshot.playerName + "'. Saved position is unsafe; player teleport cancelled."); } PlayerSnapshot playerSnapshot = JsonConvert.DeserializeObject<PlayerSnapshot>(JsonConvert.SerializeObject((object)snapshot)); playerSnapshot.position = vector3Snapshot2; float num7 = Vector3.Distance(val, vector3Snapshot2.ToUnity()); string text = $"{snapshot.playerName}: stable landing offset {num7:F1}m"; landingRestoreWarnings.Add(text); ManualLogSource logger = ((BaseUnityPlugin)this).Logger; string[] obj = new string[6] { text, "; saved=", ((Vector3)(ref val)).ToString("F3"), ", landing=", null, null }; Vector3 val3 = vector3Snapshot2.ToUnity(); obj[4] = ((Vector3)(ref val3)).ToString("F3"); obj[5] = ". Save file unchanged."; logger.LogWarning((object)string.Concat(obj)); return playerSnapshot; } private static bool IsSafeLandingSurface(Character character, RaycastHit hit) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)((RaycastHit)(ref hit)).collider == (Object)null || ((RaycastHit)(ref hit)).normal.y < 0.819152f || (Object)(object)((RaycastHit)(ref hit)).collider.attachedRigidbody != (Object)null) { return false; } if ((Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Lava>() != (Object)null || (Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<LavaRiver>() != (Object)null || (Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<MovingLava>() != (Object)null || (Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<KillPlane>() != (Object)null) { return false; } CollisionModifier component = ((Component)((RaycastHit)(ref hit)).collider).GetComponent<CollisionModifier>(); if (!((Object)(object)component == (Object)null)) { if (component.standable && !(bool)ConditionalStandableField.GetValue(component) && !component.applyEffects && !component.applyPetrify && component.knockback <= 0f) { return component.superBouncePow <= 0f; } return false; } return true; } private static bool HasLandingSupport(Character character, Vector3 torso, float torsoHeight, int mask, bool preserveOriginal) { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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_00a2: Unknown result type (might be due to invalid IL or missing references) float num = float.PositiveInfinity; float num2 = float.NegativeInfinity; RaycastHit hit = default(RaycastHit); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (!Physics.Raycast(torso + new Vector3((float)i * 0.3f, 0.35f, (float)j * 0.3f), Vector3.down, ref hit, torsoHeight + 0.65f, mask, (QueryTriggerInteraction)1) || !IsSafeLandingSurface(character, hit)) { return false; } if (!LandingSelection.IsSupportSample(torso.y - ((RaycastHit)(ref hit)).point.y, torsoHeight, ((RaycastHit)(ref hit)).normal.y, preserveOriginal)) { return false; } num = Mathf.Min(num, ((RaycastHit)(ref hit)).point.y); num2 = Mathf.Max(num2, ((RaycastHit)(ref hit)).point.y); } } if (num2 - num > 0.35f) { return false; } Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(torso.x, num2 + 0.4f, torso.z); Vector3 val2 = default(Vector3); ((Vector3)(ref val2))..ctor(torso.x, Mathf.Max(torso.y + 0.35f, val.y), torso.z); Collider[] array = Physics.OverlapCapsule(val, val2, 0.3f, -1, (QueryTriggerInteraction)2); for (int k = 0; k < array.Length; k++) { if (!((Object)(object)((Component)array[k]).GetComponentInParent<Character>() == (Object)(object)character)) { return false; } } return true; } private IEnumerator RestoreOnPlayerOwnerRoutine(Character character, PlayerSnapshot snapshot) { if (snapshot != null) { snapshot = ResolvePlayerLanding(character, snapshot); } PhotonView view = GetPhotonView((Component)(object)character); if ((Object)(object)view == (Object)null || view.IsMine) { if (snapshot == null) { yield break; } { IEnumerator local = PlayerLoadRestoration.Restore(new UnityPlayerLoadTarget(character, snapshot, () => !PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)); try { while (local.MoveNext()) { yield return local.Current; } yield break; } finally { IDisposable disposable = local as IDisposable; if (disposable != null) { disposable.Dispose(); } } } } if (!PhotonNetwork.IsMasterClient || view.Owner == null) { throw new InvalidOperationException("Only the host can request restoration on a player's owner."); } string requestId = Guid.NewGuid().ToString("N"); OwnerLoadReply reply = new OwnerLoadReply { actor = view.OwnerActorNr, viewId = view.ViewID }; ownerLoadReplies.Add(requestId, reply); int expiresAt = PhotonNetwork.ServerTimestamp + 30000; try { SendPlayerLoadEvent(reply.actor, (snapshot == null) ? "probe" : "restore", requestId, reply.viewId, expiresAt, (snapshot == null) ? "" : JsonConvert.SerializeObject((object)snapshot)); float deadline = Time.realtimeSinceStartup + ((snapshot == null) ? 5f : 30f); while (!reply.completed) { if ((Object)(object)character == (Object)null || (Object)(object)view == (Object)null || view.OwnerActorNr != reply.actor || !PhotonNetwork.IsMasterClient) { throw new InvalidOperationException("Player left or changed ownership during load."); } if (Time.realtimeSinceStartup >= deadline) { throw new TimeoutException((snapshot == null) ? "A restored player needs this save-manager build; owner support check timed out." : "Player owner did not confirm teleport/equipment restoration before timeout."); } yield return null; } if (!string.IsNullOrEmpty(reply.error)) { throw new InvalidOperationException("Player owner could not restore the save: " + reply.error); } } finally { ownerLoadReplies.Remove(requestId); } } private static void SendPlayerLoadEvent(int actor, string kind, string requestId, int viewId, int expiresAt, string payload) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) object[] array = new object[7] { "com.lucasandersen.peakallyouneedsavemanager", 6, kind, requestId, viewId, expiresAt, payload }; RaiseEventOptions val = new RaiseEventOptions(); val.TargetActors = new int[1] { actor }; if (!PhotonNetwork.RaiseEvent((byte)173, (object)array, val, SendOptions.SendReliable)) { throw new InvalidOperationException("Could not send the player restoration request."); } } public void OnEvent(EventData photonEvent) { if (photonEvent.Code != 173 || !(photonEvent.CustomData is object[] array) || array.Length != 7 || !(array[0] is string text) || text != "com.lucasandersen.peakallyouneedsavemanager" || !(array[1] is int num) || num != 6 || !(array[2] is string text2) || !(array[3] is string text3) || !(array[4] is int num2) || !(array[5] is int num3) || !(array[6] is string text4)) { return; } int num4; switch (text2) { case "scene-reply": if (num2 == 0 && num3 - PhotonNetwork.ServerTimestamp > 0) { sceneLoadBarrier?.Acknowledge(photonEvent.Sender, text3, text4); } return; case "reply": { if (ownerLoadReplies.TryGetValue(text3, out var value) && value.actor == photonEvent.Sender && value.viewId == num2) { value.error = text4; value.completed = true; } return; } default: num4 = ((text2 == "scene-finish") ? 1 : 0); break; case "scene-check": case "scene-prepare": case "scene-arm": case "scene-ready": case "scene-cancel": num4 = 1; break; } bool flag = (byte)num4 != 0; bool flag2 = text2 == "scene-cancel" || text2 == "scene-finish"; if ((!flag && text2 != "probe" && text2 != "restore" && text2 != "world-probe" && text2 != "luggage") || PhotonNetwork.MasterClient == null || photonEvent.Sender != PhotonNetwork.MasterClient.ActorNumber || (!flag2 && num3 - PhotonNetwork.ServerTimestamp <= 0)) { return; } if (flag) { if (num2 == 0) { HandleSharedSceneRequest(text2, text4, photonEvent.Sender, text3, num3); } return; } if (text2 == "world-probe" || text2 == "luggage") { if (num2 == 0) { HandleLuggageRequest(text2, text4, photonEvent.Sender, text3, num3); } return; } try { PhotonView val = PhotonView.Find(num2); Character val2 = (((Object)(object)val != (Object)null && val.IsMine) ? ((Component)val).GetComponent<Character>() : null); if ((Object)(object)val2 == (Object)null || !val2.IsLocal) { throw new InvalidOperationException("Requested player is not owned by this client."); } if (text2 == "probe") { SendPlayerLoadEvent(photonEvent.Sender, "reply", text3, num2, num3, ""); return; } PlayerSnapshot playerSnapshot = JsonConvert.DeserializeObject<PlayerSnapshot>(text4); if (playerSnapshot?.position == null || playerSnapshot.rotation == null || playerSnapshot.character?.lookValues == null) { throw new InvalidOperationException("Player position or view snapshot is missing."); } if (!ownerLoadsInProgress.Add(num2)) { throw new InvalidOperationException("A restoration is already running for this player."); } ((MonoBehaviour)this).StartCoroutine(RestoreRequestedPlayerRoutine(val2, playerSnapshot, photonEvent.Sender, text3, num2, num3)); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Owner restoration request failed: " + ex.Message)); SendPlayerLoadEvent(photonEvent.Sender, "reply", text3, num2, num3, ex.Message); } } private IEnumerator RestoreRequestedPlayerRoutine(Character character, PlayerSnapshot snapshot, int requester, string requestId, int viewId, int expiresAt) { string error = ""; IEnumerator routine = PlayerLoadRestoration.Restore(new UnityPlayerLoadTarget(character, snapshot, () => PhotonNetwork.MasterClient != null && PhotonNetwork.MasterClient.ActorNumber == requester && expiresAt - PhotonNetwork.ServerTimestamp > 0)); try { while (true) { object current; try { if (!routine.MoveNext()) { break; } current = routine.Current; goto IL_00e5; } catch (Exception ex) { error = ex.Message; ((BaseUnityPlugin)this).Logger.LogError((object)("Owner player restoration failed: " + ex)); } break; IL_00e5: yield return current; } } finally { SaveManagerPlugin saveManagerPlugin = this; (routine as IDisposable)?.Dispose(); saveManagerPlugin.ownerLoadsInProgress.Remove(viewId); } if (PhotonNetwork.InRoom) { SendPlayerLoadEvent(requester, "reply", requestId, viewId, expiresAt, error); } } private void Awake() { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) Instance = this; autoSaveEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("AutoSave", "Enable AutoSave", true, "Automatically create rolling autosaves"); autoSaveIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("AutoSave", "Interval Seconds", 300f, "Seconds between autosaves"); preferredSaveDirectory = Path.Combine(Paths.GameRootPath, "PeakSaves"); fallbackSaveDirectory = Path.Combine(Application.persistentDataPath, "PeakSaves"); saveDirectory = preferredSaveDirectory; EnsureSaveDirectoryReady(showStatus: false); harmony = new Harmony("com.lucasandersen.peakallyouneedsavemanager"); harmony.PatchAll(); PhotonNetwork.AddCallbackTarget((object)this); RefreshSaveFileList(); SetStatus("Save manager ready.", Color.cyan, 2f); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded The All You Need Save Manager Continued 1.2.5; original mod by Lucas Andersen."); } private void OnDestroy() { EndSharedScenePreparation(succeeded: false); PhotonNetwork.RemoveCallbackTarget((object)this); try { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to unpatch Harmony cleanly: {arg}"); } DestroyUiResources(); } private void Update() { UpdateSharedScenePreparation(); if (showUi) { Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } if (!showUi && !isLoading && sharedScenePreparation == null && autoSaveEnabled.Value && Time.time - lastAutoSaveTime > Mathf.Max(15f, autoSaveIntervalSeconds.Value)) { if (CanAutoSaveNow()) { TrySaveGame("Autosave"); } lastAutoSaveTime = Time.time; } } private bool CanAutoSaveNow() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (!CanSaveNow(showReason: false)) { return false; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (string.IsNullOrWhiteSpace(name) || !name.StartsWith("Level_", StringComparison.OrdinalIgnoreCase)) { return false; } if (LoadingScreenHandler.loading) { return false; } return true; } private void OnGUI() { //IL_0026: 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_0047: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (showUi) { EnsureStyles(); DrawOverlay(); GUI.depth = -1000; windowRect = GUI.Window(129193, windowRect, new WindowFunction(DrawMainWindow), "PEAK ALL YOU NEED SAVE MANAGER", windowStyle); } } private void DrawMainWindow(int _) { //IL_0032: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0405: Unknown result type (might be due to invalid IL or missing references) float num = statusMessageUntil - Time.unscaledTime; bool flag = IsInAirportScene(); GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); if (!string.IsNullOrEmpty(statusMessage) && num > 0f) { Color color = GUI.color; GUI.color = statusColor; GUILayout.Label(statusMessage, sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }); GUI.color = color; } if (isLoading) { GUILayout.Label("Loading save and synchronizing players...", sectionStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); } if (hasConfirmationPending) { DrawConfirmationPrompt(); GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 24f)); return; } GUILayout.BeginVertical(sectionStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("Create Save", subtitleStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); if (flag) { GUILayout.Label("You cannot save in the Airport.", errorLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Name", normalLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); newSaveName = GUILayout.TextField(newSaveName, textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUI.enabled = !isLoading && !flag; if (GUILayout.Button("Quick Save", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(120f) })) { TrySaveGame("Quick Save"); } GUI.enabled = !isLoading && !flag && !string.IsNullOrWhiteSpace(newSaveName); if (GUILayout.Button("Save", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(28f), GUILayout.Width(80f) })) { string saveName = newSaveName.Trim(); newSaveName = string.Empty; TrySaveGame(saveName); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.BeginVertical(sectionStyle, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Saved Runs", subtitleStyle, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); GUI.enabled = !isLoading; if (GUILayout.Button("Refresh", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(90f), GUILayout.Height(24f) })) { RefreshSaveFileList(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.Space(6f); fileScroll = GUILayout.BeginScrollView(fileScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(390f) }); if (saveEntries.Count == 0) { GUILayout.Label("No save files found.", normalLabelStyle, Array.Empty<GUILayoutOption>()); } else { foreach (SaveListEntry saveEntry in saveEntries) { DrawSaveCard(saveEntry); GUILayout.Space(5f); } } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(30f) })) { showUi = false; } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref windowRect)).width, 24f)); } private void DrawSaveCard(SaveListEntry entry) { GUIStyle obj = (entry.isCompatible ? cardStyle : cardWarningStyle); bool flag = CanSaveNow(showReason: false); GUILayout.BeginVertical(obj, Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(entry.fileName, titleStyle, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); GUILayout.Label(FormatBytes(entry.fileSize), normalLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(78f) }); GUILayout.EndHorizontal(); if (entry.metadata != null) { string text = ((entry.metadata.savedAtUtc == default(DateTime)) ? entry.fileTime.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture) : entry.metadata.savedAtUtc.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture)); int num = entry.metadata.playerCount; if (num <= 0) { num = 0; } string text2 = Safe(entry.metadata.levelName); if (entry.metadata.levelNumber.HasValue) { text2 += $" (#{entry.metadata.levelNumber.Value})"; } GUILayout.Label("Saved: " + text, normalLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label("Level: " + text2 + " Segment: " + Safe(ToDisplaySegmentName(entry.metadata.currentSegmentName, entry.metadata.biomeId)), normalLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label($"Seed: {entry.metadata.levelSeed} Ascent: {entry.metadata.ascent} Players: {num}", normalLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label(FormatRunSummary(entry.metadata), normalLabelStyle, Array.Empty<GUILayoutOption>()); } else { GUILayout.Label($"File Time: {entry.fileTime:yyyy-MM-dd HH:mm:ss}", normalLabelStyle, Array.Empty<GUILayoutOption>()); } if (!entry.isCompatible) { GUILayout.Label("Incompatible: " + entry.incompatibilityReason, errorLabelStyle, Array.Empty<GUILayoutOption>()); } GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUI.enabled = !isLoading && entry.isCompatible; if (GUILayout.Button("Load", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(24f), GUILayout.Width(90f) })) { StartLoadFromUi(entry.fullPath); } GUI.enabled = !isLoading && flag; if (GUILayout.Button("Overwrite", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(24f), GUILayout.Width(100f) })) { string saveNameHint = ((entry.metadata != null && !string.IsNullOrWhiteSpace(entry.metadata.saveName)) ? entry.metadata.saveName : Path.GetFileNameWithoutExtension(entry.fileName)); string targetPath = entry.fullPath; string fileName = entry.fileName; RequestConfirmation("Overwrite Save?", "Overwrite '" + fileName + "' with your current run state?", delegate { TryOverwriteSave(targetPath, saveNameHint); }); } GUI.enabled = !isLoading; if (GUILayout.Button("Delete", dangerButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(24f), GUILayout.Width(90f) })) { string targetPath2 = entry.fullPath; string fileName2 = entry.fileName; RequestConfirmation("Delete Save?", "Delete '" + fileName2 + "' permanently? This cannot be undone.", delegate { TryDeleteSave(targetPath2); }); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void StartLoadFromUi(string fullPath) { if (!string.IsNullOrWhiteSpace(fullPath)) { showUi = false; ClearPendingConfirmation(); pauseMenuPage?.PrepareForLoad(); ClosePauseMenuForLoad(); ((MonoBehaviour)this).StartCoroutine(LoadSaveRoutine(fullPath)); } } private void ClosePauseMenuForLoad() { try { if ((Object)(object)EventSystem.current != (Object)null) { EventSystem.current.SetSelectedGameObject((GameObject)null); } PauseMainMenu val = Object.FindFirstObjectByType<PauseMainMenu>(); if ((Object)(object)val != (Object)null) { CloseMenuWindow((MenuWindow)(object)val); ((Component)val).gameObject.SetActive(false); } if ((Object)(object)pauseMenuPage != (Object)null) { ((Component)pauseMenuPage).gameObject.SetActive(false); } if ((Object)(object)pauseMenuPageHandler != (Object)null) { ((Component)pauseMenuPageHandler).gameObject.SetActive(false); } Cursor.visible = false; Cursor.lockState = (CursorLockMode)1; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to close pause menu before load: " + ex.Message)); } } private void ToggleUi() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { SetStatus("Only the host can open Save Manager.", Color.yellow, 2.5f); return; } showUi = !showUi; if (showUi) { RefreshSaveFileList(); Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } else { ClearPendingConfirmation(); } } private void RequestConfirmation(string title, string message, Action confirmedAction) { if (confirmedAction != null) { hasConfirmationPending = true; confirmationTitle = (string.IsNullOrWhiteSpace(title) ? "Are you sure?" : title); confirmationMessage = (string.IsNullOrWhiteSpace(message) ? "Please confirm this action." : message); confirmationAction = confirmedAction; } } private void ClearPendingConfirmation() { hasConfirmationPending = false; confirmationTitle = string.Empty; confirmationMessage = string.Empty; confirmationAction = null; } private void ExecutePendingConfirmation() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) Action action = confirmationAction; ClearPendingConfirmation(); if (action == null) { return; } try { action(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Confirmation action failed: {arg}"); SetStatus("Action failed. Check BepInEx log.", Color.red, 4f); } } private void DrawConfirmationPrompt() { GUILayout.Space(8f); GUILayout.BeginVertical(sectionStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label(confirmationTitle, subtitleStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); GUILayout.Label(confirmationMessage, normalLabelStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); GUI.enabled = !isLoading; if (GUILayout.Button("Cancel", softButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(30f), GUILayout.Width(120f) })) { ClearPendingConfirmation(); } GUI.enabled = !isLoading; if (GUILayout.Button("Confirm", dangerButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(30f), GUILayout.Width(120f) })) { ExecutePendingConfirmation(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); } internal static void TryAttachPauseMenuButton(PauseMenuMainPage pauseMenuPage) { Instance?.AttachPauseMenuButton(pauseMenuPage); } private void AttachPauseMenuButton(PauseMenuMainPage pauseMenuPage) { //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0113: 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_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)pauseMenuPage == (Object)null) { return; } try { object? obj = AccessTools.Field(typeof(PauseMenuMainPage), "m_settingsButton")?.GetValue(pauseMenuPage); Button val = (Button)((obj is Button) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).transform == (Object)null || (Object)(object)((Component)val).transform.parent == (Object)null) { return; } Transform parent = ((Component)val).transform.parent; Transform val2 = parent.Find("PeakSaveManagerButton"); Button component; if ((Object)(object)val2 == (Object)null) { GameObject val3 = Object.Instantiate<GameObject>(((Component)val).gameObject, parent, false); ((Object)val3).name = "PeakSaveManagerButton"; Transform transform = ((Component)val).transform; RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null); Transform transform2 = val3.transform; RectTransform val5 = (RectTransform)(object)((transform2 is RectTransform) ? transform2 : null); if ((Object)(object)val4 != (Object)null && (Object)(object)val5 != (Object)null) { val5.anchorMin = val4.anchorMin; val5.anchorMax = val4.anchorMax; val5.pivot = val4.pivot; val5.sizeDelta = val4.sizeDelta; ((Transform)val5).localScale = ((Transform)val4).localScale; Vector2 anchoredPosition = val4.anchoredPosition; Rect rect = val4.rect; val5.anchoredPosition = anchoredPosition + new Vector2(0f, 0f - Mathf.Max(50f, ((Rect)(ref rect)).height + 8f)); } val3.transform.SetSiblingIndex(((Component)val).transform.GetSiblingIndex() + 1); MonoBehaviour[] componentsInChildren = val3.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val6 in componentsInChildren) { if ((Object)(object)val6 != (Object)null && ((object)val6).GetType().Name == "LocalizedText") { Object.Destroy((Object)(object)val6); } } SetPauseMenuButtonText(val3, "SAVE MANAGER"); component = val3.GetComponent<Button>(); } else { component = ((Component)val2).GetComponent<Button>(); } if (!((Object)(object)component == (Object)null)) { ((UnityEventBase)component.onClick).RemoveAllListeners(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { OpenPauseMenuSavePage(pauseMenuPage); }); ((Selectable)component).interactable = !PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to attach pause menu save button: " + ex.Message)); } } private void OpenPauseMenuSavePage(PauseMenuMainPage pauseMainPage) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown if ((Object)(object)pauseMainPage == (Object)null) { return; } if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { SetStatus("Only the host can open Save Manager.", Color.yellow, 2.5f); return; } UIPageHandler pageHandler = ((UIPage)pauseMainPage).GetPageHandler<UIPageHandler>(); if ((Object)(object)pageHandler == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Pause menu page handler not found."); return; } object? obj = AccessTools.Field(typeof(PauseMenuMainPage), "m_settingsButton")?.GetValue(pauseMainPage); Button settingsButtonTemplate = (Button)((obj is Button) ? obj : null); SaveManagerPausePage saveManagerPausePage = EnsurePauseMenuSavePage(pageHandler, (UIPage)(object)pauseMainPage, settingsButtonTemplate); if ((Object)(object)saveManagerPausePage == (Object)null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Save manager pause page could not be created."); return; } saveManagerPausePage.RefreshFromSource(); pageHandler.TransistionToPage((UIPage)(object)saveManagerPausePage, (PageTransistion)new SetActivePageTransistion()); Cursor.visible = true; Cursor.lockState = (CursorLockMode)0; } private SaveManagerPausePage EnsurePauseMenuSavePage(UIPageHandler pageHandler, UIPage mainPage, Button settingsButtonTemplate) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)pageHandler == (Object)null || (Object)(object)mainPage == (Object)null) { return null; } if ((Object)(object)pauseMenuPage != (Object)null && (Object)(object)pauseMenuPageHandler == (Object)(object)pageHandler) { pauseMenuPage.Initialize(this, pageHandler, mainPage, settingsButtonTemplate); return pauseMenuPage; } SaveManagerPausePage saveManagerPausePage = pageHandler.GetPage<SaveManagerPausePage>() as SaveManagerPausePage; if ((Object)(object)saveManagerPausePage != (Object)null) { pauseMenuPage = saveManagerPausePage; pauseMenuPageHandler = pageHandler; pauseMenuMainPage = mainPage; pauseMenuButtonTemplate = settingsButtonTemplate; pauseMenuPage.Initialize(this, pageHandler, mainPage, settingsButtonTemplate); return pauseMenuPage; } GameObject val = new GameObject("PauseMenuSaveManagerPage", new Type[3] { typeof(RectTransform), typeof(Image), typeof(SaveManagerPausePage) }); RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).SetParent(((Component)pageHandler).transform, false); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; ((Graphic)val.GetComponent<Image>()).color = new Color(0.02f, 0.05f, 0.1f, 0.75f); SaveManagerPausePage component2 = val.GetComponent<SaveManagerPausePage>(); AccessTools.Field(typeof(UIPage), "pageHandler")?.SetValue(component2, pageHandler); component2.Initialize(this, pageHandler, mainPage, settingsButtonTemplate); val.SetActive(false); object obj = AccessTools.Field(typeof(UIPageHandler), "_pages")?.GetValue(pageHandler); if (obj != null) { obj.GetType().GetMethod("Remove", new Type[1] { typeof(Type) })?.Invoke(obj, new object[1] { typeof(SaveManagerPausePage) }); MethodInfo method = obj.GetType().GetMethod("Add", new Type[2] { typeof(Type), typeof(UIPage) }); if (method != null) { method.Invoke(obj, new object[2] { typeof(SaveManagerPausePage), component2 }); } else { obj.GetType().GetMethod("Add")?.Invoke(obj, new object[2] { typeof(SaveManagerPausePage), component2 }); } } pauseMenuPage = component2; pauseMenuPageHandler = pageHandler; pauseMenuMainPage = mainPage; pauseMenuButtonTemplate = settingsButtonTemplate; return component2; } internal static bool TryInterceptPauseQuit(PauseMenuMainPage page) { if ((Object)(object)Instance != (Object)null) { return Instance.InterceptPauseQuit(page); } return false; } private bool InterceptPauseQuit(PauseMenuMainPage page) { //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown if (allowVanillaQuitClick || (Object)(object)page == (Object)null) { return false; } if (!CanSaveNow(showReason: false)) { return false; } FieldInfo fieldInfo = AccessTools.Field(typeof(PauseMenuMainPage), "confirmWindow"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(PauseMenuMainPage), "m_confirmOkButton"); FieldInfo fieldInfo3 = AccessTools.Field(typeof(PauseMenuMainPage), "m_confirmCancelButton"); FieldInfo fieldInfo4 = AccessTools.Field(typeof(PauseMenuMainPage), "confirmText"); MenuWindow confirmWindow = default(MenuWindow); ref MenuWindow reference = ref confirmWindow; object? obj = fieldInfo?.GetValue(page); reference = (MenuWindow)((obj is MenuWindow) ? obj : null); object? obj2 = fieldInfo2?.GetValue(page); Button val = (Button)((obj2 is Button) ? obj2 : null); object? obj3 = fieldInfo3?.GetValue(page); Button val2 = (Button)((obj3 is Button) ? obj3 : null); object? obj4 = fieldInfo4?.GetValue(page); LocalizedText val3 = (LocalizedText)((obj4 is LocalizedText) ? obj4 : null); if ((Object)(object)confirmWindow == (Object)null || (Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return false; } quitPendingSaveDecision = true; quitPendingPage = page; OpenMenuWindow(confirmWindow); if (val3 != null) { val3.SetText("Would you like to save your game before quitting?"); } SetPauseMenuButtonText(((Component)val).gameObject, "YES"); SetPauseMenuButtonText(((Component)val2).gameObject, "NO"); ((UnityEventBase)val.onClick).RemoveAllListeners(); ((UnityEvent)val.onClick).AddListener((UnityAction)delegate { CloseMenuWindow(confirmWindow); OpenPauseMenuSavePage(page); }); ((UnityEventBase)val2.onClick).RemoveAllListeners(); ((UnityEvent)val2.onClick).AddListener((UnityAction)delegate { CloseMenuWindow(confirmWindow); quitPendingSaveDecision = false; quitPendingPage = null; ContinueWithVanillaQuit(page); }); return true; } private void ContinueWithVanillaQuit(PauseMenuMainPage page) { if ((Object)(object)page == (Object)null) { return; } try { allowVanillaQuitClick = true; MethodInfo methodInfo = AccessTools.Method(typeof(PauseMenuMainPage), "Quit", (Type[])null, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(page, null); } else { AccessTools.Method(typeof(PauseMenuMainPage), "OnQuitClicked", (Type[])null, (Type[])null)?.Invoke(page, null); } } finally { allowVanillaQuitClick = false; } } private static void SetPauseMenuButtonText(GameObject buttonObject, string text) { if ((Object)(object)buttonObject == (Object)null) { return; } TMP_Text componentInChildren = buttonObject.GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = text; componentInChildren.enableAutoSizing = true; componentInChildren.fontSizeMin = 13f; componentInChildren.fontSizeMax = 21f; componentInChildren.fontStyle = (FontStyles)16; componentInChildren.overflowMode = (TextOverflowModes)1; return; } Text componentInChildren2 = buttonObject.GetComponentInChildren<Text>(true); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.text = text; componentInChildren2.fontStyle = (FontStyle)0; componentInChildren2.resizeTextForBestFit = true; componentInChildren2.resizeTextMinSize = 13; componentInChildren2.resizeTextMaxSize = 21; } } private static void OpenMenuWindow(MenuWindow window) { if ((Object)(object)window == (Object)null) { return; } try { MenuWindowOpenMethod?.Invoke(window, null); } catch { ((Component)window).gameObject.SetActive(true); } } private static void CloseMenuWindow(MenuWindow window) { if ((Object)(object)window == (Object)null) { return; } try { MenuWindowCloseMethod?.Invoke(window, null); } catch { ((Component)window).gameObject.SetActive(false); } } private static int GetLuggageState(Luggage luggage) { if ((Object)(object)luggage == (Object)null || LuggageStateField == null) { return 0; } try { object value = LuggageStateField.GetValue(luggage); return (value != null) ? Convert.ToInt32(value, CultureInfo.InvariantCulture) : 0; } catch { return 0; } } private static PhotonView GetPhotonView(Component component) { if ((Object)(object)component == (Object)null) { return null; } PhotonView component2 = component.GetComponent<PhotonView>(); if ((Object)(object)component2 != (Object)null) { return component2; } return component.GetComponentInParent<PhotonView>(); } private static void SetRespawnChestState(RespawnChest respawnChest, bool isSpent, bool hasRevivedPlayers) { if ((Object)(object)respawnChest == (Object)null) { return; } try { RespawnChestSetSpentMethod?.Invoke(respawnChest, new object[1] { isSpent }); SetRespawnChestHasRevivedPlayers(respawnChest, hasRevivedPlayers); } catch { } } private static bool GetRespawnChestHasRevivedPlayers(RespawnChest respawnChest) { if ((Object)(object)respawnChest == (Object)null) { return false; } try { if (RespawnChestGetRevivedPlayersMethod?.Invoke(respawnChest, null) is bool result) { return result; } if (RespawnChestRevivedPlayersField?.GetValue(respawnChest) is bool result2) { return result2; } } catch { } if ((Object)(object)respawnChest.skeleton != (Object)null) { return !respawnChest.skeleton.activeSelf; } return false; } private static void SetRespawnChestHasRevivedPlayers(RespawnChest respawnChest, bool hasRevivedPlayers) { if ((Object)(object)respawnChest == (Object)null) { return; } try { if (RespawnChestSetRevivedPlayersMethod != null) { RespawnChestSetRevivedPlayersMethod.Invoke(respawnChest, new object[1] { hasRevivedPlayers }); } else { RespawnChestRevivedPlayersField?.SetValue(respawnChest, hasRevivedPlayers); } } catch { } } private static string GetDayNightTimeString(DayNightManager dayNight) { if ((Object)(object)dayNight == (Object)null) { return string.Empty; } try { if ((DayNightTimeStringGetterMethod?.Invoke(dayNight, null) ?? DayNightTimeStringField?.GetValue(dayNight)) is string result) { return result; } } catch { } return string.Empty; } private static void SetDayNightTimeString(DayNightManager dayNight, string timeString) { if ((Object)(object)dayNight == (Object)null || string.IsNullOrWhiteSpace(timeString)) { return; } try { if (DayNightTimeStringSetterMethod != null) { DayNightTimeStringSetterMethod.Invoke(dayNight, new object[1] { timeString }); } else { DayNightTimeStringField?.SetValue(dayNight, timeString); } } catch { } } private bool CanSaveNow(bool showReason) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (IsInAirportScene()) { if (showReason) { SetStatus("You cannot save in the Airport.", Color.yellow, 3f); } return false; } if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { if (showReason) { SetStatus("Only host can save shared runs.", Color.yellow, 3f); } return false; } return true; } private bool TrySaveGame(string saveName) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (!CanSaveNow(showReason: true)) { return false; } try { if (!EnsureSaveDirectoryReady(showStatus: true)) { return false; } string text = NormalizeSaveDisplayName(saveName); SaveEnvelope saveEnvelope = CaptureSnapshot(text); if (saveEnvelope.players == null || saveEnvelope.players.Count == 0) { SetStatus("Could not find an active player to save. Try again in-run.", Color.yellow, 4f); ((BaseUnityPlugin)this).Logger.LogWarning((object)"Save aborted: snapshot captured 0 players."); return false; } string text2 = ResolveSavePathForName(text); string contents = JsonConvert.SerializeObject((object)saveEnvelope, (Formatting)1); File.WriteAllText(text2, contents); if (text.Equals("Autosave", StringComparison.OrdinalIgnoreCase)) { CleanupLegacyAutosaves(text2); } RefreshSaveFileList(); SetStatus("Saved: " + text, Color.green, 3f); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Saved run to " + text2)); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Save failed: {ex}"); SetStatus(BuildSaveFailureStatus(ex), Color.red, 6f); return false; } } private bool TryOverwriteSave(string fullPath, string saveNameHint) { //IL_0114: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (!CanSaveNow(showReason: true)) { return false; } if (string.IsNullOrWhiteSpace(fullPath)) { SetStatus("Invalid save path.", Color.red, 4f); return false; } try { if (!EnsureSaveDirectoryReady(showStatus: true)) { return false; } string text = NormalizeSaveDisplayName(string.IsNullOrWhiteSpace(saveNameHint) ? Path.GetFileNameWithoutExtension(fullPath) : saveNameHint.Trim()); SaveEnvelope saveEnvelope = CaptureSnapshot(text); if (saveEnvelope.players == null || saveEnvelope.players.Count == 0) { SetStatus("Could not find an active player to overwrite from. Try again in-run.", Color.yellow, 4f); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Overwrite aborted for '" + fullPath + "': snapshot captured 0 players.")); return false; } string contents = JsonConvert.SerializeObject((object)saveEnvelope, (Formatting)1); File.WriteAllText(fullPath, contents); RefreshSaveFileList(); SetStatus("Overwrote save: " + text, Color.green, 3f); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Overwrote run at " + fullPath)); return true; } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Overwrite failed for '{fullPath}': {ex}"); SetStatus(BuildSaveFailureStatus(ex), Color.red, 6f); return false; } } internal void UiRefreshSaveList() { RefreshSaveFileList(); } internal List<SaveListEntry> UiGetSaveEntries() { return new List<SaveListEntry>(saveEntries); } internal void UiQuickSave() { bool saveSucceeded = TrySaveGame("Quick Save"); MaybeCompletePendingQuitAfterSave(saveSucceeded); } internal void UiNamedSave(string saveName) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(saveName)) { SetStatus("Enter a save name first.", Color.yellow, 3f); return; } bool saveSucceeded = TrySaveGame(saveName.Trim()); MaybeCompletePendingQuitAfterSave(saveSucceeded); } internal void UiLoadSave(string fullPath) { StartLoadFromUi(fullPath); } internal void UiOverwriteSave(string fullPath, string saveNameHint) { bool saveSucceeded = TryOverwriteSave(fullPath, saveNameHint); MaybeCompletePendingQuitAfterSave(saveSucceeded); } internal void UiDeleteSave(string fullPath) { TryDeleteSave(fullPath); } internal bool UiCanSaveNow(bool showReason = false) { return CanSaveNow(showReason); } internal bool UiIsLoading() { return isLoading; } internal string UiCurrentStatus() { if (string.IsNullOrWhiteSpace(statusMessage)) { return string.Empty; } if (statusMessageUntil <= Time.unscaledTime) { return string.Empty; } return statusMessage; } internal bool UiHasPendingQuitSaveDecision() { return quitPendingSaveDecision; } internal void UiQuickSaveAndQuit() { if (CanSaveNow(showReason: true) && TrySaveGame("Quick Save")) { CompletePendingQuit(); } } internal void UiNamedSaveAndQuit(string saveName) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (CanSaveNow(showReason: true)) { if (string.IsNullOrWhiteSpace(saveName)) { SetStatus("Enter a save name before Save & Quit.", Color.yellow, 3f); } else if (TrySaveGame(saveName.Trim())) { CompletePendingQuit(); } } } internal void UiQuitWithoutSaving() { CompletePendingQuit(); } internal void UiCancelPendingQuit() { quitPendingSaveDecision = false; quitPendingPage = null; } private void MaybeCompletePendingQuitAfterSave(bool saveSucceeded) { if (saveSucceeded && quitPendingSaveDecision) { CompletePendingQuit(); } } private void CompletePendingQuit() { PauseMenuMainPage page = quitPendingPage; quitPendingSaveDecision = false; quitPendingPage = null; ContinueWithVanillaQuit(page); } private unsafe SaveEnvelope CaptureSnapshot(string saveName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0063: Expected I4, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; LevelGeneration val = Object.FindFirstObjectByType<LevelGeneration>(); int levelSeed = (((Object)(object)val != (Object)null) ? val.seed : 0); int num = 0; string currentSegmentName = ((object)(Segment)0/*cast due to .constrained prefix*/).ToString(); string currentBiomeName = null; if (MapHandler.Exists) { MapHandler val2 = Object.FindFirstObjectByType<MapHandler>(); if ((Object)(object)val2 != (Object)null) { Segment currentSegment = val2.GetCurrentSegment(); num = (int)currentSegment; currentSegmentName = ((object)(*(Segment*)(¤tSegment))/*cast due to .constrained prefix*/).ToString(); if (val2.segments != null && num >= 0 && num < val2.segments.Length) { currentBiomeName = ((object)MapHandler.GetBiomeForSegment(num)/*cast due to .constrained prefix*/).ToString(); } } } int? dailyLevelIndex = null; string biomeId = string.Empty; try { NextLevelService service = GameHandler.GetService<NextLevelService>(); if (service != null && service.HasReceivedLevelIndex) { dailyLevelIndex = service.NextLevelIndexOrFallback; biomeId = SingletonAsset<MapBaker>.Instance.GetBiomeID(dailyLevelIndex.Value + NextLevelService.debugLevelIndexOffset); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not capture NextLevelService metadata: " + ex.Message)); } string text = ResolveLevelName(name, dailyLevelIndex); int? levelNumber = ParseLevelNumber(text); CaptureRunMetadata(out var runDay, out var runTimeSeconds, out var runTimerActive, out var timeOfDay, out var inGameTime); SaveEnvelope saveEnvelope = new SaveEnvelope { pluginGuid = "com.lucasandersen.peakallyouneedsavemanager", pluginVersion = "1.2.5", me