Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of REPOInstantSave v1.0.0
REPO-Instant-Save.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Photon.Pun; using REPO_Instant_Save.Config; using REPO_Instant_Save.Hotkeys; using REPO_Instant_Save.Save; using REPO_Instant_Save.Util; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("REPO-Instant-Save")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+32d9fb03e1bc727c83679a38981610700a1b5eec")] [assembly: AssemblyProduct("REPO-Instant-Save")] [assembly: AssemblyTitle("REPO-Instant-Save")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPO_Instant_Save { internal static class ModInfo { public const string Guid = "Ashx87.REPOInstantSave"; public const string Name = "REPO Instant Save"; public const string Version = "1.0.0"; } [BepInPlugin("Ashx87.REPOInstantSave", "REPO Instant Save", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("Ashx87.REPOInstantSave"); internal static Plugin? Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal PluginConfig ModConfig { get; private set; } private void Awake() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"REPO Instant Save v1.0.0 initialising…"); try { ModConfig = new PluginConfig(((BaseUnityPlugin)this).Config); _harmony.PatchAll(); ((Component)this).gameObject.AddComponent<HotkeyManager>(); Log.LogInfo((object)string.Format("{0} loaded successfully. Full-save key: {1}.", "REPO Instant Save", ModConfig.FullSaveKey.Value)); } catch (Exception arg) { Log.LogError((object)string.Format("{0} failed to initialise: {1}", "REPO Instant Save", arg)); } } } } namespace REPO_Instant_Save.Util { internal static class GameState { public static bool IsHost => SemiFunc.IsMasterClientOrSingleplayer(); public static bool InRunLevel => SemiFunc.RunIsLevel(); public static bool StatsReady => (Object)(object)StatsManager.instance != (Object)null; } internal static class HudNotify { private static readonly Color SuccessColor = new Color(0.45f, 1f, 0.45f); private static readonly Color FailureColor = new Color(1f, 0.45f, 0.45f); public static void Toast(string message, bool success) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) try { if (!((Object)(object)MissionUI.instance == (Object)null)) { SemiFunc.UIFocusText(message, success ? SuccessColor : FailureColor, Color.white, 3f); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("HUD toast failed: " + ex.Message)); } } } } namespace REPO_Instant_Save.Save { internal static class CrossSession { public static WorldSnapshot? Pending { get; private set; } public static bool RebuildActive { get; set; } public static Level? TargetLevel { get; private set; } public static void Arm(WorldSnapshot snapshot) { Pending = snapshot; RebuildActive = false; TargetLevel = FindLevel(snapshot.levelName); Plugin.Log.LogInfo((object)("Cross-session restore armed for '" + snapshot.levelName + "' " + $"(grid {snapshot.grid.tiles.Count} tiles, {snapshot.level.Count} level objects); " + "target level " + (((Object)(object)TargetLevel != (Object)null) ? "found" : "NOT FOUND") + ".")); } private static Level? FindLevel(string levelName) { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null || instance.levels == null) { return null; } foreach (Level level in instance.levels) { if ((Object)(object)level != (Object)null && ((Object)level).name == levelName) { return level; } } return null; } public static void Clear() { Pending = null; RebuildActive = false; TargetLevel = null; } public static bool ShouldRebuildCurrentLevel() { if (Pending == null || !Pending.grid.present) { return false; } Level val = (((Object)(object)RunManager.instance != (Object)null) ? RunManager.instance.levelCurrent : null); if ((Object)(object)val != (Object)null) { return ((Object)val).name == Pending.levelName; } return false; } } internal static class InstantSaveService { public static bool SaveNow() { if (!GameState.IsHost) { Plugin.Log.LogWarning((object)"Instant Save ignored: only the host can save."); return false; } if (!GameState.InRunLevel) { Plugin.Log.LogWarning((object)"Instant Save ignored: you must be inside a level."); return false; } if (!GameState.StatsReady) { Plugin.Log.LogWarning((object)"Instant Save ignored: StatsManager not ready."); return false; } try { WorldSnapshot worldSnapshot = WorldCapture.Capture(); StatsManager.instance.SaveFileSave(); string saveFileCurrent = StatsManager.instance.saveFileCurrent; InstantSaveStore.Write(saveFileCurrent, worldSnapshot); Plugin.Log.LogInfo((object)("Instant Save captured for '" + saveFileCurrent + "' [" + worldSnapshot.levelName + "]: " + $"{worldSnapshot.level.Count} level objects, {worldSnapshot.valuables.Count} valuables, " + $"{worldSnapshot.extraction.Count} extraction points, {worldSnapshot.players.Count} players.")); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"Instant Save failed: {arg}"); return false; } } } internal static class InstantSaveStore { private const string FileName = "instantsave.json"; public static string FolderFor(string saveName) { return Path.Combine(Application.persistentDataPath, "saves", saveName); } public static string PathFor(string saveName) { return Path.Combine(FolderFor(saveName), "instantsave.json"); } public static void Write(string saveName, WorldSnapshot snapshot) { Directory.CreateDirectory(FolderFor(saveName)); string contents = JsonConvert.SerializeObject((object)snapshot, (Formatting)1); File.WriteAllText(PathFor(saveName), contents); } public static WorldSnapshot? Read(string saveName) { string path = PathFor(saveName); if (!File.Exists(path)) { return null; } return JsonConvert.DeserializeObject<WorldSnapshot>(File.ReadAllText(path)); } public static bool Exists(string saveName) { return File.Exists(PathFor(saveName)); } public static void Delete(string saveName) { string path = PathFor(saveName); if (File.Exists(path)) { File.Delete(path); } } } internal static class LevelRebuild { private static Dictionary<string, PrefabRef> _modulePrefabs = new Dictionary<string, PrefabRef>(); private static void BuildPrefabMap(LevelGenerator lg) { _modulePrefabs = new Dictionary<string, PrefabRef>(); Level level = lg.Level; if (!((Object)(object)level == (Object)null)) { AddAll(level.StartRooms); AddAll(level.ModulesNormal1); AddAll(level.ModulesNormal2); AddAll(level.ModulesNormal3); AddAll(level.ModulesPassage1); AddAll(level.ModulesPassage2); AddAll(level.ModulesPassage3); AddAll(level.ModulesDeadEnd1); AddAll(level.ModulesDeadEnd2); AddAll(level.ModulesDeadEnd3); AddAll(level.ModulesExtraction1); AddAll(level.ModulesExtraction2); AddAll(level.ModulesExtraction3); AddAll(level.ModulesSpecial); } } private static void AddAll(List<PrefabRef> list) { if (list == null) { return; } foreach (PrefabRef item in list) { if (item != null && !((Object)(object)((PrefabRef<GameObject>)(object)item).Prefab == (Object)null)) { string key = Clean(((Object)((PrefabRef<GameObject>)(object)item).Prefab).name); if (!_modulePrefabs.ContainsKey(key)) { _modulePrefabs[key] = item; } } } } public static IEnumerator RebuildTiles(LevelGenerator lg, GridDto grid) { lg.waitingForSubCoroutine = true; BuildPrefabMap(lg); lg.LevelWidth = grid.width; lg.LevelHeight = grid.height; lg.LevelGrid = new Tile[grid.width, grid.height]; for (int i = 0; i < grid.width; i++) { for (int j = 0; j < grid.height; j++) { lg.LevelGrid[i, j] = new Tile { x = i, y = j, active = false }; } } foreach (TileDto tile in grid.tiles) { if (tile.x >= 0 && tile.y >= 0 && tile.x < grid.width && tile.y < grid.height) { lg.LevelGrid[tile.x, tile.y] = new Tile { x = tile.x, y = tile.y, active = tile.active, first = tile.first, connections = tile.connections, connectedTop = tile.cTop, connectedRight = tile.cRight, connectedBot = tile.cBot, connectedLeft = tile.cLeft, type = (Type)tile.type }; } } lg.ModuleAmount = grid.tiles.Count; Plugin.Log.LogInfo((object)$"Rebuild: grid restored ({grid.tiles.Count} active tiles, ModuleAmount={lg.ModuleAmount})."); yield return null; lg.waitingForSubCoroutine = false; } public static IEnumerator RebuildStartRoom(LevelGenerator lg, WorldSnapshot snap) { lg.waitingForSubCoroutine = true; SceneObjectDto sceneObjectDto = snap.level.FirstOrDefault((SceneObjectDto o) => o.kind == "module" && o.name.StartsWith("Start Room", StringComparison.Ordinal)); if (sceneObjectDto != null && _modulePrefabs.TryGetValue(sceneObjectDto.name, out PrefabRef value) && value != null) { GameObject val = Spawn(value, Vector3.zero, Quaternion.identity); if ((Object)(object)val != (Object)null) { val.transform.parent = lg.LevelParent.transform; } } else { Plugin.Log.LogWarning((object)("Rebuild: start room prefab '" + sceneObjectDto?.name + "' not found.")); } yield return null; lg.waitingForSubCoroutine = false; } public static bool SpawnSavedModule(LevelGenerator lg, int x, int y, WorldSnapshot snap) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //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) SceneObjectDto sceneObjectDto = snap.level.FirstOrDefault((SceneObjectDto o) => o.kind == "module" && o.gridX == x && o.gridY == y && !o.name.StartsWith("Start Room", StringComparison.Ordinal)); if (sceneObjectDto == null) { return false; } if (!_modulePrefabs.TryGetValue(sceneObjectDto.name, out PrefabRef value) || value == null) { Plugin.Log.LogWarning((object)$"Rebuild: module '{sceneObjectDto.name}' at ({x},{y}) prefab not found."); return false; } GameObject val = Spawn(value, sceneObjectDto.pos.ToVector3(), Quaternion.Euler(sceneObjectDto.euler.ToVector3())); if ((Object)(object)val == (Object)null) { return false; } val.transform.parent = lg.LevelParent.transform; Module component = val.GetComponent<Module>(); if ((Object)(object)component != (Object)null) { component.GridX = x; component.GridY = y; bool first = lg.LevelGrid[x, y].first; bool flag = lg.GridCheckActive(x, y + 1); bool flag2 = lg.GridCheckActive(x, y - 1) || first; bool flag3 = lg.GridCheckActive(x + 1, y); bool flag4 = lg.GridCheckActive(x - 1, y); component.ModuleConnectionSet(flag, flag2, flag3, flag4, first); } return true; } private static GameObject Spawn(PrefabRef pr, Vector3 pos, Quaternion rot) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (GameManager.instance.gameMode == 0) { return Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)pr).Prefab, pos, rot); } return PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)pr).ResourcePath, pos, rot, (byte)0, (object[])null); } private static string Clean(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } } internal static class ProgressionSaver { public static bool SaveNow() { if (!GameState.IsHost) { Plugin.Log.LogWarning((object)"Save ignored: only the host can save the run."); return false; } if (!GameState.StatsReady) { Plugin.Log.LogWarning((object)"Save ignored: StatsManager is not ready yet."); return false; } try { StatsManager.instance.SaveFileSave(); Plugin.Log.LogInfo((object)("Run saved to '" + StatsManager.instance.saveFileCurrent + "'.")); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to save run: {arg}"); return false; } } } internal static class RestoreService { public static bool RestoreNow() { if (!GameState.IsHost) { Plugin.Log.LogWarning((object)"Restore ignored: only the host can load."); return false; } if (!GameState.StatsReady) { Plugin.Log.LogWarning((object)"Restore ignored: StatsManager not ready."); return false; } string saveFileCurrent = StatsManager.instance.saveFileCurrent; WorldSnapshot worldSnapshot = InstantSaveStore.Read(saveFileCurrent); if (worldSnapshot == null) { Plugin.Log.LogWarning((object)("No Instant Save snapshot found for '" + saveFileCurrent + "'.")); return false; } try { if ((Object)(object)Plugin.Instance == (Object)null) { Plugin.Log.LogWarning((object)"Restore ignored: plugin instance not ready."); return false; } ((MonoBehaviour)Plugin.Instance).StartCoroutine(WorldRestore.RestoreRoutine(worldSnapshot)); Plugin.Log.LogInfo((object)("Instant Save restore started from '" + saveFileCurrent + "' [" + worldSnapshot.levelName + "].")); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"Instant Save restore failed: {arg}"); return false; } } } public sealed class WorldSnapshot { public int version { get; set; } = 1; public string levelName { get; set; } = ""; public long takenUtcTicks { get; set; } public int? runLevel { get; set; } public List<SceneObjectDto> level { get; set; } = new List<SceneObjectDto>(); public List<ValuableDto> valuables { get; set; } = new List<ValuableDto>(); public List<HaulerDto> haulers { get; set; } = new List<HaulerDto>(); public List<ExtractionDto> extraction { get; set; } = new List<ExtractionDto>(); public List<PlayerDto> players { get; set; } = new List<PlayerDto>(); public RoundDto round { get; set; } = new RoundDto(); public List<HingeDto> hinges { get; set; } = new List<HingeDto>(); public GridDto grid { get; set; } = new GridDto(); } public sealed class SceneObjectDto { public string name { get; set; } = ""; public string kind { get; set; } = "other"; public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public int gridX { get; set; } = -1; public int gridY { get; set; } = -1; public int moduleType { get; set; } = -1; } public sealed class GridDto { public bool present { get; set; } public int width { get; set; } public int height { get; set; } public List<TileDto> tiles { get; set; } = new List<TileDto>(); } public sealed class TileDto { public int x { get; set; } public int y { get; set; } public bool active { get; set; } public bool first { get; set; } public int connections { get; set; } public bool cTop { get; set; } public bool cRight { get; set; } public bool cBot { get; set; } public bool cLeft { get; set; } public int type { get; set; } } public sealed class ValuableDto { public string name { get; set; } = ""; public string kind { get; set; } = "valuable"; public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public float value { get; set; } public string? inCart { get; set; } public Vec3Dto? inCartPos { get; set; } public Vec3Dto? inCartEuler { get; set; } } public sealed class HaulerDto { public string name { get; set; } = ""; public Vec3Dto pos { get; set; } = new Vec3Dto(); public float value { get; set; } } public sealed class ExtractionDto { public string name { get; set; } = ""; public Vec3Dto pos { get; set; } = new Vec3Dto(); public int haulCurrent { get; set; } public int haulGoal { get; set; } public int state { get; set; } } public sealed class RoundDto { public bool present { get; set; } public int currentHaul { get; set; } public int totalHaul { get; set; } public int currentHaulMax { get; set; } public int haulGoal { get; set; } public int haulGoalMax { get; set; } public int extractionPoints { get; set; } public int extractionPointSurplus { get; set; } public int extractionPointsCompleted { get; set; } public int extractionHaulGoal { get; set; } public bool extractionPointActive { get; set; } public bool allExtractionPointsCompleted { get; set; } } public sealed class HingeDto { public string name { get; set; } = ""; public Vec3Dto pivot { get; set; } = new Vec3Dto(); public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public bool closed { get; set; } public bool broken { get; set; } public bool dead { get; set; } } public sealed class PlayerDto { public string steamId { get; set; } = ""; public string name { get; set; } = ""; public Vec3Dto pos { get; set; } = new Vec3Dto(); public Vec3Dto euler { get; set; } = new Vec3Dto(); public int health { get; set; } } public sealed class Vec3Dto { public float x { get; set; } public float y { get; set; } public float z { get; set; } public Vec3Dto() { } public Vec3Dto(Vector3 v) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) x = v.x; y = v.y; z = v.z; } public Vector3 ToVector3() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(x, y, z); } } internal static class WorldCapture { public static WorldSnapshot Capture() { WorldSnapshot obj = new WorldSnapshot { levelName = (((Object)(object)RunManager.instance != (Object)null && (Object)(object)RunManager.instance.levelCurrent != (Object)null) ? ((Object)RunManager.instance.levelCurrent).name : "?"), takenUtcTicks = DateTime.UtcNow.Ticks, runLevel = (((Object)(object)StatsManager.instance != (Object)null) ? new int?(StatsManager.instance.GetRunStatLevel()) : ((int?)null)) }; CaptureLevelObjects(obj); CaptureGrid(obj); CaptureGrabbables(obj); CaptureHaulers(obj); CaptureExtraction(obj); CaptureRound(obj); CaptureHinges(obj); CapturePlayers(obj); return obj; } private static void CaptureLevelObjects(WorldSnapshot snap) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.LevelParent == (Object)null) { return; } foreach (Transform item in instance.LevelParent.transform) { Transform val = item; Module component = ((Component)val).GetComponent<Module>(); string kind = (((Object)(object)component != (Object)null) ? "module" : (((Object)(object)((Component)val).GetComponent<ModuleConnectObject>() != (Object)null) ? "connect" : "other")); snap.level.Add(new SceneObjectDto { name = Clean(((Object)val).name), kind = kind, pos = new Vec3Dto(val.position), euler = new Vec3Dto(val.eulerAngles), gridX = (((Object)(object)component != (Object)null) ? component.GridX : (-1)), gridY = (((Object)(object)component != (Object)null) ? component.GridY : (-1)), moduleType = ModuleTileType(instance, component) }); } } private static int ModuleTileType(LevelGenerator lg, Module? module) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected I4, but got Unknown if ((Object)(object)module == (Object)null || lg.LevelGrid == null) { return -1; } int gridX = module.GridX; int gridY = module.GridY; if (gridX < 0 || gridY < 0 || gridX >= lg.LevelWidth || gridY >= lg.LevelHeight) { return -1; } Tile val = lg.LevelGrid[gridX, gridY]; if (val == null) { return -1; } return (int)val.type; } private static void CaptureGrid(WorldSnapshot snap) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected I4, but got Unknown LevelGenerator instance = LevelGenerator.Instance; if ((Object)(object)instance == (Object)null || instance.LevelGrid == null) { return; } snap.grid.present = true; snap.grid.width = instance.LevelWidth; snap.grid.height = instance.LevelHeight; for (int i = 0; i < instance.LevelWidth; i++) { for (int j = 0; j < instance.LevelHeight; j++) { Tile val = instance.LevelGrid[i, j]; if (val != null && val.active) { snap.grid.tiles.Add(new TileDto { x = i, y = j, active = val.active, first = val.first, connections = val.connections, cTop = val.connectedTop, cRight = val.connectedRight, cBot = val.connectedBot, cLeft = val.connectedLeft, type = (int)val.type }); } } } } private static void CaptureGrabbables(WorldSnapshot snap) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) Dictionary<PhysGrabObject, PhysGrabObject> dictionary = BuildCartContainment(); PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>(); foreach (PhysGrabObject val in array) { if ((Object)(object)val == (Object)null) { continue; } ValuableObject component = ((Component)val).GetComponent<ValuableObject>(); string text = (((Object)(object)((Component)val).GetComponent<PhysGrabCart>() != (Object)null) ? "cart" : (((Object)(object)component != (Object)null) ? "valuable" : (((Object)(object)((Component)val).GetComponent<ItemAttributes>() != (Object)null) ? "item" : null))); if (text != null) { ValuableDto valuableDto = new ValuableDto { name = Clean(((Object)val).name), kind = text, pos = new Vec3Dto(((Component)val).transform.position), euler = new Vec3Dto(((Component)val).transform.eulerAngles), value = (((Object)(object)component != (Object)null) ? component.dollarValueCurrent : 0f) }; if (text != "cart" && dictionary.TryGetValue(val, out var value) && (Object)(object)value != (Object)null) { Transform transform = ((Component)value).transform; valuableDto.inCart = Clean(((Object)value).name); valuableDto.inCartPos = new Vec3Dto(transform.InverseTransformPoint(((Component)val).transform.position)); Quaternion val2 = Quaternion.Inverse(transform.rotation) * ((Component)val).transform.rotation; valuableDto.inCartEuler = new Vec3Dto(((Quaternion)(ref val2)).eulerAngles); } snap.valuables.Add(valuableDto); } } } private static void CaptureHaulers(WorldSnapshot snap) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) ItemValuableBox[] array = Object.FindObjectsOfType<ItemValuableBox>(); foreach (ItemValuableBox val in array) { if (!((Object)(object)val == (Object)null)) { snap.haulers.Add(new HaulerDto { name = Clean(((Object)val).name), pos = new Vec3Dto(((Component)val).transform.position), value = val.CurrentValue }); } } Plugin.Log.LogInfo((object)$"Instant Save: captured {snap.haulers.Count} Hauler(s)."); } private static Dictionary<PhysGrabObject, PhysGrabObject> BuildCartContainment() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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) Dictionary<PhysGrabObject, PhysGrabObject> dictionary = new Dictionary<PhysGrabObject, PhysGrabObject>(); PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>(); foreach (PhysGrabCart val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.inCart == (Object)null) { continue; } PhysGrabObject component = ((Component)val).GetComponent<PhysGrabObject>(); if ((Object)(object)component == (Object)null) { continue; } Transform inCart = val.inCart; Collider[] array2 = Physics.OverlapBox(inCart.position, inCart.localScale / 2f, inCart.rotation); foreach (Collider val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { PhysGrabObject componentInParent = ((Component)val2).GetComponentInParent<PhysGrabObject>(); if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent != (Object)(object)component && !dictionary.ContainsKey(componentInParent)) { dictionary[componentInParent] = component; } } } } return dictionary; } private static void CaptureExtraction(WorldSnapshot snap) { //IL_003a: 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_006d: Expected I4, but got Unknown ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>(); foreach (ExtractionPoint val in array) { if (!((Object)(object)val == (Object)null)) { snap.extraction.Add(new ExtractionDto { name = Clean(((Object)val).name), pos = new Vec3Dto(((Component)val).transform.position), haulCurrent = val.haulCurrent, haulGoal = val.haulGoal, state = (int)val.currentState }); } } } private static void CaptureRound(WorldSnapshot snap) { RoundDirector instance = RoundDirector.instance; if (!((Object)(object)instance == (Object)null)) { snap.round = new RoundDto { present = true, currentHaul = instance.currentHaul, totalHaul = instance.totalHaul, currentHaulMax = instance.currentHaulMax, haulGoal = instance.haulGoal, haulGoalMax = instance.haulGoalMax, extractionPoints = instance.extractionPoints, extractionPointSurplus = instance.extractionPointSurplus, extractionPointsCompleted = instance.extractionPointsCompleted, extractionHaulGoal = instance.extractionHaulGoal, extractionPointActive = instance.extractionPointActive, allExtractionPointsCompleted = instance.allExtractionPointsCompleted }; } } private static void CaptureHinges(WorldSnapshot snap) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) PhysGrabHinge[] array = Object.FindObjectsOfType<PhysGrabHinge>(); foreach (PhysGrabHinge val in array) { if (!((Object)(object)val == (Object)null)) { Vector3 v = (((Object)(object)val.hingePoint != (Object)null) ? val.hingePoint.position : ((Component)val).transform.position); snap.hinges.Add(new HingeDto { name = Clean(((Object)val).name), pivot = new Vec3Dto(v), pos = new Vec3Dto(((Component)val).transform.position), euler = new Vec3Dto(((Component)val).transform.eulerAngles), closed = val.closed, broken = val.broken, dead = val.dead }); } } } private static void CapturePlayers(WorldSnapshot snap) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null)) { snap.players.Add(new PlayerDto { steamId = (player.steamID ?? ""), name = (player.playerName ?? ""), pos = new Vec3Dto(((Component)player).transform.position), euler = new Vec3Dto(((Component)player).transform.eulerAngles), health = (((Object)(object)player.playerHealth != (Object)null) ? player.playerHealth.health : 0) }); } } } private static string Clean(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } } internal static class WorldRestore { private readonly struct ValuableSource { public readonly GameObject Prefab; public readonly string ResourcePath; public ValuableSource(GameObject prefab, string resourcePath) { Prefab = prefab; ResourcePath = resourcePath; } } private const float ImpactGraceSeconds = 3f; private const int PlayerHoldFrames = 12; private static readonly List<(PhysGrabObject pgo, Vector3 pos, Quaternion rot)> HeldHinges = new List<(PhysGrabObject, Vector3, Quaternion)>(); private static readonly List<(PhysGrabObject inst, PhysGrabObject cart, Vector3 localPos, Quaternion localRot)> HeldCartItems = new List<(PhysGrabObject, PhysGrabObject, Vector3, Quaternion)>(); public static IEnumerator RestoreRoutine(WorldSnapshot snap) { RestoreGrabbables(snap); RestoreHaulers(snap); RefreshEnemies(); RestoreExtraction(snap); RestoreHinges(snap); RestoreHealth(snap); RestorePlayers(snap); for (int i = 0; i < 12; i++) { HoldLocalPlayer(snap); HoldHinges(); HoldCartItems(); yield return (object)new WaitForFixedUpdate(); } Plugin.Log.LogInfo((object)"Restore: complete."); } public static IEnumerator CrossSessionRestore(WorldSnapshot snap) { ClearSpawnedValuables(); yield return null; yield return RestoreRoutine(snap); } private static void ClearSpawnedValuables() { int num = 0; ValuableObject[] array = Object.FindObjectsOfType<ValuableObject>(); foreach (ValuableObject val in array) { if ((Object)(object)val == (Object)null) { continue; } if (SemiFunc.IsMultiplayer()) { if (PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(((Component)val).gameObject); num++; } } else { Object.Destroy((Object)(object)((Component)val).gameObject); num++; } } Plugin.Log.LogInfo((object)$"Cross-session: cleared {num} generator-spawned valuable(s)."); } public static void RestorePlayers(WorldSnapshot snap) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //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) if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } int num = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null)) { PlayerDto playerDto = snap.players.Find((PlayerDto x) => x.steamId == player.steamID); if (playerDto != null) { player.Spawn(playerDto.pos.ToVector3(), Quaternion.Euler(playerDto.euler.ToVector3())); ZeroVelocity(((Component)player).GetComponent<Rigidbody>()); num++; } } } Plugin.Log.LogInfo((object)$"Restore: teleported {num} player(s) to saved positions."); } private static void HoldLocalPlayer(WorldSnapshot snap) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || !player.isLocal) { continue; } PlayerDto playerDto = snap.players.Find((PlayerDto x) => x.steamId == player.steamID); if (playerDto != null) { Vector3 position = playerDto.pos.ToVector3(); Quaternion rotation = Quaternion.Euler(playerDto.euler.ToVector3()); if ((Object)(object)PlayerController.instance != (Object)null) { ((Component)PlayerController.instance).transform.position = position; ((Component)PlayerController.instance).transform.rotation = rotation; } Rigidbody component = ((Component)player).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.position = position; ZeroVelocity(component); } ((Component)player).transform.position = position; } } } public static void RestoreHealth(WorldSnapshot snap) { if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null) { return; } int num = 0; foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if ((Object)(object)player == (Object)null || (Object)(object)player.playerHealth == (Object)null) { continue; } PlayerDto playerDto = snap.players.Find((PlayerDto x) => x.steamId == player.steamID); if (playerDto != null) { int health = player.playerHealth.health; int health2 = playerDto.health; if (health2 > health) { player.playerHealth.Heal(health2 - health, false); } else if (health2 < health && health2 > 0) { player.playerHealth.Hurt(health - health2, false, -1, true); } num++; } } Plugin.Log.LogInfo((object)$"Restore: set health on {num} player(s)."); } public static void RefreshEnemies() { EnemyDirector instance = EnemyDirector.instance; if ((Object)(object)instance == (Object)null || instance.enemiesSpawned == null) { return; } int num = 0; foreach (EnemyParent item in instance.enemiesSpawned.ToList()) { if (!((Object)(object)item == (Object)null)) { try { item.Despawn(); num++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: enemy despawn failed: " + ex.Message)); } } } Plugin.Log.LogInfo((object)$"Restore: refreshed (despawned) {num} enemy group(s)."); } public static void RestoreExtraction(WorldSnapshot snap) { //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0152: 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) RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance != (Object)null && snap.round != null && snap.round.present) { instance.currentHaul = snap.round.currentHaul; instance.totalHaul = snap.round.totalHaul; instance.currentHaulMax = snap.round.currentHaulMax; instance.haulGoal = snap.round.haulGoal; instance.haulGoalMax = snap.round.haulGoalMax; instance.extractionPoints = snap.round.extractionPoints; instance.extractionPointSurplus = snap.round.extractionPointSurplus; instance.extractionPointsCompleted = snap.round.extractionPointsCompleted; instance.extractionHaulGoal = snap.round.extractionHaulGoal; instance.allExtractionPointsCompleted = snap.round.allExtractionPointsCompleted; } List<ExtractionPoint> list = Object.FindObjectsOfType<ExtractionPoint>().ToList(); HashSet<ExtractionPoint> hashSet = new HashSet<ExtractionPoint>(); int num = 0; foreach (ExtractionDto item in snap.extraction) { Vector3 val = item.pos.ToVector3(); ExtractionPoint val2 = null; float num2 = float.MaxValue; foreach (ExtractionPoint item2 in list) { if (!((Object)(object)item2 == (Object)null) && !hashSet.Contains(item2)) { Vector3 val3 = ((Component)item2).transform.position - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = item2; } } } if ((Object)(object)val2 != (Object)null) { val2.UpdateLock(false); val2.haulGoal = item.haulGoal; if (item.state == 7) { MarkExtractionComplete(val2); } else { DriveExtractionState(val2, (State)1); } hashSet.Add(val2); num++; } } foreach (ExtractionPoint item3 in list) { if ((Object)(object)item3 != (Object)null) { item3.UpdateLock(false); } } if ((Object)(object)instance != (Object)null) { instance.extractionPointActive = false; instance.extractionPointCurrent = null; } Plugin.Log.LogInfo((object)$"Restore: reset {num} extraction point(s) + round accounting."); } private static void MarkExtractionComplete(ExtractionPoint ep) { ep.taxReturn = true; DriveExtractionState(ep, (State)7); if ((Object)(object)ep.platform != (Object)null) { ((Component)ep.platform).gameObject.SetActive(false); } } private static void DriveExtractionState(ExtractionPoint ep, State state) { //IL_0035: 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) //IL_003e: 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) try { PhotonView component = ((Component)ep).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer()) { component.RPC("StateSetRPC", (RpcTarget)0, new object[1] { state }); } else { ep.StateSetRPC(state, default(PhotonMessageInfo)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: extraction StateSet failed: " + ex.Message)); } } public static void RestoreHinges(WorldSnapshot snap) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013b: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) List<PhysGrabHinge> list = Object.FindObjectsOfType<PhysGrabHinge>().ToList(); HashSet<PhysGrabHinge> hashSet = new HashSet<PhysGrabHinge>(); int num = 0; HeldHinges.Clear(); foreach (HingeDto hinge in snap.hinges) { Vector3 val = hinge.pivot.ToVector3(); PhysGrabHinge val2 = null; float num2 = float.MaxValue; foreach (PhysGrabHinge item in list) { if (!((Object)(object)item == (Object)null) && !hashSet.Contains(item) && !(Clean(((Object)item).name) != hinge.name)) { Vector3 val3 = (((Object)(object)item.hingePoint != (Object)null) ? item.hingePoint.position : ((Component)item).transform.position) - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = item; } } } if ((Object)(object)val2 != (Object)null) { PhysGrabObject component = ((Component)val2).GetComponent<PhysGrabObject>(); if ((Object)(object)component != (Object)null) { Vector3 val4 = hinge.pos.ToVector3(); Quaternion val5 = Quaternion.Euler(hinge.euler.ToVector3()); component.Teleport(val4, val5); ZeroVelocity(((Component)component).GetComponent<Rigidbody>()); HeldHinges.Add((component, val4, val5)); } val2.closed = hinge.closed; val2.broken = hinge.broken; val2.dead = hinge.dead; hashSet.Add(val2); num++; } } Plugin.Log.LogInfo((object)$"Restore: reset {num}/{snap.hinges.Count} hinge(s) (doors/drawers)."); } public static void RestoreGrabbables(WorldSnapshot snap) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) List<PhysGrabObject> live = Object.FindObjectsOfType<PhysGrabObject>().ToList(); HashSet<PhysGrabObject> hashSet = new HashSet<PhysGrabObject>(); Dictionary<string, ValuableSource> map = BuildValuablePrefabMap(); List<PhysGrabObject> list = new List<PhysGrabObject>(); List<(PhysGrabObject, ValuableDto)> list2 = new List<(PhysGrabObject, ValuableDto)>(); int num = 0; int num2 = 0; int num3 = 0; foreach (ValuableDto valuable in snap.valuables) { Vector3 val = valuable.pos.ToVector3(); Quaternion val2 = Quaternion.Euler(valuable.euler.ToVector3()); PhysGrabObject val3 = null; PhysGrabObject val4 = FindNearestUnused(live, hashSet, valuable.name, val); if ((Object)(object)val4 != (Object)null) { val4.Teleport(val, val2); ZeroVelocity(((Component)val4).GetComponent<Rigidbody>()); GraceImpact(val4); if (valuable.kind == "valuable") { ValuableObject component = ((Component)val4).GetComponent<ValuableObject>(); if ((Object)(object)component != (Object)null) { SetValuableValue(component, valuable.value); num2++; } } hashSet.Add(val4); val3 = val4; num++; } else if (valuable.kind == "valuable") { PhysGrabObject val5 = RecreateValuable(valuable, map); if ((Object)(object)val5 != (Object)null) { ZeroVelocity(((Component)val5).GetComponent<Rigidbody>()); GraceImpact(val5); val3 = val5; num3++; } } if ((Object)(object)val3 != (Object)null) { if (valuable.kind == "cart") { list.Add(val3); } else if (valuable.inCart != null && valuable.inCartPos != null) { list2.Add((val3, valuable)); } } } SeatCartContents(list, list2); Plugin.Log.LogInfo((object)$"Restore: moved {num}, revalued {num2}, recreated {num3} / {snap.valuables.Count} object(s)."); } private static void SeatCartContents(List<PhysGrabObject> carts, List<(PhysGrabObject inst, ValuableDto dto)> contents) { //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) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) HeldCartItems.Clear(); if (carts.Count == 0 || contents.Count == 0) { return; } int num = 0; foreach (var (val, valuableDto) in contents) { if (!((Object)(object)val == (Object)null)) { PhysGrabObject val2 = FindCartFor(carts, valuableDto); if (!((Object)(object)val2 == (Object)null)) { Vector3 val3 = valuableDto.inCartPos.ToVector3(); Quaternion val4 = Quaternion.Euler((valuableDto.inCartEuler != null) ? valuableDto.inCartEuler.ToVector3() : Vector3.zero); val.Teleport(((Component)val2).transform.TransformPoint(val3), ((Component)val2).transform.rotation * val4); ZeroVelocity(((Component)val).GetComponent<Rigidbody>()); GraceImpact(val); HeldCartItems.Add((val, val2, val3, val4)); num++; } } } Plugin.Log.LogInfo((object)$"Restore: re-seated {num} object(s) inside {carts.Count} cart(s)."); } private static PhysGrabObject? FindCartFor(List<PhysGrabObject> carts, ValuableDto dto) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject result = null; float num = float.MaxValue; Vector3 val = dto.pos.ToVector3(); foreach (PhysGrabObject cart in carts) { if (!((Object)(object)cart == (Object)null) && !(Clean(((Object)cart).name) != dto.inCart)) { Vector3 val2 = ((Component)cart).transform.position - val; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = cart; } } } return result; } private static void HoldCartItems() { //IL_0026: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) foreach (var (val, val2, val3, val4) in HeldCartItems) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { Vector3 position = ((Component)val2).transform.TransformPoint(val3); Quaternion rotation = ((Component)val2).transform.rotation * val4; Rigidbody component = ((Component)val).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.position = position; component.rotation = rotation; ZeroVelocity(component); } ((Component)val).transform.position = position; ((Component)val).transform.rotation = rotation; } } } private static PhysGrabObject? FindNearestUnused(List<PhysGrabObject> live, HashSet<PhysGrabObject> used, string name, Vector3 target) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) PhysGrabObject result = null; float num = float.MaxValue; foreach (PhysGrabObject item in live) { if (!((Object)(object)item == (Object)null) && !used.Contains(item) && !(Clean(((Object)item).name) != name)) { Vector3 val = ((Component)item).transform.position - target; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; result = item; } } } return result; } private static void SetValuableValue(ValuableObject vo, float value) { //IL_0038: 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) try { PhotonView component = ((Component)vo).GetComponent<PhotonView>(); if ((Object)(object)component != (Object)null && SemiFunc.IsMultiplayer()) { component.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { value }); } else { vo.DollarValueSetRPC(value, default(PhotonMessageInfo)); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: could not set value on '" + ((Object)vo).name + "': " + ex.Message)); } } public static void RestoreHaulers(WorldSnapshot snap) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if (snap.haulers == null || snap.haulers.Count == 0) { return; } List<ItemValuableBox> list = Object.FindObjectsOfType<ItemValuableBox>().ToList(); HashSet<ItemValuableBox> hashSet = new HashSet<ItemValuableBox>(); int num = 0; foreach (HaulerDto hauler in snap.haulers) { Vector3 val = hauler.pos.ToVector3(); ItemValuableBox val2 = null; float num2 = float.MaxValue; foreach (ItemValuableBox item in list) { if (!((Object)(object)item == (Object)null) && !hashSet.Contains(item) && !(Clean(((Object)item).name) != hauler.name)) { Vector3 val3 = ((Component)item).transform.position - val; float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = item; } } } if ((Object)(object)val2 != (Object)null) { RestoreValuableBox(val2, hauler.value); hashSet.Add(val2); num++; } } Plugin.Log.LogInfo((object)$"Restore: set stored value on {num}/{snap.haulers.Count} Hauler(s) (live: {list.Count})."); } private static void RestoreValuableBox(ItemValuableBox box, float value) { try { box.UpdateValue(value); Plugin.Log.LogInfo((object)$"Restore: set Hauler '{Clean(((Object)box).name)}' stored value to {value:0}."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Restore: could not set Hauler value on '" + ((Object)box).name + "': " + ex.Message)); } } private static PhysGrabObject? RecreateValuable(ValuableDto dto, Dictionary<string, ValuableSource> map) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_006a: Unknown result type (might be due to invalid IL or missing references) if (!map.TryGetValue(dto.name, out var value) || (Object)(object)value.Prefab == (Object)null) { Plugin.Log.LogWarning((object)("Restore: cannot re-create '" + dto.name + "' — prefab not found in level presets.")); return null; } Vector3 val = dto.pos.ToVector3(); Quaternion val2 = Quaternion.Euler(dto.euler.ToVector3()); GameObject val3 = (SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject(value.ResourcePath, val, val2, (byte)0, (object[])null) : Object.Instantiate<GameObject>(value.Prefab, val, val2)); if ((Object)(object)val3 == (Object)null) { return null; } ValuableObject component = val3.GetComponent<ValuableObject>(); if ((Object)(object)component != (Object)null) { component.dollarValueOverride = Mathf.RoundToInt(dto.value); } return val3.GetComponent<PhysGrabObject>(); } private static Dictionary<string, ValuableSource> BuildValuablePrefabMap() { Dictionary<string, ValuableSource> dictionary = new Dictionary<string, ValuableSource>(); Level val = (((Object)(object)LevelGenerator.Instance != (Object)null) ? LevelGenerator.Instance.Level : null); if ((Object)(object)val != (Object)null && val.ValuablePresets != null) { foreach (LevelValuables valuablePreset in val.ValuablePresets) { if (!((Object)(object)valuablePreset == (Object)null)) { AddAll(dictionary, valuablePreset.tiny); AddAll(dictionary, valuablePreset.small); AddAll(dictionary, valuablePreset.medium); AddAll(dictionary, valuablePreset.big); AddAll(dictionary, valuablePreset.wide); AddAll(dictionary, valuablePreset.tall); AddAll(dictionary, valuablePreset.veryTall); } } } AssetManager instance = AssetManager.instance; if ((Object)(object)instance != (Object)null) { AddSurplus(dictionary, instance.surplusValuableSmall); AddSurplus(dictionary, instance.surplusValuableMedium); AddSurplus(dictionary, instance.surplusValuableBig); } return dictionary; } private static void AddAll(Dictionary<string, ValuableSource> map, List<PrefabRef> list) { if (list == null) { return; } foreach (PrefabRef item in list) { if (item != null && !((Object)(object)((PrefabRef<GameObject>)(object)item).Prefab == (Object)null)) { string key = Clean(((Object)((PrefabRef<GameObject>)(object)item).Prefab).name); if (!map.ContainsKey(key)) { map[key] = new ValuableSource(((PrefabRef<GameObject>)(object)item).Prefab, ((PrefabRef<GameObject>)(object)item).ResourcePath); } } } } private static void AddSurplus(Dictionary<string, ValuableSource> map, GameObject? prefab) { if (!((Object)(object)prefab == (Object)null)) { string key = Clean(((Object)prefab).name); if (!map.ContainsKey(key)) { map[key] = new ValuableSource(prefab, "Valuables/" + ((Object)prefab).name); } } } private static void HoldHinges() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) foreach (var (val, position, rotation) in HeldHinges) { if (!((Object)(object)val == (Object)null)) { Rigidbody component = ((Component)val).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.position = position; component.rotation = rotation; ZeroVelocity(component); } ((Component)val).transform.position = position; ((Component)val).transform.rotation = rotation; } } } private static void GraceImpact(PhysGrabObject pgo) { PhysGrabObjectImpactDetector component = ((Component)pgo).GetComponent<PhysGrabObjectImpactDetector>(); if ((Object)(object)component != (Object)null) { component.ImpactDisable(3f); } } private static void ZeroVelocity(Rigidbody? rb) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rb == (Object)null) && !rb.isKinematic) { rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } } private static string Clean(string name) { if (!name.EndsWith("(Clone)", StringComparison.Ordinal)) { return name; } return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd(); } } } namespace REPO_Instant_Save.Patches { [HarmonyPatch(typeof(StatsManager), "LoadGame")] internal static class CrossSessionLoadPatch { [HarmonyPostfix] private static void Postfix(string fileName) { try { WorldSnapshot worldSnapshot = InstantSaveStore.Read(fileName); string reason; if (worldSnapshot == null || !worldSnapshot.grid.present) { CrossSession.Clear(); } else if (IsSnapshotStale(worldSnapshot, out reason)) { Plugin.Log.LogInfo((object)("Cross-session: discarding stale Instant Save for '" + fileName + "' (" + reason + "); resuming native progression.")); CrossSession.Clear(); InstantSaveStore.Delete(fileName); } else { CrossSession.Arm(worldSnapshot); if ((Object)(object)CrossSession.TargetLevel != (Object)null && (Object)(object)RunManager.instance != (Object)null) { RunManager.instance.debugLevel = CrossSession.TargetLevel; Plugin.Log.LogInfo((object)("Cross-session: forcing next level to '" + ((Object)CrossSession.TargetLevel).name + "'.")); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cross-session arm failed for '" + fileName + "': " + ex.Message)); } } private static bool IsSnapshotStale(WorldSnapshot snapshot, out string reason) { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { reason = ""; return false; } int runStatSaveLevel = instance.GetRunStatSaveLevel(); if (runStatSaveLevel != 0) { reason = $"native save resumes in phase {runStatSaveLevel} (shop/lobby), not in a level"; return true; } if (snapshot.runLevel.HasValue) { int runStatLevel = instance.GetRunStatLevel(); if (runStatLevel != snapshot.runLevel.Value) { reason = $"native run level {runStatLevel} != snapshot run level {snapshot.runLevel.Value}"; return true; } } reason = ""; return false; } } [HarmonyPatch(typeof(LevelGenerator), "GenerateDone")] internal static class CrossSessionGenerateDonePatch { [HarmonyPostfix] private static void Postfix() { if (CrossSession.Pending != null && CrossSession.RebuildActive) { WorldSnapshot pending = CrossSession.Pending; Plugin.Log.LogInfo((object)("Cross-session: saved level '" + pending.levelName + "' rebuilt — applying world restore.")); if ((Object)(object)RunManager.instance != (Object)null) { RunManager.instance.debugLevel = null; } if ((Object)(object)Plugin.Instance != (Object)null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(WorldRestore.CrossSessionRestore(pending)); } CrossSession.Clear(); } } } [HarmonyPatch(typeof(LevelGenerator), "TileGeneration")] internal static class TileGenerationRebuildPatch { [HarmonyPrefix] private static bool Prefix(LevelGenerator __instance, ref IEnumerator __result) { if (CrossSession.Pending == null || !CrossSession.ShouldRebuildCurrentLevel()) { return true; } CrossSession.RebuildActive = true; Plugin.Log.LogInfo((object)("Cross-session: rebuilding saved layout for '" + CrossSession.Pending.levelName + "'.")); __result = LevelRebuild.RebuildTiles(__instance, CrossSession.Pending.grid); return false; } } [HarmonyPatch(typeof(LevelGenerator), "StartRoomGeneration")] internal static class StartRoomRebuildPatch { [HarmonyPrefix] private static bool Prefix(LevelGenerator __instance, ref IEnumerator __result) { if (!CrossSession.RebuildActive || CrossSession.Pending == null) { return true; } __result = LevelRebuild.RebuildStartRoom(__instance, CrossSession.Pending); return false; } } [HarmonyPatch(typeof(LevelGenerator), "SpawnModule")] internal static class SpawnModuleRebuildPatch { [HarmonyPrefix] private static bool Prefix(LevelGenerator __instance, int x, int y) { if (!CrossSession.RebuildActive || CrossSession.Pending == null) { return true; } return !LevelRebuild.SpawnSavedModule(__instance, x, y, CrossSession.Pending); } } [HarmonyPatch(typeof(DataDirector), "SaveDeleteCheck")] internal static class SaveDeletePatch { [HarmonyPrefix] private static bool Prefix() { Plugin? instance = Plugin.Instance; if (instance != null && instance.ModConfig?.PreventDeathDelete.Value == true) { Plugin.Log.LogInfo((object)"Blocked REPO auto-delete of the save file (PreventDeathDelete)."); return false; } return true; } } } namespace REPO_Instant_Save.Hotkeys { internal sealed class HotkeyManager : MonoBehaviour { private void Update() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) PluginConfig pluginConfig = Plugin.Instance?.ModConfig; if (pluginConfig != null && pluginConfig.EnableHotkeys.Value && Input.GetKeyDown(pluginConfig.FullSaveKey.Value)) { bool flag = InstantSaveService.SaveNow(); HudNotify.Toast(flag ? "Instant Save complete" : "Instant Save failed", flag); } } } } namespace REPO_Instant_Save.Config { internal sealed class PluginConfig { public ConfigEntry<KeyCode> FullSaveKey { get; } public ConfigEntry<bool> EnableHotkeys { get; } public ConfigEntry<bool> PreventDeathDelete { get; } public PluginConfig(ConfigFile file) { FullSaveKey = file.Bind<KeyCode>("Hotkeys", "FullSaveNow", (KeyCode)288, "Capture an Instant Save of the whole level right now (host only)."); EnableHotkeys = file.Bind<bool>("Hotkeys", "EnableHotkeys", true, "Enable the Instant Save hotkey (F7 by default)."); PreventDeathDelete = file.Bind<bool>("Safety", "PreventDeathDelete", true, "Prevent REPO from deleting your save when the whole team dies or you leave a run early."); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } internal static class IsExternalInit { } }