Skip to content

Commit

Permalink
Fix destroyed vehicle also destroying their children player entities
Browse files Browse the repository at this point in the history
  • Loading branch information
tornac1234 committed Aug 21, 2024
1 parent 4421247 commit 3807b79
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 34 deletions.
2 changes: 1 addition & 1 deletion NitroxClient/GameLogic/Vehicles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public void BroadcastDestroyedVehicle(NitroxId id)
{
using (PacketSuppressor<VehicleOnPilotModeChanged>.Suppress())
{
EntityDestroyed vehicleDestroyed = new(id);
VehicleDestroyed vehicleDestroyed = new(id);
packetSender.Send(vehicleDestroyed);
}
}
Expand Down
12 changes: 10 additions & 2 deletions NitroxClient/MonoBehaviours/Cyclops/NitroxCyclops.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,17 @@ public void Update()
/// </summary>
public void RemoveAllPlayers()
{
// This will call OnLocalPlayerExit
if (Player.main.currentSub == subRoot)
{
Player.main.SetCurrentSub(null);
}

foreach (RemotePlayerIdentifier remotePlayerIdentifier in GetComponentsInChildren<RemotePlayerIdentifier>(true))
{
remotePlayerIdentifier.RemotePlayer.ResetStates();
OnPlayerExit(remotePlayerIdentifier.RemotePlayer);
}
OnLocalPlayerExit();
}

/// <summary>
Expand Down Expand Up @@ -84,7 +89,10 @@ public void OnLocalPlayerExit()
cyclopsMotor.ToggleCyclopsMotor(false);
cyclopsMotor.Pawn = null;

Virtual.SetCurrentCyclops(null);
if (Virtual)
{
Virtual.SetCurrentCyclops(null);
}
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions NitroxClient/MonoBehaviours/Cyclops/VirtualCyclops.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public static void CreateVirtualCyclops()
GameObject.Destroy(model.GetComponent<EcoTarget>());
GameObject.Destroy(model.GetComponent<PingInstance>());
GameObject.Destroy(model.GetComponent<CyclopsDestructionEvent>());
Instance.InitialPosition = position;
Instance.InitialRotation = rotation;
Expand All @@ -78,6 +79,7 @@ public static void CreateVirtualCyclops()
model.GetComponent<WorldForces>().lockInterpolation = false;
model.GetComponent<Stabilizer>().stabilizerEnabled = false;
model.GetComponent<Rigidbody>().isKinematic = true;
model.GetComponent<LiveMixin>().invincible = true;
Instance.RegisterVirtualOpenables();
Instance.ToggleRenderers(false);
Expand Down
15 changes: 15 additions & 0 deletions NitroxModel/Packets/VehicleDestroyed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using NitroxModel.DataStructures;

namespace NitroxModel.Packets;

