Please disclose if your mod was created primarily 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 SmartItemSaving v1.2.4
SmartItemSaving.dll
Decompiled 2 years agousing System; using System.Collections.Generic; using System.Diagnostics; 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.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using IL; using IL.GameNetcodeStuff; using Microsoft.CodeAnalysis; using Mono.Cecil.Cil; using MonoMod.Cil; using On; using On.GameNetcodeStuff; using SmartItemSaving.Fixes; using Unity.Collections; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SmartItemSaving")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("A mod for Lethal Company")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+462be6473b002a343c431db4f5928d445fdb6193")] [assembly: AssemblyProduct("SmartItemSaving")] [assembly: AssemblyTitle("SmartItemSaving")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SmartItemSaving { public static class Compatibility { public static bool HasSaveItemRotations() { PluginInfo pluginInfo; return HasSaveItemRotations(out pluginInfo); } public static bool HasSaveItemRotations(out PluginInfo pluginInfo) { return Chainloader.PluginInfos.TryGetValue("moe.sylvi.SaveItemRotations", out pluginInfo); } public static bool HasLethalLevelLoader() { PluginInfo pluginInfo; return HasLethalLevelLoader(out pluginInfo); } public static bool HasLethalLevelLoader(out PluginInfo pluginInfo) { return Chainloader.PluginInfos.TryGetValue("imabatby.lethallevelloader", out pluginInfo); } } public class Config { public static ConfigEntry<bool> FixItemIds { get; private set; } public static ConfigEntry<bool> RemoveIfNotFound { get; private set; } public static ConfigEntry<bool> SaveItemRotation { get; private set; } public static ConfigEntry<bool> FixItemFalling { get; private set; } public static ConfigEntry<bool> BetterSyncItems { get; private set; } public static ConfigEntry<bool> BackupOnLoad { get; private set; } public static ConfigEntry<bool> ForceHandleFixItemIds { get; private set; } public static ConfigEntry<bool> ForceHandleSaveItemRotation { get; private set; } public Config(ConfigFile cfg) { FixItemIds = cfg.Bind<bool>("FixItemIds", "Enabled", true, "Attempts to fix changed item ids on load by comparing the saved item names."); RemoveIfNotFound = cfg.Bind<bool>("FixItemIds", "RemoveIfNotFound", true, "Removes items with missing names instead of replacing them with an item of the same ID."); SaveItemRotation = cfg.Bind<bool>("Misc", "SaveItemRotation", true, "Saves and loads the rotation of items on the ship."); FixItemFalling = cfg.Bind<bool>("Misc", "FixItemFalling", true, "Fixes items falling through furniture on load."); BetterSyncItems = cfg.Bind<bool>("Misc", "BetterSyncItems", true, "[CLIENT AND HOST] Correctly synchronizes item positions and rotations upon joining (important for Fix Item Falling and Save Item Rotation)."); BackupOnLoad = cfg.Bind<bool>("Misc", "BackupOnLoad", true, "Whether save files should be backed up on load incase the mod causes any destructive behaviour. NOTE: Only one backup is made!"); ForceHandleFixItemIds = cfg.Bind<bool>("Compatibility", "ForceHandleFixItemIds", false, "Forces this mod to handle item ID fixing even if the mod LethalLevelLoader is active, which uses a generally more comprehensive ID fixing system."); ForceHandleSaveItemRotation = cfg.Bind<bool>("Compatibility", "ForceHandleSaveItemRotation", false, "Forces this mod to handle item rotation loading even if the mod SaveItemRotations is active."); } } public class Patches { [CompilerGenerated] private static class <>O { public static Manipulator <0>__GameNetworkManager_SaveGameValues; public static Manipulator <1>__GameNetworkManager_SaveItemsInShip; public static hook_SetTimeAndPlanetToSavedSettings <2>__StartOfRound_SetTimeAndPlanetToSavedSettings; public static Manipulator <3>__StartOfRound_LoadUnlockables; public static Manipulator <4>__StartOfRound_LoadShipGrabbableItems; public static hook_Start <5>__GrabbableObject_Start; public static hook_Update <6>__GrabbableObject_Update; public static hook_ConnectClientToPlayerObject <7>__PlayerControllerB_ConnectClientToPlayerObject; public static Manipulator <8>__PlayerControllerB_ThrowObjectClientRpc; public static Action<GameNetworkManager> <9>__SaveInitialValues; public static Action<GameNetworkManager, List<int>> <10>__SaveFixUnlockIds; public static Action<GameNetworkManager> <11>__DeleteItemKeys; public static Action <12>__PreSave; public static Action<GrabbableObject[], int> <13>__Save; public static Func<Vector3, GrabbableObject[], int, Vector3> <14>__Save; } public static void Initialize() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //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_003d: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_0074: 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_007f: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_0119: 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_0124: Expected O, but got Unknown object obj = <>O.<0>__GameNetworkManager_SaveGameValues; if (obj == null) { Manipulator val = GameNetworkManager_SaveGameValues; <>O.<0>__GameNetworkManager_SaveGameValues = val; obj = (object)val; } GameNetworkManager.SaveGameValues += (Manipulator)obj; object obj2 = <>O.<1>__GameNetworkManager_SaveItemsInShip; if (obj2 == null) { Manipulator val2 = GameNetworkManager_SaveItemsInShip; <>O.<1>__GameNetworkManager_SaveItemsInShip = val2; obj2 = (object)val2; } GameNetworkManager.SaveItemsInShip += (Manipulator)obj2; object obj3 = <>O.<2>__StartOfRound_SetTimeAndPlanetToSavedSettings; if (obj3 == null) { hook_SetTimeAndPlanetToSavedSettings val3 = StartOfRound_SetTimeAndPlanetToSavedSettings; <>O.<2>__StartOfRound_SetTimeAndPlanetToSavedSettings = val3; obj3 = (object)val3; } StartOfRound.SetTimeAndPlanetToSavedSettings += (hook_SetTimeAndPlanetToSavedSettings)obj3; object obj4 = <>O.<3>__StartOfRound_LoadUnlockables; if (obj4 == null) { Manipulator val4 = StartOfRound_LoadUnlockables; <>O.<3>__StartOfRound_LoadUnlockables = val4; obj4 = (object)val4; } StartOfRound.LoadUnlockables += (Manipulator)obj4; object obj5 = <>O.<4>__StartOfRound_LoadShipGrabbableItems; if (obj5 == null) { Manipulator val5 = StartOfRound_LoadShipGrabbableItems; <>O.<4>__StartOfRound_LoadShipGrabbableItems = val5; obj5 = (object)val5; } StartOfRound.LoadShipGrabbableItems += (Manipulator)obj5; object obj6 = <>O.<5>__GrabbableObject_Start; if (obj6 == null) { hook_Start val6 = GrabbableObject_Start; <>O.<5>__GrabbableObject_Start = val6; obj6 = (object)val6; } GrabbableObject.Start += (hook_Start)obj6; object obj7 = <>O.<6>__GrabbableObject_Update; if (obj7 == null) { hook_Update val7 = GrabbableObject_Update; <>O.<6>__GrabbableObject_Update = val7; obj7 = (object)val7; } GrabbableObject.Update += (hook_Update)obj7; object obj8 = <>O.<7>__PlayerControllerB_ConnectClientToPlayerObject; if (obj8 == null) { hook_ConnectClientToPlayerObject val8 = PlayerControllerB_ConnectClientToPlayerObject; <>O.<7>__PlayerControllerB_ConnectClientToPlayerObject = val8; obj8 = (object)val8; } PlayerControllerB.ConnectClientToPlayerObject += (hook_ConnectClientToPlayerObject)obj8; object obj9 = <>O.<8>__PlayerControllerB_ThrowObjectClientRpc; if (obj9 == null) { Manipulator val9 = PlayerControllerB_ThrowObjectClientRpc; <>O.<8>__PlayerControllerB_ThrowObjectClientRpc = val9; obj9 = (object)val9; } PlayerControllerB.ThrowObjectClientRpc += (Manipulator)obj9; } private static void GameNetworkManager_SaveGameValues(ILContext il) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); int unlockListLoc = -1; if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[2] { (Instruction instr1) => ILPatternMatchingExt.MatchLdstr(instr1, "UnlockedShipObjects"), (Instruction instr2) => ILPatternMatchingExt.MatchLdloc(instr2, ref unlockListLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveGameValues @ Save unlocked ship objects list"); return; } Instruction next = val.Next; if (!val.TryGotoPrev((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, unlockListLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveGameValues @ After initialize unlocks list"); return; } val.MoveAfterLabels(); val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Action<GameNetworkManager>>((Action<GameNetworkManager>)General.SaveInitialValues); val.Goto(next, (MoveType)1, false); val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Ldloc, unlockListLoc); val.EmitDelegate<Action<GameNetworkManager, List<int>>>((Action<GameNetworkManager, List<int>>)FixUnlockIds.SaveFixUnlockIds); } private static void GameNetworkManager_SaveItemsInShip(ILContext il) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt<Object>(instr, "FindObjectsByType") })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Find grabbable objects"); return; } int grabbableObjectsLoc = -1; if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, ref grabbableObjectsLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Get grabbable objects local"); return; } if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt<ES3>(instr, "DeleteKey") })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Delete empty keys"); return; } val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Action<GameNetworkManager>>((Action<GameNetworkManager>)General.DeleteItemKeys); int[] array = new int[4]; for (int i = 0; i < array.Length; i++) { int loc = -1; if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, ref loc) })) { Plugin.Logger.LogError((object)$"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Get list {i} local"); return; } array[i] = loc; } int num = array[0]; int posLoc = array[1]; int num2 = array[2]; int num3 = array[3]; int iLoc = -1; ILLabel val2 = default(ILLabel); if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[3] { (Instruction instr1) => ILPatternMatchingExt.MatchLdcI4(instr1, 0), (Instruction instr2) => ILPatternMatchingExt.MatchStloc(instr2, ref iLoc), (Instruction instr3) => ILPatternMatchingExt.MatchBr(instr3, ref val2) })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Get loop 'i' local"); return; } val.EmitDelegate<Action>((Action)FixItemRotation.PreSave); if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchLdloc(instr, posLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Before add item position"); return; } val.Emit(OpCodes.Ldloc, grabbableObjectsLoc); val.Emit(OpCodes.Ldloc, iLoc); val.EmitDelegate<Action<GrabbableObject[], int>>((Action<GrabbableObject[], int>)FixItemRotation.Save); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt<Transform>(instr, "get_position") })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ Get item position"); return; } val.Emit(OpCodes.Ldloc, grabbableObjectsLoc); val.Emit(OpCodes.Ldloc, iLoc); val.EmitDelegate<Func<Vector3, GrabbableObject[], int, Vector3>>((Func<Vector3, GrabbableObject[], int, Vector3>)FixItemFalling.Save); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt<ES3>(instr, "Save") })) { Plugin.Logger.LogError((object)"Failed IL hook for GameNetworkManager.SaveItemsInShip @ First save call"); return; } val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Ldloc, num); val.EmitDelegate<Action<GameNetworkManager, List<int>>>((Action<GameNetworkManager, List<int>>)delegate(GameNetworkManager self, List<int> ids) { FixItemRotation.PostSave(self); FixItemIds.Save(self, ids); }); } private static void StartOfRound_SetTimeAndPlanetToSavedSettings(orig_SetTimeAndPlanetToSavedSettings orig, StartOfRound self) { General.LoadCreateBackup(); orig.Invoke(self); General.LoadInitialValues(self); } private static void StartOfRound_LoadUnlockables(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt<ES3>(instr, "Load") })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadUnlockables @ Load unlocks list"); return; } val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Func<int[], StartOfRound, int[]>>((Func<int[], StartOfRound, int[]>)delegate(int[] unlocks, StartOfRound self) { try { FixUnlockIds.LoadFixUnlockIds(self, ref unlocks); } catch (Exception ex) { Plugin.Logger.LogError((object)"Load | Unlocks | Error occured during load"); Plugin.Logger.LogError((object)ex); } return unlocks; }); } private static void StartOfRound_LoadShipGrabbableItems(ILContext il) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0395: Unknown result type (might be due to invalid IL or missing references) //IL_03a7: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); int idsLoc = -1; if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchLdstr(instr, "shipGrabbableItemIDs") }) || !val.TryGotoNext((MoveType)1, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, ref idsLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Get item IDs local"); return; } ILLabel val3 = default(ILLabel); if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[2] { (Instruction instr1) => ILPatternMatchingExt.MatchBrfalse(instr1, ref val3), (Instruction instr2) => ILPatternMatchingExt.MatchLdstr(instr2, "shipScrapValues") })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Load values"); return; } int valuesLoc = -1; if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, ref valuesLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Get item values local"); return; } int dataLoc = 1; if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2] { (Instruction instr1) => ILPatternMatchingExt.MatchCallOrCallvirt<ES3>(instr1, "Load"), (Instruction instr2) => ILPatternMatchingExt.MatchStloc(instr2, ref dataLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Get item data local"); return; } val.MoveAfterLabels(); val.Emit(OpCodes.Ldarg_0); val.Emit(OpCodes.Ldloc, idsLoc); val.Emit(OpCodes.Ldloc, valuesLoc); val.Emit(OpCodes.Ldloc, dataLoc); val.EmitDelegate<Func<StartOfRound, int[], int[], int[], (int[], int[])>>((Func<StartOfRound, int[], int[], int[], (int[], int[])>)delegate(StartOfRound self, int[] ids, int[] values, int[] data) { try { FixItemFalling.PreLoad(); FixItemRotation.PreLoad(ids); FixItemIds.Load(self, ids, ref values, ref data); } catch (Exception ex2) { Plugin.Logger.LogError((object)"Load | Items | Error occured during pre-load"); Plugin.Logger.LogError((object)ex2); } return (values, data); }); val.Emit(OpCodes.Dup); val.EmitDelegate<Func<(int[], int[]), int[]>>((Func<(int[], int[]), int[]>)(((int[], int[]) tuple) => tuple.Item1)); val.Emit(OpCodes.Stloc, valuesLoc); val.EmitDelegate<Func<(int[], int[]), int[]>>((Func<(int[], int[]), int[]>)(((int[], int[]) tuple) => tuple.Item2)); val.Emit(OpCodes.Stloc, dataLoc); int iLoc = -1; ILLabel val2 = default(ILLabel); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[3] { (Instruction instr1) => ILPatternMatchingExt.MatchLdcI4(instr1, 0), (Instruction instr2) => ILPatternMatchingExt.MatchStloc(instr2, ref iLoc), (Instruction instr3) => ILPatternMatchingExt.MatchBr(instr3, ref val2) })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Get loop 'i' local"); return; } if (!val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchCallOrCallvirt<GameObject>(instr, "GetComponent") })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Grabbable object instantiation"); return; } int grabbableObjectLoc = -1; if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchStloc(instr, ref grabbableObjectLoc) })) { Plugin.Logger.LogError((object)"Failed IL hook for StartOfRound.LoadShipGrabbableItems @ Get grabbable object local"); return; } val.Emit(OpCodes.Ldloc, iLoc); val.Emit(OpCodes.Ldloc, grabbableObjectLoc); val.EmitDelegate<Action<int, GrabbableObject>>((Action<int, GrabbableObject>)delegate(int i, GrabbableObject grabbableObject) { try { FixItemFalling.Load(grabbableObject); FixItemRotation.Load(i, grabbableObject); } catch (Exception ex) { Plugin.Logger.LogError((object)"Load | Items | Error occured during load"); Plugin.Logger.LogError((object)ex); } }); } private static void GrabbableObject_Update(orig_Update orig, GrabbableObject self) { orig.Invoke(self); FixItemRotation.Apply(self); } private static void GrabbableObject_Start(orig_Start orig, GrabbableObject self) { orig.Invoke(self); FixItemFalling.Apply(self); } private static void PlayerControllerB_ConnectClientToPlayerObject(orig_ConnectClientToPlayerObject orig, PlayerControllerB self) { orig.Invoke(self); BetterSyncItems.InitializeNetworkingAndSync(); } private static void PlayerControllerB_ThrowObjectClientRpc(ILContext il) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) ILCursor val = new ILCursor(il); if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { (Instruction instr) => ILPatternMatchingExt.MatchLdcI4(instr, -1) })) { Plugin.Logger.LogWarning((object)"Failed IL hook for PlayerControllerB.ThrowObjectClientRpc @ Pass -1 (Not a big deal)"); return; } val.Emit(OpCodes.Ldarg, 5); val.EmitDelegate<Func<int, int, int>>((Func<int, int, int>)((int orig, int floorYRot) => (!Config.BetterSyncItems.Value) ? orig : floorYRot)); } } [BepInPlugin("SylviBlossom.SmartItemSaving", "SmartItemSaving", "1.2.4")] public class Plugin : BaseUnityPlugin { public const int FormatVersion = 2; public static Plugin Instance { get; private set; } public static Config Config { get; private set; } public static ManualLogSource Logger { get; private set; } private void Awake() { Instance = this; Config = new Config(((BaseUnityPlugin)this).Config); Logger = ((BaseUnityPlugin)this).Logger; Patches.Initialize(); Logger.LogInfo((object)"Plugin SmartItemSaving is loaded!"); } } public static class PluginInfo { public const string PLUGIN_GUID = "SylviBlossom.SmartItemSaving"; public const string PLUGIN_NAME = "SmartItemSaving"; public const string PLUGIN_VERSION = "1.2.4"; } public static class SaveKeys { public const string FormatVersion = "SylviBlossom.SmartItemSaving_formatVersion"; public const string ParityStepsTaken = "SylviBlossom.SmartItemSaving_parityStepsTaken"; public const string ItemNames = "SylviBlossom.SmartItemSaving_itemNames"; public const string ItemHasValue = "SylviBlossom.SmartItemSaving_itemHasValue"; public const string ItemHasData = "SylviBlossom.SmartItemSaving_itemHasData"; public const string ItemRotations = "SylviBlossom.SmartItemSaving_itemRotations"; public const string UnlockNames = "SylviBlossom.SmartItemSaving_unlockNames"; } } namespace SmartItemSaving.Fixes { public static class BetterSyncItems { [CompilerGenerated] private static class <>O { public static Func<GrabbableObject, bool> <0>__IsValidObject; public static HandleNamedMessageDelegate <1>__OnRequestSync; public static HandleNamedMessageDelegate <2>__OnReceiveSync; } internal static void RequestSync() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (!NetworkManager.Singleton.IsClient) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(4, (Allocator)2, -1); try { NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("SylviBlossom.SmartItemSaving_OnRequestItemSync", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } internal static void OnRequestSync(ulong clientId, FastBufferReader _) { if (NetworkManager.Singleton.IsHost) { SendSyncTo(clientId); } } internal static void SendSyncTo(ulong clientId) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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) if (!NetworkManager.Singleton.IsHost) { return; } GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>().Where(IsValidObject).ToArray(); int num = Math.Min(array.Length, 999); FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(8 + 32 * num, (Allocator)2, 65536); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); for (int i = 0; i < num; i++) { NetworkObjectReference val2 = NetworkObjectReference.op_Implicit(((NetworkBehaviour)array[i]).NetworkObject); ((FastBufferWriter)(ref val)).WriteValueSafe<NetworkObjectReference>(ref val2, default(ForNetworkSerializable)); Vector3 position = ((Component)array[i]).transform.position; ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); position = ((Component)array[i]).transform.eulerAngles; ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); } Plugin.Logger.LogInfo((object)$"Item rotation sync: Sending packet\n- Buffer size: {((FastBufferWriter)(ref val)).Capacity}\n- Bytes written: {((FastBufferWriter)(ref val)).Length}\n- Objects synced: {num}"); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage("SylviBlossom.SmartItemSaving_OnReceiveItemSync", clientId, val, (NetworkDelivery)4); } catch (Exception arg) { Plugin.Logger.LogError((object)$"Error occured syncing item rotations with client: {clientId}\n{arg}"); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } internal static void OnReceiveSync(ulong _, FastBufferReader reader) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) try { if (!((FastBufferReader)(ref reader)).TryBeginRead(4)) { Plugin.Logger.LogError((object)"Item rotation sync error: Could not begin reading buffer"); return; } int num = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives)); if (!((FastBufferReader)(ref reader)).TryBeginRead(32 * num)) { Plugin.Logger.LogError((object)"Item rotation sync error: Invalid buffer size"); return; } NetworkObjectReference val = default(NetworkObjectReference); Vector3 position = default(Vector3); Vector3 eulerAngles = default(Vector3); NetworkObject val2 = default(NetworkObject); for (int i = 0; i < num; i++) { ((FastBufferReader)(ref reader)).ReadValueSafe<NetworkObjectReference>(ref val, default(ForNetworkSerializable)); ((FastBufferReader)(ref reader)).ReadValueSafe(ref position); ((FastBufferReader)(ref reader)).ReadValueSafe(ref eulerAngles); if (!((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null)) { Plugin.Logger.LogWarning((object)$"Item rotation sync: Unknown object reference {((NetworkObjectReference)(ref val)).NetworkObjectId}"); continue; } GrabbableObject component = ((Component)val2).gameObject.GetComponent<GrabbableObject>(); if ((Object)(object)component != (Object)null && IsValidObject(component)) { ApplyPositionTo(component, position); ApplyRotationTo(component, eulerAngles); } } } catch (Exception arg) { Plugin.Logger.LogError((object)$"Error occured receiving item rotation sync!\n{arg}"); } } public static void InitializeNetworkingAndSync() { //IL_0075: 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_0080: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown if (!Config.BetterSyncItems.Value) { return; } if (NetworkManager.Singleton.IsHost) { CustomMessagingManager customMessagingManager = NetworkManager.Singleton.CustomMessagingManager; object obj = <>O.<1>__OnRequestSync; if (obj == null) { HandleNamedMessageDelegate val = OnRequestSync; <>O.<1>__OnRequestSync = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("SylviBlossom.SmartItemSaving_OnRequestItemSync", (HandleNamedMessageDelegate)obj); return; } CustomMessagingManager customMessagingManager2 = NetworkManager.Singleton.CustomMessagingManager; object obj2 = <>O.<2>__OnReceiveSync; if (obj2 == null) { HandleNamedMessageDelegate val2 = OnReceiveSync; <>O.<2>__OnReceiveSync = val2; obj2 = (object)val2; } customMessagingManager2.RegisterNamedMessageHandler("SylviBlossom.SmartItemSaving_OnReceiveItemSync", (HandleNamedMessageDelegate)obj2); RequestSync(); } private static bool IsValidObject(GrabbableObject grabbableObject) { return !grabbableObject.isHeld && (Object)(object)grabbableObject.parentObject == (Object)null && grabbableObject.reachedFloorTarget; } private static void ApplyPositionTo(GrabbableObject grabbableObject, Vector3 position) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) grabbableObject.fallTime = 1f; grabbableObject.hasHitGround = true; ((Component)grabbableObject).transform.position = position; grabbableObject.targetFloorPosition = ((Component)grabbableObject).transform.localPosition; } private static void ApplyRotationTo(GrabbableObject grabbableObject, Vector3 eulerAngles) { //IL_0014: 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) //IL_002a: 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) grabbableObject.floorYRot = -1; ((Component)grabbableObject).transform.rotation = Quaternion.Euler(((Component)grabbableObject).transform.eulerAngles.x, eulerAngles.y, ((Component)grabbableObject).transform.eulerAngles.z); } } public static class FixItemFalling { public static HashSet<GrabbableObject> NeedsFixItemFalling = new HashSet<GrabbableObject>(); public static Vector3 Save(Vector3 position, GrabbableObject[] grabbableObjects, int i) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0078: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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) GrabbableObject val = grabbableObjects[i]; if (val.isHeld || (Object)(object)val.parentObject != (Object)null) { return val.GetItemFloorPosition(default(Vector3)); } if (!val.reachedFloorTarget) { Vector3 val2 = val.targetFloorPosition; if ((Object)(object)((Component)val).transform.parent != (Object)null) { val2 = ((Component)val).transform.parent.TransformPoint(val2); } return val2; } return position; } public static void PreLoad() { NeedsFixItemFalling = new HashSet<GrabbableObject>(); } public static void Load(GrabbableObject grabbableObject) { if (Config.FixItemFalling.Value) { NeedsFixItemFalling.Add(grabbableObject); } } public static void Apply(GrabbableObject grabbableObject) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)StartOfRound.Instance).IsServer && NeedsFixItemFalling.Contains(grabbableObject) && grabbableObject.itemProperties.itemSpawnsOnGround) { grabbableObject.fallTime = 1f; grabbableObject.hasHitGround = true; grabbableObject.targetFloorPosition = ((Component)grabbableObject).transform.localPosition; } } } public static class FixItemIds { public static void Save(GameNetworkManager gameNetworkManager, List<int> ids) { string[] array = new string[ids.Count]; bool[] array2 = new bool[ids.Count]; bool[] array3 = new bool[ids.Count]; for (int i = 0; i < ids.Count; i++) { int num = ids[i]; List<Item> itemsList = StartOfRound.Instance.allItemsList.itemsList; if (itemsList.Count < num || string.IsNullOrEmpty(itemsList[num].itemName)) { Plugin.Logger.LogWarning((object)$"Save | Items | No item name found for item id {num}"); array[i] = ""; array2[i] = false; array3[i] = false; continue; } Item val = itemsList[num]; string name = val.itemName; List<Item> list = itemsList.Where((Item x) => x.itemName.Equals(name, StringComparison.InvariantCultureIgnoreCase)).ToList(); int num2 = list.IndexOf(itemsList[num]); if (list.Count > 1 && num2 >= 0) { name += $"##ID{num2}"; } array[i] = name; array2[i] = val.isScrap; array3[i] = val.saveItemVariable; } ES3.Save<string[]>("SylviBlossom.SmartItemSaving_itemNames", array, gameNetworkManager.currentSaveFileName); ES3.Save<bool[]>("SylviBlossom.SmartItemSaving_itemHasValue", array2, gameNetworkManager.currentSaveFileName); ES3.Save<bool[]>("SylviBlossom.SmartItemSaving_itemHasData", array3, gameNetworkManager.currentSaveFileName); Plugin.Logger.LogInfo((object)$"Save | Items | Successfully saved {array.Length} items"); } public static void Load(StartOfRound startOfRound, int[] ids, ref int[] values, ref int[] data) { string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!General.LoadedParityCheck) { return; } if (!ES3.KeyExists("SylviBlossom.SmartItemSaving_itemNames", currentSaveFileName)) { Plugin.Logger.LogWarning((object)"Load | Items | No item name save data found, skipping item id fixing"); return; } string[] array = ES3.Load<string[]>("SylviBlossom.SmartItemSaving_itemNames", currentSaveFileName, new string[0]); bool[] array2 = ES3.Load<bool[]>("SylviBlossom.SmartItemSaving_itemHasValue", currentSaveFileName, new bool[ids.Length]); bool[] array3 = ES3.Load<bool[]>("SylviBlossom.SmartItemSaving_itemHasData", currentSaveFileName, new bool[ids.Length]); if (!Config.FixItemIds.Value) { Plugin.Logger.LogInfo((object)"Load | Items | FixItemIds is disabled, skipping item id fixing"); return; } if (Compatibility.HasLethalLevelLoader(out var pluginInfo) && !Config.ForceHandleFixItemIds.Value) { Plugin.Logger.LogInfo((object)$"Load | Items | Found mod {pluginInfo.Metadata.Name} v{pluginInfo.Metadata.Version}, skipping item id fixing"); return; } if (array.Length != ids.Length) { Plugin.Logger.LogError((object)$"Load | Items | Item count mismatch (Expected {array.Length}, got {ids.Length}), likely outdated save, skipping item id fixing"); return; } int num = 0; if (values != null) { for (int i = 0; i < values.Length; i++) { num += values[i]; } } for (int j = 0; j < ids.Length; j++) { int num2 = ids[j]; string text = array[j]; if (string.IsNullOrEmpty(text)) { Plugin.Logger.LogWarning((object)$"Load | Items | Found empty item name for item id {num2}, loading normally"); continue; } string text2 = ""; if (startOfRound.allItemsList.itemsList.Count > num2 && !string.IsNullOrEmpty(startOfRound.allItemsList.itemsList[num2].itemName)) { text2 = "as \"" + startOfRound.allItemsList.itemsList[num2].itemName + "\""; } int result = 0; bool flag = false; int num3 = text.IndexOf("##ID"); if (num3 != -1) { if (!int.TryParse(text.Substring(num3 + 4), out result)) { Plugin.Logger.LogError((object)("Load | Items | Failed to parse item name " + text)); } text = text.Substring(0, num3); flag = true; } List<int> list = new List<int>(); for (int k = 0; k < startOfRound.allItemsList.itemsList.Count; k++) { Item val = startOfRound.allItemsList.itemsList[k]; if (val.itemName.Equals(text, StringComparison.InvariantCultureIgnoreCase)) { list.Add(k); } } if (list.Count == 0) { if (Config.RemoveIfNotFound.Value) { Plugin.Logger.LogWarning((object)("Load | Items | No item id found for item \"" + text + "\", removing item")); ids[j] = int.MaxValue; } else { Plugin.Logger.LogWarning((object)$"Load | Items | No item id found for item \"{text}\", loading normally with id {num2} {text2}"); } continue; } if (list.Count <= result) { Plugin.Logger.LogWarning((object)$"Load | Items | Saved {num2} as \"{text}\" #{result + 1}, but only found {list.Count} name duplicates, loading as #{list.Count}"); result = list.Count - 1; } if (list.Count > 1 && !flag) { string text3 = string.Join(",", list); if (list.Contains(num2)) { Plugin.Logger.LogWarning((object)$"Load | Items | Multiple ids ({text3}) found for item \"{text}\", loading normally with id {num2} {text2}"); continue; } Plugin.Logger.LogWarning((object)$"Load | Items | Multiple ids ({text3}) found for item \"{text}\", arbitrarily loading {list[0]}"); } if (num2 != list[result]) { if (num2 < startOfRound.allItemsList.itemsList.Count && num2 >= 0) { Plugin.Logger.LogInfo((object)$"Load | Items | Fixed item mismatch ({num2}, \"{startOfRound.allItemsList.itemsList[num2].itemName}\" -> {list[result]}, \"{text}\")"); } else { Plugin.Logger.LogInfo((object)$"Load | Items | Fixed item mismatch ({num2}, unknown -> {list[result]}, \"{text}\")"); } ids[j] = list[result]; } } List<int> list2 = new List<int>(); List<int> list3 = new List<int>(); int num4 = 0; int num5 = 0; int num6 = 0; for (int l = 0; l < ids.Length; l++) { if (ids[l] < startOfRound.allItemsList.itemsList.Count) { Item val2 = startOfRound.allItemsList.itemsList[ids[l]]; if (val2.isScrap) { if (array2[l]) { list2.Add(values[num4]); num6 += values[num4]; } else { int num7 = (int)((float)Random.Range(val2.minValue, val2.maxValue - 1) * RoundManager.Instance.scrapValueMultiplier); list2.Add(num7); num6 += num7; Plugin.Logger.LogWarning((object)$"Load | Items | Assigning random value of {num7} to \"{val2.itemName}\""); } } if (val2.saveItemVariable) { if (array3[l]) { list3.Add(data[num5]); } else { list3.Add(0); Plugin.Logger.LogWarning((object)("Load | Items | Loaded item \"" + val2.itemName + "\" without its associated save data")); } } } if (array2[l]) { num4++; } if (array3[l]) { num5++; } } string text4 = (num6 - num).ToString(); if (num6 > num) { text4 = "+" + text4; } Plugin.Logger.LogInfo((object)$"Load | Items | Loaded {array.Length} items with a total value of {num6} ({text4})"); values = list2.ToArray(); data = list3.ToArray(); } } public static class FixItemRotation { public static Vector3[] LoadedItemRotations; public static Dictionary<GrabbableObject, Vector3> NeedsItemRotation = new Dictionary<GrabbableObject, Vector3>(); public static List<Vector3> SavedItemRotations; public static void PreSave() { SavedItemRotations = new List<Vector3>(); } public static void Save(GrabbableObject[] grabbableObjects, int i) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) GrabbableObject val = grabbableObjects[i]; SavedItemRotations.Add(((Component)val).transform.eulerAngles); } public static void PostSave(GameNetworkManager gameNetworkManager) { if (SavedItemRotations != null) { ES3.Save<Vector3[]>("SylviBlossom.SmartItemSaving_itemRotations", SavedItemRotations.ToArray(), gameNetworkManager.currentSaveFileName); } } public static void PreLoad(int[] ids) { LoadedItemRotations = null; NeedsItemRotation = new Dictionary<GrabbableObject, Vector3>(); string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!General.LoadedParityCheck) { return; } if (!Config.SaveItemRotation.Value) { Plugin.Logger.LogInfo((object)"Load | Items | SaveItemRotation is disabled, skipping load item rotation"); return; } if (Compatibility.HasSaveItemRotations(out var pluginInfo) && !Config.ForceHandleSaveItemRotation.Value) { Plugin.Logger.LogInfo((object)$"Load | Items | Found mod {pluginInfo.Metadata.Name} v{pluginInfo.Metadata.Version}, skipping load item rotation"); return; } if (!ES3.KeyExists("SylviBlossom.SmartItemSaving_itemRotations", currentSaveFileName)) { Plugin.Logger.LogWarning((object)"Load | Items | No item rotation save data found, skipping load item rotation"); return; } LoadedItemRotations = ES3.Load<Vector3[]>("SylviBlossom.SmartItemSaving_itemRotations", currentSaveFileName); if (LoadedItemRotations.Length != ids.Length) { Plugin.Logger.LogError((object)$"Load | Items | Item count mismatch (Expected {LoadedItemRotations.Length}, got {ids.Length}), likely outdated save, skipping load item rotation"); LoadedItemRotations = null; } } public static void Load(int i, GrabbableObject grabbableObject) { //IL_0040: 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) if (LoadedItemRotations != null) { if (i >= LoadedItemRotations.Length) { Plugin.Logger.LogError((object)"Load | Items | Item index outside bounds of saved rotations, this shouldn't happen"); return; } NeedsItemRotation.Add(grabbableObject, LoadedItemRotations[i]); ApplyRotationTo(grabbableObject, LoadedItemRotations[i]); } } public static void Apply(GrabbableObject grabbableObject) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)grabbableObject).IsServer && NeedsItemRotation.TryGetValue(grabbableObject, out var value)) { ApplyRotationTo(grabbableObject, value); NeedsItemRotation.Remove(grabbableObject); } } private static void ApplyRotationTo(GrabbableObject grabbableObject, Vector3 eulerAngles) { //IL_0014: 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) //IL_002a: 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) grabbableObject.floorYRot = -1; ((Component)grabbableObject).transform.rotation = Quaternion.Euler(((Component)grabbableObject).transform.eulerAngles.x, eulerAngles.y, ((Component)grabbableObject).transform.eulerAngles.z); } } public static class FixUnlockIds { public static void SaveFixUnlockIds(GameNetworkManager gameNetworkManager, List<int> unlocks) { string[] array = new string[unlocks.Count]; for (int i = 0; i < unlocks.Count; i++) { array[i] = StartOfRound.Instance.unlockablesList.unlockables[unlocks[i]].unlockableName; } ES3.Save<string[]>("SylviBlossom.SmartItemSaving_unlockNames", array, gameNetworkManager.currentSaveFileName); Plugin.Logger.LogInfo((object)$"Save | Unlockables | Successfully saved {array.Length} unlocks"); } public static void LoadFixUnlockIds(StartOfRound startOfRound, ref int[] unlocks) { string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!General.LoadedParityCheck) { return; } if (!Config.FixItemIds.Value) { Plugin.Logger.LogInfo((object)"Load | Unlockables | FixItemIds is disabled, skipping unlockable id fixing"); return; } if (!ES3.KeyExists("SylviBlossom.SmartItemSaving_unlockNames", currentSaveFileName)) { Plugin.Logger.LogWarning((object)"Load | Unlockables | No unlock name save data found, skipping unlockable id fixing"); return; } string[] array = ES3.Load<string[]>("SylviBlossom.SmartItemSaving_unlockNames", currentSaveFileName, new string[0]); if (array.Length != unlocks.Length) { Plugin.Logger.LogError((object)$"Load | Unlockables | Unlocks count mismatch (Expected {array.Length}, got {unlocks.Length}), likely outdated save, skipping unlockable id fixing"); return; } List<int> list = new List<int>(); for (int i = 0; i < array.Length; i++) { string text = array[i]; int num = unlocks[i]; if (string.IsNullOrEmpty(text)) { Plugin.Logger.LogWarning((object)$"Load | Unlockables | Found empty unlock name for unlock id {num}, loading normally"); list.Add(num); continue; } string text2 = ""; if (startOfRound.unlockablesList.unlockables.Count > num && !string.IsNullOrEmpty(startOfRound.unlockablesList.unlockables[num].unlockableName)) { text2 = "as \"" + startOfRound.unlockablesList.unlockables[num].unlockableName + "\""; } List<int> list2 = new List<int>(); for (int j = 0; j < startOfRound.unlockablesList.unlockables.Count; j++) { UnlockableItem val = startOfRound.unlockablesList.unlockables[j]; if (val.unlockableName.Equals(text, StringComparison.InvariantCultureIgnoreCase)) { list2.Add(j); } } if (list2.Count == 0) { if (Config.RemoveIfNotFound.Value) { Plugin.Logger.LogWarning((object)("Load | Unlockables | No unlock id found for unlock \"" + text + "\", removing item")); continue; } Plugin.Logger.LogWarning((object)$"Load | Unlockables | No unlock id found for unlock \"{text}\", loading normally with id {num} {text2}"); list.Add(num); continue; } if (list2.Count > 1) { string text3 = string.Join(",", list2); if (list2.Contains(num)) { Plugin.Logger.LogWarning((object)$"Load | Unlockables | Multiple ids ({text3}) found for unlock \"{text}\", loading normally with id {num} {text2}"); list.Add(num); continue; } Plugin.Logger.LogWarning((object)$"Load | Unlockables | Multiple ids ({text3}) found for unlock \"{text}\", arbitrarily loading {list2[0]}"); } if (num != list2[0]) { if (num < startOfRound.unlockablesList.unlockables.Count && num >= 0) { Plugin.Logger.LogInfo((object)$"Load | Unlockables | Fixed unlock mismatch ({num}, \"{startOfRound.unlockablesList.unlockables[num].unlockableName}\" -> {list2[0]}, \"{text}\")"); } else { Plugin.Logger.LogInfo((object)$"Load | Unlockables | Fixed unlock mismatch ({num}, unknown -> {list2[0]}, \"{text}\")"); } list.Add(list2[0]); } else { list.Add(num); } } unlocks = list.ToArray(); Plugin.Logger.LogInfo((object)$"Load | Unlockables | Loaded {list.Count} unlocks"); } } public static class General { public static int LoadedFormatVersion; public static bool LoadedParityCheck; public static void SaveInitialValues(GameNetworkManager gameNetworkManager) { ES3.Save<int>("SylviBlossom.SmartItemSaving_formatVersion", 2, gameNetworkManager.currentSaveFileName); ES3.Save<int>("SylviBlossom.SmartItemSaving_parityStepsTaken", StartOfRound.Instance.gameStats.allStepsTaken, gameNetworkManager.currentSaveFileName); } public static void LoadInitialValues(StartOfRound startOfRound) { string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!ES3.KeyExists("SylviBlossom.SmartItemSaving_formatVersion", currentSaveFileName)) { LoadedFormatVersion = 0; LoadedParityCheck = false; Plugin.Logger.LogWarning((object)"Load | General | No SmartItemSaving save data found, skipping all"); return; } LoadedFormatVersion = ES3.Load<int>("SylviBlossom.SmartItemSaving_formatVersion", currentSaveFileName, 2); int num = ES3.Load<int>("SylviBlossom.SmartItemSaving_parityStepsTaken", currentSaveFileName, startOfRound.gameStats.allStepsTaken); if (num != startOfRound.gameStats.allStepsTaken) { LoadedParityCheck = false; Plugin.Logger.LogWarning((object)$"Load | General | Steps Taken mismatch (Expected {num}, got {startOfRound.gameStats.allStepsTaken}), likely outdated save, skipping all"); } else { LoadedParityCheck = true; } } public static void LoadCreateBackup() { string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (Config.BackupOnLoad.Value && ES3.FileExists(currentSaveFileName)) { Plugin.Logger.LogInfo((object)"Load | General | Creating save backup"); ES3.CreateBackup(currentSaveFileName); } } public static void DeleteItemKeys(GameNetworkManager gameNetworkManager) { ES3.DeleteKey("SylviBlossom.SmartItemSaving_itemNames", gameNetworkManager.currentSaveFileName); ES3.DeleteKey("SylviBlossom.SmartItemSaving_itemHasValue", gameNetworkManager.currentSaveFileName); ES3.DeleteKey("SylviBlossom.SmartItemSaving_itemHasData", gameNetworkManager.currentSaveFileName); ES3.DeleteKey("SylviBlossom.SmartItemSaving_itemRotations", gameNetworkManager.currentSaveFileName); } } }