Skip to content

Commit

Permalink
Fix "spawn" command and sync "sub" command
Browse files Browse the repository at this point in the history
  • Loading branch information
tornac1234 committed Jan 21, 2024
1 parent 5c48f8c commit 43fc3c8
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -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<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(SpawnConsoleCommand_OnConsoleCommand_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = SpawnConsoleCommand_OnConsoleCommand_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count() + 2);
}
}
Original file line number Diff line number Diff line change
@@ -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<CodeInstruction> originalIl = PatchTestHelper.GetInstructionsFromMethod(SubConsoleCommand_OnConsoleCommand_sub_Patch.TARGET_METHOD);
IEnumerable<CodeInstruction> transformedIl = SubConsoleCommand_OnConsoleCommand_sub_Patch.Transpiler(originalIl);
transformedIl.Count().Should().Be(originalIl.Count());
}
}
5 changes: 1 addition & 4 deletions NitroxClient/GameLogic/MobileVehicleBay.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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));

Expand Down
36 changes: 24 additions & 12 deletions NitroxClient/GameLogic/NitroxConsole.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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
{
Expand All @@ -48,20 +46,17 @@ public void Spawn(GameObject gameObject)
}

/// <summary>
/// Spawns a Seamoth or an Exosuit
/// Spawns Seamoth, Exosuit and Cyclops
/// </summary>
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}");
}

/// <summary>
Expand All @@ -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;
}
}
}
13 changes: 11 additions & 2 deletions NitroxClient/GameLogic/Vehicles.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
Expand All @@ -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<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> 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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<GameObject>)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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// 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
/// </summary>
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<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> 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<NitroxConsole>().Spawn(instance.lastCreatedSub);
}
}

0 comments on commit 43fc3c8

Please sign in to comment.