[Serializable]
public class VehicleDestroyed : Packet
{
public NitroxId Id { get; }

public VehicleDestroyed(NitroxId id)
{
Id = id;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Reflection;
using NitroxClient.GameLogic;
using NitroxClient.MonoBehaviours;
using NitroxClient.MonoBehaviours.Cyclops;
using NitroxModel.DataStructures;
using NitroxModel.Helper;
Expand All @@ -15,16 +16,26 @@ public sealed partial class CyclopsDestructionEvent_DestroyCyclops_Patch : Nitro

public static void Prefix(CyclopsDestructionEvent __instance)
{
__instance.subLiveMixin.Kill();
if (__instance.TryGetNitroxId(out NitroxId nitroxId))
{
Resolve<SimulationOwnership>().StopSimulatingEntity(nitroxId);
EntityPositionBroadcaster.StopWatchingEntity(nitroxId);
}

bool wasInCyclops = Player.main.currentSub == __instance.subRoot;

// Before the cyclops destruction, we move out the remote players so that they aren't stuck in its hierarchy
if (Player.main && Player.main.currentSub == __instance.subRoot && __instance.subRoot.TryGetComponent(out NitroxCyclops nitroxCyclops))
if (__instance.subRoot && __instance.subRoot.TryGetComponent(out NitroxCyclops nitroxCyclops))
{
nitroxCyclops.RemoveAllPlayers();
}

if (wasInCyclops)
{
// Particular case here, this is not broadcasted and should not be, it's just there to have player be really inside the cyclops while not being registered by NitroxCyclops
Player.main._currentSub = __instance.subRoot;
}

__instance.subLiveMixin.Kill();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NitroxClient.GameLogic;
using NitroxModel.DataStructures;
using NitroxModel.Helper;
using static NitroxModel.Helper.Reflect;

namespace NitroxPatcher.Patches.Dynamic;

Expand All @@ -18,25 +19,31 @@ public static bool PrefixRestore()
return false;
}

public static bool PrefixDestroy(CyclopsDestructionEvent __instance)
public static bool PrefixDestroy(CyclopsDestructionEvent __instance, out bool __state)
{
// We only apply the destroy to the current Cyclops
if (!Player.main.currentSub || Player.main.currentSub.gameObject != __instance.gameObject)
{
return false;
}
__state = Player.main.currentSub == __instance.subRoot;
return __state;
}

if (__instance.TryGetIdOrWarn(out NitroxId id))
/// <remarks>
/// This must happen at postfix so that the SubRootChanged packet are sent before the destroyed vehicle one,
/// thus saving player entities from deletion.
/// </remarks>
public static void PostfixDestroy(CyclopsDestructionEvent __instance, bool __state)
{
if (__state && __instance.TryGetIdOrWarn(out NitroxId id))
{
Resolve<Vehicles>().BroadcastDestroyedVehicle(id);
}

return true;
}

public override void Patch(Harmony harmony)
{
MethodInfo destroyPrefixInfo = Method(() => PrefixDestroy(default, out Ref<bool>.Field));

PatchPrefix(harmony, TARGET_METHOD_RESTORE, ((Func<bool>)PrefixRestore).Method);
PatchPrefix(harmony, TARGET_METHOD_DESTROY, ((Func<CyclopsDestructionEvent, bool>)PrefixDestroy).Method);
PatchPrefix(harmony, TARGET_METHOD_DESTROY, destroyPrefixInfo);
PatchPostfix(harmony, TARGET_METHOD_DESTROY, ((Action<CyclopsDestructionEvent, bool>)PostfixDestroy).Method);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ public override void Process(EntityDestroyed packet, Player destroyingPlayer)
{
entitySimulation.EntityDestroyed(packet.Id);

if (worldEntityManager.TryDestroyEntity(packet.Id, out Optional<Entity> entity))
if (worldEntityManager.TryDestroyEntity(packet.Id, out Entity entity))
{
foreach (Player player in playerManager.GetConnectedPlayers())
{
bool isOtherPlayer = player != destroyingPlayer;
if (isOtherPlayer && player.CanSee(entity.Value))
if (isOtherPlayer && player.CanSee(entity))
{
player.SendPacket(packet);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public override void Process(PlayerJoiningMultiplayerSession packet, INitroxConn

List<SimulatedEntity> simulations = world.EntitySimulation.AssignGlobalRootEntitiesAndGetData(player);

player.Entity = wasBrandNewPlayer ? SetupPlayerEntity(player) : RespawnExistingEntity(player); ;
player.Entity = wasBrandNewPlayer ? SetupPlayerEntity(player) : RespawnExistingEntity(player);

List<GlobalRootEntity> globalRootEntities = world.WorldEntityManager.GetGlobalRootEntities(true);
bool isFirstPlayer = playerManager.GetConnectedPlayers().Count == 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public override void Process(RemoveCreatureCorpse packet, Player destroyingPlaye
// So that even players rejoining can see it (before it despawns)
entitySimulation.EntityDestroyed(packet.CreatureId);

if (worldEntityManager.TryDestroyEntity(packet.CreatureId, out Optional<Entity> entity))
if (worldEntityManager.TryDestroyEntity(packet.CreatureId, out Entity entity))
{
foreach (Player player in playerManager.GetConnectedPlayers())
{
bool isOtherPlayer = player != destroyingPlayer;
if (isOtherPlayer && player.CanSee(entity.Value))
if (isOtherPlayer && player.CanSee(entity))
{
player.SendPacket(packet);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.Packets;
using NitroxServer.Communication.Packets.Processors.Abstract;
using NitroxServer.GameLogic;
using NitroxServer.GameLogic.Entities;

namespace NitroxServer.Communication.Packets.Processors;

public class VehicleDestroyedPacketProcessor : AuthenticatedPacketProcessor<VehicleDestroyed>
{
private readonly PlayerManager playerManager;
private readonly EntitySimulation entitySimulation;
private readonly WorldEntityManager worldEntityManager;

public VehicleDestroyedPacketProcessor(PlayerManager playerManager, EntitySimulation entitySimulation, WorldEntityManager worldEntityManager)
{
this.playerManager = playerManager;
this.worldEntityManager = worldEntityManager;
this.entitySimulation = entitySimulation;
}

public override void Process(VehicleDestroyed packet, Player destroyingPlayer)
{
entitySimulation.EntityDestroyed(packet.Id);

if (worldEntityManager.TryDestroyEntity(packet.Id, out Entity entity))
{
if (entity is VehicleWorldEntity vehicleWorldEntity)
{
worldEntityManager.MovePlayerChildrenToRoot(vehicleWorldEntity);
}
foreach (Player player in playerManager.GetConnectedPlayers())
{
bool isOtherPlayer = player != destroyingPlayer;
if (isOtherPlayer && player.CanSee(entity))
{
player.SendPacket(packet);
}
}
}
}
}
35 changes: 21 additions & 14 deletions NitroxServer/GameLogic/Entities/WorldEntityManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,7 @@ public Optional<Entity> RemoveGlobalRootEntity(NitroxId entityId, bool removeFro
// to make sure that they won't be removed
if (entityRegistry.TryGetEntityById(entityId, out GlobalRootEntity globalRootEntity))
{
List<PlayerWorldEntity> playerEntities = FindPlayerEntitiesInChildren(globalRootEntity);
foreach (PlayerWorldEntity childPlayerEntity in playerEntities)
{
// Reparent the entity on top of GlobalRoot
globalRootEntity.ChildEntities.Remove(childPlayerEntity);
childPlayerEntity.ParentId = null;

// Make sure the PlayerEntity is correctly registered
AddOrUpdateGlobalRootEntity(childPlayerEntity);
}
MovePlayerChildrenToRoot(globalRootEntity);
}
removedEntity = entityRegistry.RemoveEntity(entityId);
}
Expand All @@ -134,6 +125,20 @@ public Optional<Entity> RemoveGlobalRootEntity(NitroxId entityId, bool removeFro
return removedEntity;
}

public void MovePlayerChildrenToRoot(GlobalRootEntity globalRootEntity)
{
List<PlayerWorldEntity> playerEntities = FindPlayerEntitiesInChildren(globalRootEntity);
foreach (PlayerWorldEntity childPlayerEntity in playerEntities)
{
// Reparent the entity on top of GlobalRoot
globalRootEntity.ChildEntities.Remove(childPlayerEntity);
childPlayerEntity.ParentId = null;

// Make sure the PlayerEntity is correctly registered
AddOrUpdateGlobalRootEntity(childPlayerEntity);
}
}

public void TrackEntityInTheWorld(WorldEntity entity)
{
if (entity is GlobalRootEntity globalRootEntity)
Expand Down Expand Up @@ -284,16 +289,18 @@ public void StopTrackingEntity(WorldEntity entity)
}
}

public bool TryDestroyEntity(NitroxId entityId, out Optional<Entity> entity)
public bool TryDestroyEntity(NitroxId entityId, out Entity entity)
{
entity = entityRegistry.RemoveEntity(entityId);
Optional<Entity> optEntity = entityRegistry.RemoveEntity(entityId);

if (!entity.HasValue)
if (!optEntity.HasValue)
{
entity = null;
return false;
}
entity = optEntity.Value;

if (entity.Value is WorldEntity worldEntity)
if (entity is WorldEntity worldEntity)
{
StopTrackingEntity(worldEntity);
}
Expand Down
2 changes: 1 addition & 1 deletion NitroxServer/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class Player : IProcessorContext
public bool IsPermaDeath { get; set; }
public NitroxVector3 Position { get; set; }
public NitroxQuaternion Rotation { get; set; }
public NitroxId GameObjectId { get; }
public NitroxId GameObjectId { get; set; }
public Optional<NitroxId> SubRootId { get; set; }
public Perms Permissions { get; set; }
public PlayerStatsData Stats { get; set; }
Expand Down

0 comments on commit 3807b79

Please sign in to comment.