Skip to content

Commit

Permalink
Prevent grabbing another player with propulsion cannon, fix teleporta…
Browse files Browse the repository at this point in the history
…tion to another player, fix wrong cyclops detection by leviathans (too short), persist leash position of leviathans so they don't derive away from their original spawn position indefinitely, fix leviathans not spawning when they get close to a player who has them in range
  • Loading branch information
tornac1234 committed Jan 1, 2025
1 parent 80dcff9 commit 6d08896
Show file tree
Hide file tree
Showing 22 changed files with 448 additions and 51 deletions.
1 change: 1 addition & 0 deletions Nitrox.Test/Patcher/Patches/PatchesTranspilerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public class PatchesTranspilerTest
[typeof(EntityCell_AwakeAsync_Patch), 2],
[typeof(StasisSphere_LateUpdate_Patch), 0],
[typeof(MeleeAttack_CanDealDamageTo_Patch), 4],
[typeof(LargeWorldEntity_UpdateCell_Patch), 1],
];

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using NitroxClient.Communication.Packets.Processors.Abstract;
using NitroxClient.GameLogic;
using NitroxModel.Packets;

namespace NitroxClient.Communication.Packets.Processors;

public class DropSimulationOwnershipProcessor : ClientPacketProcessor<DropSimulationOwnership>
{
private readonly SimulationOwnership simulationOwnershipManager;

public DropSimulationOwnershipProcessor(SimulationOwnership simulationOwnershipManager)
{
this.simulationOwnershipManager = simulationOwnershipManager;
}

public override void Process(DropSimulationOwnership packet)
{
simulationOwnershipManager.DropSimulationFrom(packet.EntityId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,18 @@ public override void Process(EntityDestroyed packet)

private void DestroySubroot(SubRoot subRoot)
{
DamageInfo damageInfo = new() { type = DAMAGE_TYPE_RUN_ORIGINAL };
if (subRoot.live.health > 0f)
{
// oldHPPercent must be in the interval [0; 0.25[ because else, SubRoot.OnTakeDamage will end up in the wrong else condition
subRoot.oldHPPercent = 0f;
subRoot.live.health = 0f;
subRoot.live.NotifyAllAttachedDamageReceivers(damageInfo);
subRoot.live.Kill();
}

// We use a specific DamageType so that the Prefix on this method will accept this call
subRoot.OnTakeDamage(new() { type = DAMAGE_TYPE_RUN_ORIGINAL });
subRoot.OnTakeDamage(damageInfo);
}

private void DestroyVehicle(Vehicle vehicle)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System;
using NitroxClient.Communication.Packets.Processors.Abstract;
using NitroxClient.MonoBehaviours;
using NitroxClient.Unity.Helper;
using NitroxModel.Packets;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using UWE;
using Terrain = NitroxClient.GameLogic.Terrain;

Expand All @@ -15,32 +14,21 @@ public override void Process(PlayerTeleported packet)
{
Player.main.OnPlayerPositionCheat();

Player.main.SetPosition(packet.DestinationTo.ToUnity());

if (packet.SubRootID.HasValue && NitroxEntity.TryGetComponentFrom(packet.SubRootID.Value, out SubRoot subRoot))
{
// Cyclops is using a local position system inside it's subroot
if (subRoot.isCyclops)
{
// Reversing calculations from PlayerMovementBroadcaster.Update()
Vector3 position = (subRoot.transform.rotation * packet.DestinationTo.ToUnity()) + subRoot.transform.position;

Player.main.SetPosition(position);
Player.main.SetCurrentSub(subRoot);
return;
}

Player.main.SetCurrentSub(subRoot);
Player.main.SetCurrentSub(subRoot, true);
return;
}

Player.main.SetPosition(packet.DestinationTo.ToUnity());
// Freeze the player while he's loading its new position
Player.main.cinematicModeActive = true;
try
{
CoroutineHost.StartCoroutine(Terrain.WaitForWorldLoad());
} catch (Exception e)

CoroutineHost.StartCoroutine(Terrain.WaitForWorldLoad().OnYieldError(e =>
{
Player.main.cinematicModeActive = false;
Log.Warn($"Something wrong happened while waiting for the terrain to load.\n{e}");
}
}));
}
}
22 changes: 22 additions & 0 deletions NitroxClient/GameLogic/SimulationOwnership.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,28 @@ public void TreatSimulatedEntity(SimulatedEntity simulatedEntity)
}
}

