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 SaveItemRotations v1.0.0
moe.sylvi.SaveItemRotations.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.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using IL; using IL.GameNetcodeStuff; using LethalNetworkAPI; using Microsoft.CodeAnalysis; using Mono.Cecil.Cil; using MonoMod.Cil; using On; using On.GameNetcodeStuff; using Unity.Netcode; using UnityEngine; using moe.sylvi.SaveItemRotations.Features; using moe.sylvi.SaveItemRotations.NetcodePatcher; [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: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: AssemblyCompany("moe.sylvi.SaveItemRotations")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+db532e148358b75214ef624b9188e57c19c63e50")] [assembly: AssemblyProduct("SaveItemRotations")] [assembly: AssemblyTitle("moe.sylvi.SaveItemRotations")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [module: NetcodePatchedAssembly] internal class <Module> { static <Module>() { } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace moe.sylvi.SaveItemRotations { public class Config { public static Config Instance { get; internal set; } public ConfigEntry<bool> SyncOnLoad { get; internal set; } public Config(ConfigFile cfg) { Instance = this; SyncOnLoad = cfg.Bind<bool>("General", "SyncOnLoad", true, "Whether to sync item rotations to clients when they join the game. Should only be disabled if it causes issues."); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("moe.sylvi.SaveItemRotations", "SaveItemRotations", "1.0.0")] public class Plugin : BaseUnityPlugin { public const int FormatVersion = 1; public static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } private void Awake() { Logger = ((BaseUnityPlugin)this).Logger; Instance = this; new Config(((BaseUnityPlugin)this).Config); Common.Patches.Initialize(); FixItemDrop.Patches.Initialize(); SaveRotations.Patches.Initialize(); SyncRotations.Patches.Initialize(); NetcodePatcher(); Logger.LogInfo((object)"moe.sylvi.SaveItemRotations v1.0.0 has loaded!"); } private void NetcodePatcher() { Type[] types = Assembly.GetExecutingAssembly().GetTypes(); Type[] array = types; foreach (Type type in array) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic); MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo in array2) { object[] customAttributes = methodInfo.GetCustomAttributes(typeof(RuntimeInitializeOnLoadMethodAttribute), inherit: false); if (customAttributes.Length != 0) { methodInfo.Invoke(null, null); } } } } } public static class SaveKeys { public const string FormatVersion = "moe.sylvi.SaveItemRotations_formatVersion"; public const string ParityStepsTaken = "moe.sylvi.SaveItemRotations_parityStepsTaken"; public const string ItemRotations = "moe.sylvi.SaveItemRotations_itemRotations"; } public static class MyPluginInfo { public const string PLUGIN_GUID = "moe.sylvi.SaveItemRotations"; public const string PLUGIN_NAME = "SaveItemRotations"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace moe.sylvi.SaveItemRotations.Features { public static class Common { public static class Patches { [CompilerGenerated] private static class <>O { public static hook_SaveItemsInShip <0>__GameNetworkManager_SaveItemsInShip; public static hook_SetTimeAndPlanetToSavedSettings <1>__StartOfRound_SetTimeAndPlanetToSavedSettings; } 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 object obj = <>O.<0>__GameNetworkManager_SaveItemsInShip; if (obj == null) { hook_SaveItemsInShip val = GameNetworkManager_SaveItemsInShip; <>O.<0>__GameNetworkManager_SaveItemsInShip = val; obj = (object)val; } GameNetworkManager.SaveItemsInShip += (hook_SaveItemsInShip)obj; object obj2 = <>O.<1>__StartOfRound_SetTimeAndPlanetToSavedSettings; if (obj2 == null) { hook_SetTimeAndPlanetToSavedSettings val2 = StartOfRound_SetTimeAndPlanetToSavedSettings; <>O.<1>__StartOfRound_SetTimeAndPlanetToSavedSettings = val2; obj2 = (object)val2; } StartOfRound.SetTimeAndPlanetToSavedSettings += (hook_SetTimeAndPlanetToSavedSettings)obj2; } private static void GameNetworkManager_SaveItemsInShip(orig_SaveItemsInShip orig, GameNetworkManager self) { if (!StartOfRound.Instance.isChallengeFile) { SaveInitialValues(self); } orig.Invoke(self); } private static void StartOfRound_SetTimeAndPlanetToSavedSettings(orig_SetTimeAndPlanetToSavedSettings orig, StartOfRound self) { orig.Invoke(self); LoadInitialValues(self); } } public static int LoadedFormatVersion; public static bool LoadedParityCheck; public static void SaveInitialValues(GameNetworkManager gameNetworkManager) { ES3.Save<int>("moe.sylvi.SaveItemRotations_formatVersion", 1, gameNetworkManager.currentSaveFileName); ES3.Save<int>("moe.sylvi.SaveItemRotations_parityStepsTaken", StartOfRound.Instance.gameStats.allStepsTaken, gameNetworkManager.currentSaveFileName); } public static void LoadInitialValues(StartOfRound startOfRound) { string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!ES3.KeyExists("moe.sylvi.SaveItemRotations_formatVersion", currentSaveFileName)) { LoadedFormatVersion = 0; LoadedParityCheck = false; Plugin.Logger.LogWarning((object)"Load | No SaveItemRotations save data found, skipping all"); return; } LoadedFormatVersion = ES3.Load<int>("moe.sylvi.SaveItemRotations_formatVersion", currentSaveFileName, 1); int num = ES3.Load<int>("moe.sylvi.SaveItemRotations_parityStepsTaken", currentSaveFileName, startOfRound.gameStats.allStepsTaken); if (num != startOfRound.gameStats.allStepsTaken) { LoadedParityCheck = false; Plugin.Logger.LogWarning((object)$"Load | Steps Taken mismatch (Expected {num}, got {startOfRound.gameStats.allStepsTaken}), likely outdated save, skipping all"); } else { LoadedParityCheck = true; } } } public static class FixItemDrop { public static class Patches { [CompilerGenerated] private static class <>O { public static Manipulator <0>__PlayerControllerB_ThrowObjectClientRpc; public static Func<int, int, int> <1>__Apply; } 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 object obj = <>O.<0>__PlayerControllerB_ThrowObjectClientRpc; if (obj == null) { Manipulator val = PlayerControllerB_ThrowObjectClientRpc; <>O.<0>__PlayerControllerB_ThrowObjectClientRpc = val; obj = (object)val; } PlayerControllerB.ThrowObjectClientRpc += (Manipulator)obj; } 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_0042: 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) })) { val.Emit(OpCodes.Ldarg, 5); val.EmitDelegate<Func<int, int, int>>((Func<int, int, int>)Apply); } } } public static int Apply(int orig, int actualRotation) { return actualRotation; } } public static class SaveRotations { public static class Patches { [CompilerGenerated] private static class <>O { public static Manipulator <0>__GameNetworkManager_SaveItemsInShip; public static Manipulator <1>__StartOfRound_LoadShipGrabbableItems; public static hook_Update <2>__GrabbableObject_Update; public static Action <3>__PreSave; public static Action<GrabbableObject[], int> <4>__Save; public static Action<GameNetworkManager> <5>__PostSave; public static Action<int[]> <6>__PreLoad; public static Action<int, GrabbableObject> <7>__Load; } 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 object obj = <>O.<0>__GameNetworkManager_SaveItemsInShip; if (obj == null) { Manipulator val = GameNetworkManager_SaveItemsInShip; <>O.<0>__GameNetworkManager_SaveItemsInShip = val; obj = (object)val; } GameNetworkManager.SaveItemsInShip += (Manipulator)obj; object obj2 = <>O.<1>__StartOfRound_LoadShipGrabbableItems; if (obj2 == null) { Manipulator val2 = StartOfRound_LoadShipGrabbableItems; <>O.<1>__StartOfRound_LoadShipGrabbableItems = val2; obj2 = (object)val2; } StartOfRound.LoadShipGrabbableItems += (Manipulator)obj2; object obj3 = <>O.<2>__GrabbableObject_Update; if (obj3 == null) { hook_Update val3 = GrabbableObject_Update; <>O.<2>__GrabbableObject_Update = val3; obj3 = (object)val3; } GrabbableObject.Update += (hook_Update)obj3; } 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_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_02e9: 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; } 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; } Instruction next = val.Next; 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; } Instruction next2 = val.Next; 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; } Instruction next3 = val.Next; val.Goto(next, (MoveType)0, false); val.EmitDelegate<Action>((Action)PreSave); val.Goto(next2, (MoveType)0, false); val.Emit(OpCodes.Ldloc, grabbableObjectsLoc); val.Emit(OpCodes.Ldloc, iLoc); val.EmitDelegate<Action<GrabbableObject[], int>>((Action<GrabbableObject[], int>)Save); val.Goto(next3, (MoveType)0, false); val.Emit(OpCodes.Ldarg_0); val.EmitDelegate<Action<GameNetworkManager>>((Action<GameNetworkManager>)PostSave); } 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_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0203: 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)2, 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; } Instruction next = val.Next; 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; } Instruction next2 = val.Next; val.Goto(next, (MoveType)0, false); val.Emit(OpCodes.Ldloc, idsLoc); val.EmitDelegate<Action<int[]>>((Action<int[]>)PreLoad); val.Goto(next2, (MoveType)0, false); val.Emit(OpCodes.Ldloc, iLoc); val.Emit(OpCodes.Ldloc, grabbableObjectLoc); val.EmitDelegate<Action<int, GrabbableObject>>((Action<int, GrabbableObject>)Load); } private static void GrabbableObject_Update(orig_Update orig, GrabbableObject self) { orig.Invoke(self); Apply(self); } } 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_0016: 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[]>("moe.sylvi.SaveItemRotations_itemRotations", SavedItemRotations.ToArray(), gameNetworkManager.currentSaveFileName); } } public static void PreLoad(int[] ids) { LoadedItemRotations = null; NeedsItemRotation = new Dictionary<GrabbableObject, Vector3>(); string currentSaveFileName = GameNetworkManager.Instance.currentSaveFileName; if (!Common.LoadedParityCheck) { return; } if (!ES3.KeyExists("moe.sylvi.SaveItemRotations_itemRotations", currentSaveFileName)) { Plugin.Logger.LogWarning((object)"Load | No item rotation save data found, skipping load item rotation"); return; } LoadedItemRotations = ES3.Load<Vector3[]>("moe.sylvi.SaveItemRotations_itemRotations", currentSaveFileName); if (LoadedItemRotations.Length != ids.Length) { Plugin.Logger.LogError((object)$"Load | 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 | 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 SyncRotations { public class ItemData { public NetworkObjectReference NetworkObject; public Vector3 EulerAngles; } public static class Patches { [CompilerGenerated] private static class <>O { public static hook_ConnectClientToPlayerObject <0>__PlayerControllerB_ConnectClientToPlayerObject; } 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 object obj = <>O.<0>__PlayerControllerB_ConnectClientToPlayerObject; if (obj == null) { hook_ConnectClientToPlayerObject val = PlayerControllerB_ConnectClientToPlayerObject; <>O.<0>__PlayerControllerB_ConnectClientToPlayerObject = val; obj = (object)val; } PlayerControllerB.ConnectClientToPlayerObject += (hook_ConnectClientToPlayerObject)obj; } private static void PlayerControllerB_ConnectClientToPlayerObject(orig_ConnectClientToPlayerObject orig, PlayerControllerB self) { orig.Invoke(self); InitializeNetworkingAndSync(); } } public static LNetworkEvent RequestSyncEvent = LNetworkEvent.Connect("RequestItemSync", (Action<ulong>)OnRequestSync, (Action)null, (Action<ulong>)null); public static LNetworkMessage<List<ItemData>> SyncItemMessage = LNetworkMessage<List<ItemData>>.Connect("SyncItemData", (Action<List<ItemData>, ulong>)null, (Action<List<ItemData>>)OnReceiveSync, (Action<List<ItemData>, ulong>)null); private static void OnReceiveSync(List<ItemData> dataList) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (!Config.Instance.SyncOnLoad.Value) { Plugin.Logger.LogInfo((object)"Sync | Got item sync from server but SyncOnLoad is disabled, ignoring"); return; } Plugin.Logger.LogInfo((object)$"Sync | Got item sync from server with {dataList.Count} object(s)"); NetworkObject val = default(NetworkObject); foreach (ItemData data in dataList) { if (!((NetworkObjectReference)(ref data.NetworkObject)).TryGet(ref val, (NetworkManager)null)) { Plugin.Logger.LogWarning((object)$"Sync | Unknown object reference {((NetworkObjectReference)(ref data.NetworkObject)).NetworkObjectId}"); Plugin.Logger.LogWarning((object)$"Sync | - Supplied rotation: {data.EulerAngles}"); continue; } GrabbableObject component = ((Component)val).gameObject.GetComponent<GrabbableObject>(); if ((Object)(object)component != (Object)null && IsValidObject(component)) { ApplyRotationTo(component, data.EulerAngles); } else { Plugin.Logger.LogWarning((object)$"Sync | Attempted to sync invalid item {((NetworkObjectReference)(ref data.NetworkObject)).NetworkObjectId}"); } } } private static void OnRequestSync(ulong clientId) { if (!Config.Instance.SyncOnLoad.Value) { Plugin.Logger.LogInfo((object)$"Sync | Got item sync request from client {clientId} but SyncOnLoad is disabled, ignoring"); return; } Plugin.Logger.LogInfo((object)$"Sync | Got item sync request from client {clientId}"); IEnumerable<ItemData> source = from grabbableObject in Object.FindObjectsOfType<GrabbableObject>().Where(IsValidObject) select new ItemData { NetworkObject = NetworkObjectReference.op_Implicit(((NetworkBehaviour)grabbableObject).NetworkObject), EulerAngles = ((Component)grabbableObject).transform.eulerAngles }; SyncItemMessage.SendClient(source.ToList(), clientId); } public static void InitializeNetworkingAndSync() { if (!NetworkManager.Singleton.IsHost) { if (!Config.Instance.SyncOnLoad.Value) { Plugin.Logger.LogInfo((object)"Sync | SyncOnLoad is disabled, skipping item sync"); return; } Plugin.Logger.LogInfo((object)"Sync | Requesting item sync"); RequestSyncEvent.InvokeServer(); } } private static bool IsValidObject(GrabbableObject grabbableObject) { return !grabbableObject.isHeld && (Object)(object)grabbableObject.parentObject == (Object)null && grabbableObject.reachedFloorTarget; } 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); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } } namespace moe.sylvi.SaveItemRotations.NetcodePatcher { [AttributeUsage(AttributeTargets.Module)] internal class NetcodePatchedAssemblyAttribute : Attribute { } }