From 43fc3c8ff034d736adb32b52ef1f383593ac4548 Mon Sep 17 00:00:00 2001 From: tornac1234 <24827220+tornac1234@users.noreply.github.com> Date: Sun, 21 Jan 2024 13:39:22 +0100 Subject: [PATCH 1/2] Fix "spawn" command and sync "sub" command --- ...nsoleCommand_OnConsoleCommand_PatchTest.cs | 20 ++++++++ ...eCommand_OnConsoleCommand_sub_PatchTest.cs | 20 ++++++++ NitroxClient/GameLogic/MobileVehicleBay.cs | 5 +- NitroxClient/GameLogic/NitroxConsole.cs | 36 ++++++++++----- NitroxClient/GameLogic/Vehicles.cs | 13 +++++- ...wnConsoleCommand_OnConsoleCommand_Patch.cs | 46 ++++++++----------- ...nsoleCommand_OnConsoleCommand_sub_Patch.cs | 38 +++++++++++---- 7 files changed, 125 insertions(+), 53 deletions(-) create mode 100644 Nitrox.Test/Patcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_PatchTest.cs create mode 100644 Nitrox.Test/Patcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_PatchTest.cs diff --git a/Nitrox.Test/Patcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_PatchTest.cs b/Nitrox.Test/Patcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_PatchTest.cs new file mode 100644 index 0000000000..014b1b9750 --- /dev/null +++ b/Nitrox.Test/Patcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_PatchTest.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using HarmonyLib; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NitroxTest.Patcher; + +namespace NitroxPatcher.Patches.Dynamic; + +[TestClass] +public class SpawnConsoleCommand_OnConsoleCommand_PatchTest +{ + [TestMethod] + public void Sanity() + { + IEnumerable originalIl = PatchTestHelper.GetInstructionsFromMethod(SpawnConsoleCommand_OnConsoleCommand_Patch.TARGET_METHOD); + IEnumerable transformedIl = SpawnConsoleCommand_OnConsoleCommand_Patch.Transpiler(originalIl); + transformedIl.Count().Should().Be(originalIl.Count() + 2); + } +} diff --git a/Nitrox.Test/Patcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_PatchTest.cs b/Nitrox.Test/Patcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_PatchTest.cs new file mode 100644 index 0000000000..c2b8ddb679 --- /dev/null +++ b/Nitrox.Test/Patcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_PatchTest.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using HarmonyLib; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NitroxTest.Patcher; + +namespace NitroxPatcher.Patches.Dynamic; + +[TestClass] +public class SubConsoleCommand_OnConsoleCommand_sub_PatchTest +{ + [TestMethod] + public void Sanity() + { + IEnumerable originalIl = PatchTestHelper.GetInstructionsFromMethod(SubConsoleCommand_OnConsoleCommand_sub_Patch.TARGET_METHOD); + IEnumerable transformedIl = SubConsoleCommand_OnConsoleCommand_sub_Patch.Transpiler(originalIl); + transformedIl.Count().Should().Be(originalIl.Count()); + } +} diff --git a/NitroxClient/GameLogic/MobileVehicleBay.cs b/NitroxClient/GameLogic/MobileVehicleBay.cs index 16c6456ce7..6a33c16586 100644 --- a/NitroxClient/GameLogic/MobileVehicleBay.cs +++ b/NitroxClient/GameLogic/MobileVehicleBay.cs @@ -1,10 +1,8 @@ using NitroxClient.Communication.Abstract; -using NitroxClient.GameLogic.Helper; using NitroxClient.MonoBehaviours; using NitroxModel.DataStructures; using NitroxModel.DataStructures.GameLogic.Entities; using NitroxModel.Packets; -using NitroxModel_Subnautica.DataStructures; using UnityEngine; namespace NitroxClient.GameLogic; @@ -42,8 +40,7 @@ public void BeginCrafting(ConstructorInput constructor, GameObject constructedOb NitroxId constructedObjectId = NitroxEntity.GenerateNewId(constructedObject); - VehicleWorldEntity vehicleEntity = new(constructorId, DayNightCycle.main.timePassedAsFloat, constructedObject.transform.ToLocalDto(), string.Empty, false, constructedObjectId, techType.ToDto(), null); - VehicleChildEntityHelper.PopulateChildren(constructedObjectId, constructedObject.GetFullHierarchyPath(), vehicleEntity.ChildEntities, constructedObject); + VehicleWorldEntity vehicleEntity = Vehicles.MakeVehicleEntity(constructedObject, constructedObjectId, techType, constructorId); packetSender.Send(new EntitySpawnedByClient(vehicleEntity)); diff --git a/NitroxClient/GameLogic/NitroxConsole.cs b/NitroxClient/GameLogic/NitroxConsole.cs index 903a14938a..acfb93ae40 100644 --- a/NitroxClient/GameLogic/NitroxConsole.cs +++ b/NitroxClient/GameLogic/NitroxConsole.cs @@ -1,11 +1,9 @@ using System; using NitroxClient.Communication.Abstract; -using NitroxClient.GameLogic.Helper; using NitroxClient.MonoBehaviours; using NitroxModel.DataStructures; using NitroxModel.DataStructures.GameLogic.Entities; using NitroxModel.Packets; -using NitroxModel_Subnautica.DataStructures; using NitroxModel_Subnautica.Helper; using UnityEngine; @@ -27,13 +25,13 @@ public NitroxConsole(IPacketSender packetSender, Items item) //List of things that can be spawned : https://subnauticacommands.com/items public void Spawn(GameObject gameObject) { - TechType techType = CraftData.GetTechType(gameObject); + TechType techType = GetObjectTechType(gameObject); try { if (VehicleHelper.IsVehicle(techType)) { - SpawnVehicle(gameObject); + SpawnVehicle(gameObject, techType); } else { @@ -48,20 +46,17 @@ public void Spawn(GameObject gameObject) } /// - /// Spawns a Seamoth or an Exosuit + /// Spawns Seamoth, Exosuit and Cyclops /// - private void SpawnVehicle(GameObject gameObject) + private void SpawnVehicle(GameObject gameObject, TechType techType) { - TechType techType = CraftData.GetTechType(gameObject); - NitroxId id = NitroxEntity.GetIdOrGenerateNew(gameObject); - VehicleWorldEntity vehicleEntity = new VehicleWorldEntity(null, DayNightCycle.main.timePassedAsFloat, gameObject.transform.ToLocalDto(), "", false, id, techType.ToDto(), null); - VehicleChildEntityHelper.PopulateChildren(id, gameObject.GetFullHierarchyPath(), vehicleEntity.ChildEntities, gameObject); - + VehicleWorldEntity vehicleEntity = Vehicles.MakeVehicleEntity(gameObject, id, techType); + packetSender.Send(new EntitySpawnedByClient(vehicleEntity)); - Log.Debug($"Spawning vehicle {techType} with id {techType} at {gameObject.transform.position}"); + Log.Debug($"Spawning vehicle {techType} with id {id} at {gameObject.transform.position}"); } /// @@ -75,5 +70,22 @@ private void SpawnItem(GameObject gameObject) item.Dropped(gameObject, pickupable.GetTechType()); } } + + private static TechType GetObjectTechType(GameObject gameObject) + { + TechType techType = CraftData.GetTechType(gameObject); + if (techType != TechType.None) + { + return techType; + } + + // Cyclops' GameObject doesn't have a way to give its a TechType so we detect it differently + if (gameObject.TryGetComponent(out SubRoot subRoot) && subRoot.isCyclops) + { + return TechType.Cyclops; + } + + return TechType.None; + } } } diff --git a/NitroxClient/GameLogic/Vehicles.cs b/NitroxClient/GameLogic/Vehicles.cs index ea0fd0d530..ba31024c32 100644 --- a/NitroxClient/GameLogic/Vehicles.cs +++ b/NitroxClient/GameLogic/Vehicles.cs @@ -1,14 +1,16 @@ using System.Collections; using NitroxClient.Communication; using NitroxClient.Communication.Abstract; +using NitroxClient.GameLogic.Helper; using NitroxClient.MonoBehaviours; using NitroxClient.Unity.Helper; -using NitroxModel_Subnautica.DataStructures; -using NitroxModel_Subnautica.DataStructures.GameLogic; using NitroxModel.DataStructures; using NitroxModel.DataStructures.GameLogic; +using NitroxModel.DataStructures.GameLogic.Entities; using NitroxModel.DataStructures.Util; using NitroxModel.Packets; +using NitroxModel_Subnautica.DataStructures; +using NitroxModel_Subnautica.DataStructures.GameLogic; using UnityEngine; namespace NitroxClient.GameLogic; @@ -232,4 +234,11 @@ public static void RemoveNitroxEntitiesTagging(GameObject constructedObject) UnityEngine.Object.DestroyImmediate(nitroxEntity); } } + + public static VehicleWorldEntity MakeVehicleEntity(GameObject constructedObject, NitroxId constructedObjectId, TechType techType, NitroxId constructorId = null) + { + VehicleWorldEntity vehicleEntity = new(constructorId, DayNightCycle.main.timePassedAsFloat, constructedObject.transform.ToLocalDto(), string.Empty, false, constructedObjectId, techType.ToDto(), null); + VehicleChildEntityHelper.PopulateChildren(constructedObjectId, constructedObject.GetFullHierarchyPath(), vehicleEntity.ChildEntities, constructedObject); + return vehicleEntity; + } } diff --git a/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs b/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs index 4bfffc018e..c7133fd69b 100644 --- a/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; @@ -11,33 +10,26 @@ namespace NitroxPatcher.Patches.Dynamic; public sealed partial class SpawnConsoleCommand_OnConsoleCommand_Patch : NitroxPatch, IDynamicPatch { - private static readonly MethodInfo TARGET_METHOD = Reflect.Method((SpawnConsoleCommand t) => t.OnConsoleCommand_spawn(default(NotificationCenter.Notification))); - - private static readonly OpCode INJECTION_CODE = OpCodes.Call; - private static readonly object INJECTION_OPERAND = Reflect.Method(() => Utils.CreatePrefab(default(GameObject), default(float), default(bool))); - - public static IEnumerable Transpiler(MethodBase original, IEnumerable instructions) + internal static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method((SpawnConsoleCommand t) => t.SpawnAsync(default))); + + /* + * GameObject gameObject = global::Utils.CreatePrefab(prefabForTechType, maxDist, i > 0); + * -> SpawnConsoleCommand_OnConsoleCommand_Patch.Callback(gameObject); + * LargeWorldEntity.Register(gameObject); + * CrafterLogic.NotifyCraftEnd(gameObject, techType); + * gameObject.SendMessage("StartConstruction", SendMessageOptions.DontRequireReceiver); + */ + public static IEnumerable Transpiler(IEnumerable instructions) { - Validate.NotNull(INJECTION_OPERAND); - - foreach (CodeInstruction instruction in instructions) - { - yield return instruction; - - /* - * GameObject gameObject = global::Utils.CreatePrefab(prefabForTechType, maxDist, i > 0); - * -> SpawnConsoleCommand_OnConsoleCommand_Patch.Callback(gameObject); - * LargeWorldEntity.Register(gameObject); - * CrafterLogic.NotifyCraftEnd(gameObject, techType); - * gameObject.SendMessage("StartConstruction", SendMessageOptions.DontRequireReceiver); - */ - if (instruction.opcode == INJECTION_CODE && instruction.operand.Equals(INJECTION_OPERAND)) - { - yield return new CodeInstruction(OpCodes.Dup); - yield return new CodeInstruction(OpCodes.Call, ((Action)Callback).Method); - } - - } + return new CodeMatcher(instructions).MatchStartForward([ + new CodeMatch(OpCodes.Call, Reflect.Method(() => Utils.CreatePrefab(default, default, default))) + ]) + .Advance(1) + .Insert([ + new CodeInstruction(OpCodes.Dup), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => Callback(default))) + ]) + .InstructionEnumeration(); } public static void Callback(GameObject gameObject) diff --git a/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs b/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs index d0d6c13dbf..eb9fb89d6e 100644 --- a/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs @@ -1,19 +1,41 @@ +using System.Collections.Generic; using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using NitroxClient.GameLogic; using NitroxModel.Helper; +using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; -/// -/// Called whenever a Cyclops or Seamoth is spawned. Nitrox already has its own command to spawn vehicles. -/// This patch is only meant to block the method from executing, causing two vehicles to be spawned instead of one -/// public sealed partial class SubConsoleCommand_OnConsoleCommand_sub_Patch : NitroxPatch, IDynamicPatch { - private static readonly MethodInfo TARGET_METHOD = Reflect.Method((SubConsoleCommand t) => t.OnConsoleCommand_sub(default)); + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SubConsoleCommand t) => t.OnConsoleCommand_sub(default)); - public static bool Prefix() + /* + * REPLACE: + * LightmappedPrefabs.main.RequestScenePrefab(text, new LightmappedPrefabs.OnPrefabLoaded(this.OnSubPrefabLoaded)); + * BY: + * LightmappedPrefabs.main.RequestScenePrefab(text, new LightmappedPrefabs.OnPrefabLoaded(SubConsoleCommand_OnConsoleCommand_sub_Patch.WrappedCallback)); + */ + public static IEnumerable Transpiler(IEnumerable instructions) { - Log.InGame(Language.main.Get("Nitrox_CommandNotAvailable")); - return false; + return new CodeMatcher(instructions).MatchEndForward([ + new CodeMatch(OpCodes.Stfld), + new CodeMatch(OpCodes.Ldsfld), + new CodeMatch(OpCodes.Ldloc_0), + new CodeMatch(OpCodes.Ldarg_0) + ]) + .SetOpcodeAndAdvance(OpCodes.Ldnull) + .SetOperandAndAdvance(Reflect.Method(() => WrappedCallback(default))) + .InstructionEnumeration(); + } + + public static void WrappedCallback(GameObject prefab) + { + SubConsoleCommand instance = SubConsoleCommand.main; + // Call the original callback and then get the object it created to broadcast its creation + instance.OnSubPrefabLoaded(prefab); + Resolve().Spawn(instance.lastCreatedSub); } } From 80a29797e0a8c6bd512fc880bf7a021395c2af46 Mon Sep 17 00:00:00 2001 From: tornac1234 <24827220+tornac1234@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:47:37 +0100 Subject: [PATCH 2/2] Sync "spawn" command for all entities and restrict "sub" and "spawn" to at least moderator perms --- NitroxClient/GameLogic/MobileVehicleBay.cs | 2 +- NitroxClient/GameLogic/NitroxConsole.cs | 24 ++++------- NitroxClient/GameLogic/Vehicles.cs | 2 +- ...wnConsoleCommand_OnConsoleCommand_Patch.cs | 39 ++++++----------- .../SpawnConsoleCommand_SpawnAsync_Patch.cs | 43 +++++++++++++++++++ ...nsoleCommand_OnConsoleCommand_sub_Patch.cs | 22 ++++++++++ 6 files changed, 87 insertions(+), 45 deletions(-) create mode 100644 NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_SpawnAsync_Patch.cs diff --git a/NitroxClient/GameLogic/MobileVehicleBay.cs b/NitroxClient/GameLogic/MobileVehicleBay.cs index 6a33c16586..b48f5a61a1 100644 --- a/NitroxClient/GameLogic/MobileVehicleBay.cs +++ b/NitroxClient/GameLogic/MobileVehicleBay.cs @@ -40,7 +40,7 @@ public void BeginCrafting(ConstructorInput constructor, GameObject constructedOb NitroxId constructedObjectId = NitroxEntity.GenerateNewId(constructedObject); - VehicleWorldEntity vehicleEntity = Vehicles.MakeVehicleEntity(constructedObject, constructedObjectId, techType, constructorId); + VehicleWorldEntity vehicleEntity = Vehicles.BuildVehicleWorldEntity(constructedObject, constructedObjectId, techType, constructorId); packetSender.Send(new EntitySpawnedByClient(vehicleEntity)); diff --git a/NitroxClient/GameLogic/NitroxConsole.cs b/NitroxClient/GameLogic/NitroxConsole.cs index acfb93ae40..ed313db3c9 100644 --- a/NitroxClient/GameLogic/NitroxConsole.cs +++ b/NitroxClient/GameLogic/NitroxConsole.cs @@ -14,12 +14,12 @@ public class NitroxConsole public static bool DisableConsole { get; set; } = true; private readonly IPacketSender packetSender; - private readonly Items item; + private readonly Items items; - public NitroxConsole(IPacketSender packetSender, Items item) + public NitroxConsole(IPacketSender packetSender, Items items) { this.packetSender = packetSender; - this.item = item; + this.items = items; } //List of things that can be spawned : https://subnauticacommands.com/items @@ -35,8 +35,7 @@ public void Spawn(GameObject gameObject) } else { - SpawnItem(gameObject); - //TODO: Add support for no AI creature that need to be spawned as well + DefaultSpawn(gameObject); } } catch (Exception ex) @@ -46,29 +45,22 @@ public void Spawn(GameObject gameObject) } /// - /// Spawns Seamoth, Exosuit and Cyclops + /// Spawns Seamoth, Exosuit or Cyclops /// private void SpawnVehicle(GameObject gameObject, TechType techType) { NitroxId id = NitroxEntity.GetIdOrGenerateNew(gameObject); - VehicleWorldEntity vehicleEntity = Vehicles.MakeVehicleEntity(gameObject, id, techType); + VehicleWorldEntity vehicleEntity = Vehicles.BuildVehicleWorldEntity(gameObject, id, techType); packetSender.Send(new EntitySpawnedByClient(vehicleEntity)); Log.Debug($"Spawning vehicle {techType} with id {id} at {gameObject.transform.position}"); } - /// - /// Spawns a Pickupable item - /// - private void SpawnItem(GameObject gameObject) + private void DefaultSpawn(GameObject gameObject) { - if (gameObject.TryGetComponent(out Pickupable pickupable)) - { - Log.Debug($"Spawning item {pickupable.GetTechName()} at {gameObject.transform.position}"); - item.Dropped(gameObject, pickupable.GetTechType()); - } + items.Dropped(gameObject); } private static TechType GetObjectTechType(GameObject gameObject) diff --git a/NitroxClient/GameLogic/Vehicles.cs b/NitroxClient/GameLogic/Vehicles.cs index ba31024c32..3132b8b7b3 100644 --- a/NitroxClient/GameLogic/Vehicles.cs +++ b/NitroxClient/GameLogic/Vehicles.cs @@ -235,7 +235,7 @@ public static void RemoveNitroxEntitiesTagging(GameObject constructedObject) } } - public static VehicleWorldEntity MakeVehicleEntity(GameObject constructedObject, NitroxId constructedObjectId, TechType techType, NitroxId constructorId = null) + public static VehicleWorldEntity BuildVehicleWorldEntity(GameObject constructedObject, NitroxId constructedObjectId, TechType techType, NitroxId constructorId = null) { VehicleWorldEntity vehicleEntity = new(constructorId, DayNightCycle.main.timePassedAsFloat, constructedObject.transform.ToLocalDto(), string.Empty, false, constructedObjectId, techType.ToDto(), null); VehicleChildEntityHelper.PopulateChildren(constructedObjectId, constructedObject.GetFullHierarchyPath(), vehicleEntity.ChildEntities, constructedObject); diff --git a/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs b/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs index c7133fd69b..4381696cc4 100644 --- a/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_OnConsoleCommand_Patch.cs @@ -1,39 +1,24 @@ -using System.Collections.Generic; using System.Reflection; -using System.Reflection.Emit; -using HarmonyLib; using NitroxClient.GameLogic; +using NitroxModel.DataStructures.GameLogic; using NitroxModel.Helper; -using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; +/// +/// Prevents local player from using "spawn" command without at least the permissions. +/// public sealed partial class SpawnConsoleCommand_OnConsoleCommand_Patch : NitroxPatch, IDynamicPatch { - internal static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method((SpawnConsoleCommand t) => t.SpawnAsync(default))); + internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SpawnConsoleCommand t) => t.OnConsoleCommand_spawn(default)); - /* - * GameObject gameObject = global::Utils.CreatePrefab(prefabForTechType, maxDist, i > 0); - * -> SpawnConsoleCommand_OnConsoleCommand_Patch.Callback(gameObject); - * LargeWorldEntity.Register(gameObject); - * CrafterLogic.NotifyCraftEnd(gameObject, techType); - * gameObject.SendMessage("StartConstruction", SendMessageOptions.DontRequireReceiver); - */ - public static IEnumerable Transpiler(IEnumerable instructions) + public static bool Prefix(NotificationCenter.Notification n) { - return new CodeMatcher(instructions).MatchStartForward([ - new CodeMatch(OpCodes.Call, Reflect.Method(() => Utils.CreatePrefab(default, default, default))) - ]) - .Advance(1) - .Insert([ - new CodeInstruction(OpCodes.Dup), - new CodeInstruction(OpCodes.Call, Reflect.Method(() => Callback(default))) - ]) - .InstructionEnumeration(); - } - - public static void Callback(GameObject gameObject) - { - Resolve().Spawn(gameObject); + if (Resolve().Permissions < Perms.MODERATOR) + { + Log.InGame(Language.main.Get("Nitrox_MissingPermission").Replace("{PERMISSION}", Perms.MODERATOR.ToString())); + return false; + } + return true; } } diff --git a/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_SpawnAsync_Patch.cs b/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_SpawnAsync_Patch.cs new file mode 100644 index 0000000000..750de7a5be --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/SpawnConsoleCommand_SpawnAsync_Patch.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using HarmonyLib; +using NitroxClient.GameLogic; +using NitroxModel.Helper; +using UnityEngine; + +namespace NitroxPatcher.Patches.Dynamic; + +/// +/// Syncs "spawn" command. +/// +public sealed partial class SpawnConsoleCommand_SpawnAsync_Patch : NitroxPatch, IDynamicPatch +{ + internal static readonly MethodInfo TARGET_METHOD = AccessTools.EnumeratorMoveNext(Reflect.Method((SpawnConsoleCommand t) => t.SpawnAsync(default))); + + /* + * MODIFIED: + * GameObject gameObject = global::Utils.CreatePrefab(prefabForTechType, maxDist, i > 0); + * SpawnConsoleCommand_OnConsoleCommand_Patch.WrappedCallback(gameObject); <---- INSERTED LINE + * LargeWorldEntity.Register(gameObject); + * CrafterLogic.NotifyCraftEnd(gameObject, techType); + * gameObject.SendMessage("StartConstruction", SendMessageOptions.DontRequireReceiver); + */ + public static IEnumerable Transpiler(IEnumerable instructions) + { + return new CodeMatcher(instructions).MatchStartForward([ + new CodeMatch(OpCodes.Call, Reflect.Method(() => Utils.CreatePrefab(default, default, default))) + ]) + .Advance(1) + .Insert([ + new CodeInstruction(OpCodes.Dup), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => Callback(default))) + ]) + .InstructionEnumeration(); + } + + public static void Callback(GameObject gameObject) + { + Resolve().Spawn(gameObject); + } +} diff --git a/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs b/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs index eb9fb89d6e..8c7714329b 100644 --- a/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/SubConsoleCommand_OnConsoleCommand_sub_Patch.cs @@ -3,15 +3,37 @@ using System.Reflection.Emit; using HarmonyLib; using NitroxClient.GameLogic; +using NitroxModel.DataStructures.GameLogic; using NitroxModel.Helper; using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; +/// +/// Prevents local player from using "sub" command without at least the permissions. +/// Once they have the permissions, sync this command. +/// public sealed partial class SubConsoleCommand_OnConsoleCommand_sub_Patch : NitroxPatch, IDynamicPatch { internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((SubConsoleCommand t) => t.OnConsoleCommand_sub(default)); + public static bool Prefix(NotificationCenter.Notification n) + { + if (Resolve().Permissions < Perms.MODERATOR) + { + Log.InGame(Language.main.Get("Nitrox_MissingPermission").Replace("{PERMISSION}", Perms.MODERATOR.ToString())); + return false; + } + + string text = (string)n.data[0]; + if (!string.IsNullOrEmpty(text) && !text.ToLowerInvariant().Equals("cyclops")) + { + Log.InGame(Language.main.Get("Nitrox_CommandNotAvailable")); + return false; + } + return true; + } + /* * REPLACE: * LightmappedPrefabs.main.RequestScenePrefab(text, new LightmappedPrefabs.OnPrefabLoaded(this.OnSubPrefabLoaded));