public void DropSimulationFrom(NitroxId entityId)
{
StopSimulatingEntity(entityId);
EntityPositionBroadcaster.StopWatchingEntity(entityId);
if (!NitroxEntity.TryGetObjectFrom(entityId, out GameObject gameObject))
{
return;
}

if (gameObject.TryGetComponent(out RemotelyControlled remotelyControlled))
{
Object.Destroy(remotelyControlled);
}

// Very specific edge case (we are still seeing the entity but it's not in a cell that we have marked as visible)
// so the server couldn't automatically hand the simulation ownership to us
if (gameObject.TryGetComponent(out OutOfCellEntity outOfCellEntity))
{
outOfCellEntity.TryClaim();
}
}

public bool TryGetLockType(NitroxId nitroxId, out SimulationLockType simulationLockType)
{
return simulatedIdsByLockType.TryGetValue(nitroxId, out simulationLockType);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using NitroxClient.GameLogic.Spawning.Metadata.Processor.Abstract;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;

namespace NitroxClient.GameLogic.Spawning.Metadata.Processor;

public class StayAtLeashPositionMetadataProcessor : EntityMetadataProcessor<StayAtLeashPositionMetadata>
{
public override void ProcessMetadata(GameObject gameObject, StayAtLeashPositionMetadata metadata)
{
if (!gameObject.TryGetComponent(out Creature creature))
{
Log.Error($"Could not find {nameof(Creature)} on {gameObject.name}");
return;
}

if (!creature.isInitialized)
{
// TODO: When #2137 is merged, only a MetadataHolder to the creature and postfix patch creature.Start to consume it
creature.InitializeOnce();
creature.isInitialized = true;
}
creature.leashPosition = metadata.LeashPosition.ToUnity();
}
}
14 changes: 14 additions & 0 deletions NitroxClient/MonoBehaviours/Cyclops/NitroxCyclops.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@ public class NitroxCyclops : MonoBehaviour
private WorldForces worldForces;
private Stabilizer stabilizer;
private CharacterController controller;
private CyclopsNoiseManager cyclopsNoiseManager;

public readonly Dictionary<INitroxPlayer, CyclopsPawn> Pawns = [];

public static readonly Dictionary<NitroxCyclops, float> ScaledNoiseByCyclops = [];

public void Start()
{
cyclopsMotor = Player.mainObject.GetComponent<CyclopsMotor>();
Expand All @@ -31,15 +34,26 @@ public void Start()
worldForces = GetComponent<WorldForces>();
stabilizer = GetComponent<Stabilizer>();
controller = cyclopsMotor.controller;
cyclopsNoiseManager = GetComponent<CyclopsNoiseManager>();

UWE.Utils.SetIsKinematicAndUpdateInterpolation(rigidbody, false, true);

WorkaroundColliders();

ScaledNoiseByCyclops.Add(this, 0f);
}

public void Update()
{
MaintainPawns();

// Calculation from AttackCyclops.UpdateAggression
ScaledNoiseByCyclops[this] = Mathf.Lerp(0f, 150f, cyclopsNoiseManager.GetNoisePercent());
}

public void OnDestroy()
{
ScaledNoiseByCyclops.Remove(this);
}

/// <summary>
Expand Down
59 changes: 59 additions & 0 deletions NitroxClient/MonoBehaviours/OutOfCellEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using NitroxClient.Communication.Abstract;
using NitroxClient.GameLogic;
using NitroxModel.DataStructures;
using NitroxModel.Packets;
using UnityEngine;

namespace NitroxClient.MonoBehaviours;

/// <summary>
/// Entities might move slightly out of the loaded zone, in which case the server thinks that they're in another cell
/// (because the cell is only determined by the entity's position). Thus we need to be able to know when this entity is unloaded
/// and broadcast this event so the server can switch the ownership from it.
/// </summary>
public class OutOfCellEntity : MonoBehaviour
{
private bool manuallyDestroyed;
private NitroxId nitroxId;

private SimulationOwnership SimulationOwnership => this.Resolve<SimulationOwnership>();

public void Init(NitroxId nitroxId)
{
this.nitroxId = nitroxId;
}

/// <remarks>
/// Once an entity out of cell is "free" from simulation ownership, we need to ask the server to lock it since we don't have
/// it's actual cell loaded (from position)
/// </remarks>
public void TryClaim()
{
SimulationOwnership.RequestSimulationLock(nitroxId, SimulationLockType.TRANSIENT);
}

/// <summary>
/// Remove the component without triggering the entity unload broadcast
/// </summary>
public void ManuallyDestroy()
{
manuallyDestroyed = true;
Destroy(this);
}

public void OnDestroy()
{
if (manuallyDestroyed)
{
return;
}

// We check for IsAlive because we don't want to broadcast this when the entity was killed (it's managed in another way)
if (TryGetComponent(out LiveMixin liveMixin) && liveMixin.IsAlive() && SimulationOwnership.HasAnyLockType(nitroxId))
{
SimulationOwnership.StopSimulatingEntity(nitroxId);
EntityPositionBroadcaster.StopWatchingEntity(nitroxId);
this.Resolve<IPacketSender>().Send(new DropSimulationOwnership(nitroxId));
}
}
}
30 changes: 17 additions & 13 deletions NitroxClient/MonoBehaviours/PlayerDeathBroadcaster.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
using NitroxClient.GameLogic;
using NitroxClient.GameLogic;
using NitroxModel.Core;
using UnityEngine;

namespace NitroxClient.MonoBehaviours
namespace NitroxClient.MonoBehaviours;

public class PlayerDeathBroadcaster : MonoBehaviour
{
public class PlayerDeathBroadcaster : MonoBehaviour
private LocalPlayer localPlayer;

public void Awake()
{
private LocalPlayer localPlayer;
localPlayer = NitroxServiceLocator.LocateService<LocalPlayer>();

public void Awake()
{
localPlayer = NitroxServiceLocator.LocateService<LocalPlayer>();
Player.main.playerDeathEvent.AddHandler(this, PlayerDeath);
}

Player.main.playerDeathEvent.AddHandler(this, PlayerDeath);
}
private void PlayerDeath(Player player)
{
localPlayer.BroadcastDeath(player.transform.position);
}

private void PlayerDeath(Player player)
{
localPlayer.BroadcastDeath(player.transform.position);
}
public void OnDestroy()
{
Player.main.playerDeathEvent.RemoveHandler(this, PlayerDeath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace NitroxModel.DataStructures.GameLogic.Entities.Metadata
[ProtoInclude(77, typeof(CrashHomeMetadata))]
[ProtoInclude(78, typeof(EatableMetadata))]
[ProtoInclude(79, typeof(SeaTreaderMetadata))]
[ProtoInclude(80, typeof(StayAtLeashPositionMetadata))]
public abstract class EntityMetadata
{
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Runtime.Serialization;
using BinaryPack.Attributes;
using NitroxModel.DataStructures.Unity;

namespace NitroxModel.DataStructures.GameLogic.Entities.Metadata;

[Serializable, DataContract]
public class StayAtLeashPositionMetadata : EntityMetadata
{
[DataMember(Order = 1)]
public NitroxVector3 LeashPosition { get; }

[IgnoreConstructor]
protected StayAtLeashPositionMetadata()
{
// Constructor for serialization. Has to be "protected" for json serialization.
}

public StayAtLeashPositionMetadata(NitroxVector3 leashPosition)
{
LeashPosition = leashPosition;
}

public override string ToString()
{
return $"[{nameof(StayAtLeashPositionMetadata)} LeashPosition: {LeashPosition}]";
}
}
15 changes: 15 additions & 0 deletions NitroxModel/Packets/DropSimulationOwnership.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 DropSimulationOwnership : Packet
{
public NitroxId EntityId { get; set; }

public DropSimulationOwnership(NitroxId entityId)
{
EntityId = entityId;
}
}
Loading

0 comments on commit 6d08896

Please sign in to comment.