Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
4f8d334
Added boilerplate for DeathMarkers setting
OhmV-IR Feb 15, 2025
9c528c4
Create beacons on all clients
OhmV-IR Jan 8, 2025
aa32a3d
Fix syntax error
OhmV-IR Jan 8, 2025
bc433fe
Shift to using the existing PlayerDeathEvent packet to trigger the be…
OhmV-IR Jan 10, 2025
46618e4
Make death beacons player exclusive(only spawns for the player that t…
OhmV-IR Jan 10, 2025
887fc59
Disconnecting also despawns all active death beacons
OhmV-IR Jan 10, 2025
c309ada
Fix death beacons getting synced to other players and not disappearin…
OhmV-IR Jan 12, 2025
68a3130
Remove unnecessary logging
OhmV-IR Jan 12, 2025
90822ec
Remove unnecessary method, various semantic updates to prep for PR
OhmV-IR Jan 12, 2025
b12d092
Fixed a random import that was accidentally added
OhmV-IR Jan 12, 2025
c6ad6ef
Update NitroxServer/ConsoleCommands/SetDeathMarkersCommand.cs
OhmV-IR Jan 12, 2025
54596ac
Fixed deathbeacons on land
OhmV-IR Jan 12, 2025
30ab97a
Switch to using blank gameobjects instead of actual beacons
OhmV-IR Jan 12, 2025
c20ea8c
Added missing using
OhmV-IR Jan 16, 2025
e231019
Removed unneeded changes
OhmV-IR Jan 16, 2025
481dc6a
Moved DeathBeacon class to its own file
OhmV-IR Jan 16, 2025
d631a82
changed field to const and named deathbeacon GO
OhmV-IR Jan 16, 2025
48e7a6d
Use InvokeRepeating and sqrMagnitude to check distances for better pe…
OhmV-IR Jan 16, 2025
afaf90f
Add translation string for death beacon label
OhmV-IR Jan 16, 2025
4968b3e
implement various code review requsted changes
OhmV-IR Jan 16, 2025
0ccbe3f
Fix error in building
OhmV-IR Jan 16, 2025
f20e5e0
Add beacon fade out
OhmV-IR Jan 16, 2025
47c5829
Fixed add and sort usings, as well as added a whitespace
OhmV-IR Jan 17, 2025
3b4f648
set DeathMarkers setting to true by default
OhmV-IR Jan 18, 2025
4f9fa44
Fix DeathMarkers for NitroxLauncher(tested in-game for this commit)
OhmV-IR Feb 15, 2025
74beffe
Added missing namespace declaration
OhmV-IR Feb 19, 2025
99d9ce4
Moved spawning of death beacon to PlayerDeath in PlayerDeathBroadcaster
OhmV-IR Feb 19, 2025
870f6a6
Update NitroxModel/Packets/DeathMarkersChanged.cs
OhmV-IR Feb 24, 2025
a03e883
Update NitroxServer/ConsoleCommands/SetDeathMarkersCommand.cs
OhmV-IR Feb 24, 2025
120fa35
Add newline between namespace and class
OhmV-IR Feb 24, 2025
a52d626
Merge branch 'DeathMarkers' of https://github.com/OhmV-IR/NitroxModde…
OhmV-IR Feb 24, 2025
5262c91
Fix missing import
OhmV-IR Feb 24, 2025
b63420f
change setdeathmarkers to deathmarkers cmd name
OhmV-IR Feb 27, 2025
04bb00a
merge master
Dec 24, 2025
a3d305c
Merge branch 'master' into DeathMarkers
Dec 24, 2025
74a6a63
make it build
Dec 24, 2025
d38172b
Merge master
OhmV-IR May 2, 2026
ce46f89
Adapt to refactors (make it compile)
OhmV-IR May 2, 2026
d49e0d2
fix logic error
OhmV-IR May 2, 2026
915dfa6
DeathBeacon code cleanup
Measurity May 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Nitrox.Model.Subnautica/Packets/DeathMarkersChanged.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using Nitrox.Model.Packets;

namespace NitroxModel.Packets;

Comment thread
OhmV-IR marked this conversation as resolved.
[Serializable]
public class DeathMarkersChanged : Packet
{
public bool MarkDeathPointsWithBeacon { get; }

public DeathMarkersChanged(bool markDeathPointsWithBeacon)
{
MarkDeathPointsWithBeacon = markDeathPointsWithBeacon;
}
}
9 changes: 7 additions & 2 deletions Nitrox.Model.Subnautica/Packets/InitialPlayerSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class InitialPlayerSync : Packet
public SessionSettings SessionSettings { get; }
public bool InPrecursor { get; }
public bool DisplaySurfaceWater { get; }
public bool MarkDeathPointsWithBeacon { get; }

public InitialPlayerSync(NitroxId playerGameObjectId,
bool firstTimeConnecting,
Expand All @@ -68,7 +69,8 @@ public InitialPlayerSync(NitroxId playerGameObjectId,
bool keepInventoryOnDeath,
SessionSettings sessionSettings,
bool inPrecursor,
bool displaySurfaceWater)
bool displaySurfaceWater,
bool markDeathPointsWithBeacon)
{
AssignedEscapePodId = assignedEscapePodId;
PlayerGameObjectId = playerGameObjectId;
Expand Down Expand Up @@ -96,6 +98,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId,
SessionSettings = sessionSettings;
InPrecursor = inPrecursor;
DisplaySurfaceWater = displaySurfaceWater;
MarkDeathPointsWithBeacon = markDeathPointsWithBeacon;
}

/// <remarks>Used for deserialization</remarks>
Expand Down Expand Up @@ -125,7 +128,8 @@ public InitialPlayerSync(
bool keepInventoryOnDeath,
SessionSettings sessionSettings,
bool inPrecursor,
bool displaySurfaceWater)
bool displaySurfaceWater,
bool markDeathPointsWithBeacon)
{
AssignedEscapePodId = assignedEscapePodId;
PlayerGameObjectId = playerGameObjectId;
Expand Down Expand Up @@ -153,6 +157,7 @@ public InitialPlayerSync(
SessionSettings = sessionSettings;
InPrecursor = inPrecursor;
DisplaySurfaceWater = displaySurfaceWater;
MarkDeathPointsWithBeacon = markDeathPointsWithBeacon;
}
}
}
3 changes: 3 additions & 0 deletions Nitrox.Model/Configuration/SubnauticaServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public sealed partial class SubnauticaServerOptions
public bool DisableConsole { get; set; }
public string? AdminPassword { get; set; } = "";
public bool KeepInventoryOnDeath { get; set; }

[PropertyDescription("Places a beacon where players die")]
public bool MarkDeathPointsWithBeacon { get; set; }
public bool PvpEnabled { get; set; } = true;
public bool AutoSave { get; set; } = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,6 @@ public async ValueTask SendAsync<T>(SessionId sessionId, T data)
break;
}
}

public override string ToString() => $"'{OriginName}' #{OriginId}";
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,6 @@ public async ValueTask SendToOthersAsync<T>(T data)
break;
}
}

public override string ToString() => $"'{OriginName}' #{OriginId}";
}
25 changes: 25 additions & 0 deletions Nitrox.Server.Subnautica/Models/Commands/DeathMarkerCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.ComponentModel;
using Nitrox.Model.DataStructures.GameLogic;
using Nitrox.Server.Subnautica.Models.Commands.Core;
using NitroxModel.Packets;

namespace Nitrox.Server.Subnautica.Models.Commands;

[RequiresPermission(Perms.ADMIN)]
internal sealed class DeathMarkerCommand(IOptions<SubnauticaServerOptions> options) : ICommandHandler<bool>
{
private readonly IOptions<SubnauticaServerOptions> options = options;

[Description("Sets \"death markers\" setting to on/off. If \"on\", a beacon will be placed when a player dies at the location of the death.")]
public async Task Execute(ICommandContext context, [Description("The true/false state to set the death markers setting to")] bool newState)
{
if (options.Value.MarkDeathPointsWithBeacon == newState)
{
await context.ReplyAsync($"{nameof(options.Value.MarkDeathPointsWithBeacon)} already set to {newState}");
return;
}
options.Value.MarkDeathPointsWithBeacon = newState;
await context.SendToAllAsync(new DeathMarkersChanged(newState));
await context.SendToAllAsync($"{nameof(options.Value.MarkDeathPointsWithBeacon)} changed to \"{newState}\" by {context}");
}
}
3 changes: 2 additions & 1 deletion Nitrox.Server.Subnautica/Models/GameLogic/JoiningManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ private async Task SendInitialSyncAsync(SessionId sessionId, string reservationK
options.Value.KeepInventoryOnDeath,
sessionSettings,
player.InPrecursor,
player.DisplaySurfaceWater
player.DisplaySurfaceWater,
options.Value.MarkDeathPointsWithBeacon
);

await packetSender.SendPacketAsync(initialPlayerSync, player.SessionId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;

namespace NitroxClient.Communication.MultiplayerSession.ConnectionState
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using NitroxClient.Communication.Packets.Processors.Core;
using NitroxClient.GameLogic;
using NitroxModel.Packets;

namespace NitroxClient.Communication.Packets.Processors;

internal sealed class DeathMarkersChangedProcessor(LocalPlayer localPlayer) : IClientPacketProcessor<DeathMarkersChanged>
{
private readonly LocalPlayer localPlayer = localPlayer;

public Task Process(ClientProcessorContext context, DeathMarkersChanged packet)
{
localPlayer.MarkDeathPointsWithBeacon = packet.MarkDeathPointsWithBeacon;
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Nitrox.Model.Helper;
using Nitrox.Model.Helper;
using Nitrox.Model.Subnautica.Packets;
using NitroxClient.Communication.Packets.Processors.Core;
using NitroxClient.GameLogic;
Expand Down
11 changes: 7 additions & 4 deletions NitroxClient/Extensions/GameObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ namespace NitroxClient.Extensions;

public static class GameObjectExtensions
{
/// <summary>
/// Returns true if game object is the local player, playing on the executing machine.
/// </summary>
public static bool IsLocalPlayer(this GameObject gameObject) => gameObject == Player.main.gameObject;
extension(GameObject self)
{
/// <summary>
/// Returns true if game object is the local player, playing on the executing machine.
/// </summary>
public bool IsLocalPlayer => self == Player.main.gameObject;
}

public static bool TryGetComponentInChildren<T>(this GameObject go, out T component, bool includeInactive = false) where T : Component
{
Expand Down
16 changes: 16 additions & 0 deletions NitroxClient/Extensions/PingInstanceExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NitroxClient.Extensions;

internal static class PingInstanceExtensions
{
extension(PingInstance self)
{
/// <summary>
/// If true, ping instance should not be synchronized to remote players.
/// </summary>
public bool IsLocalOnly
{
set => self._id = "local";
get => self._id == "local";
}
}
}
2 changes: 1 addition & 1 deletion NitroxClient/GameLogic/Helper/EquipmentHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class EquipmentHelper
o => o.GetComponent<UpgradeConsole>().AliveOrNull()?.modules,
o => o.GetComponent<Vehicle>().AliveOrNull()?.modules,
o => o.GetComponent<VehicleUpgradeConsoleInput>().AliveOrNull()?.equipment,
o => o.IsLocalPlayer() ? Inventory.main.equipment : null
o => o.IsLocalPlayer ? Inventory.main.equipment : null
};

public static Optional<Equipment> FindEquipmentComponent(GameObject owner)
Expand Down
2 changes: 1 addition & 1 deletion NitroxClient/GameLogic/Helper/InventoryContainerHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class InventoryContainerHelper
/// </summary>
public static Optional<ItemsContainer> TryGetContainerByOwner(GameObject owner)
{
if (owner.IsLocalPlayer())
if (owner.IsLocalPlayer)
{
return Optional.Of(Inventory.main.container);
}
Expand Down
10 changes: 8 additions & 2 deletions NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public PlayerInitialSyncProcessor(Items item, ItemContainers itemContainers, Loc
AddStep(sync => SetPlayerStats(sync.PlayerStatsData));
AddStep(sync => SetUsedItems(sync.UsedItems));
AddStep(sync => SetPlayerGameMode(sync.GameMode));
AddStep(sync => ApplySettings(sync.KeepInventoryOnDeath, sync.SessionSettings.FastHatch, sync.SessionSettings.FastGrow));
AddStep(sync => ApplySettings(sync.KeepInventoryOnDeath, sync.SessionSettings.FastHatch, sync.SessionSettings.FastGrow, sync.MarkDeathPointsWithBeacon));
}

private void SetPlayerPermissions(Perms permissions)
Expand Down Expand Up @@ -149,9 +149,10 @@ private static void SetPlayerGameMode(SubnauticaGameMode gameMode)
GameModeUtils.SetGameMode((GameModeOption)(int)gameMode, GameModeOption.None);
}

private void ApplySettings(bool keepInventoryOnDeath, bool fastHatch, bool fastGrow)
private void ApplySettings(bool keepInventoryOnDeath, bool fastHatch, bool fastGrow, bool markDeathPointsWithBeacon)
{
localPlayer.KeepInventoryOnDeath = keepInventoryOnDeath;
localPlayer.MarkDeathPointsWithBeacon = markDeathPointsWithBeacon;
NoCostConsoleCommand.main.fastHatchCheat = fastHatch;
NoCostConsoleCommand.main.fastGrowCheat = fastGrow;
if (!fastHatch && !fastGrow)
Expand All @@ -170,4 +171,9 @@ private void ApplySettings(bool keepInventoryOnDeath, bool fastHatch, bool fastG
}
Log.InGame(cheatsEnabled.ToString());
}

private void SetPlayerMarkDeathPointsWithBeacon(bool markDeathPointsWithBeacon)
{
localPlayer.MarkDeathPointsWithBeacon = markDeathPointsWithBeacon;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ private static void RefreshPingEntryInPDA(PingInstance pingInstance)
public static bool TryGetKeyForPingInstance(PingInstance pingInstance, out string pingKey, out bool isRemotePlayerPing, Action failCallback = null)
{
isRemotePlayerPing = false;
if (pingInstance.IsLocalOnly)
{
pingKey = string.Empty;
return false;
}
if (pingInstance.TryGetComponent(out SignalPing signalPing))
{
pingKey = signalPing.descriptionKey;
Expand Down Expand Up @@ -129,7 +134,7 @@ public static bool TryGetKeyForPingInstance(PingInstance pingInstance, out strin
return false;
}

Log.Warn($"Couldn't find PingInstance identifier for {pingInstance.name} under {pingInstance.transform.parent}");
Log.Warn($"Couldn't find {nameof(PingInstance)} identifier for {pingInstance.name} under {pingInstance.transform.parent}");
pingKey = string.Empty;
return false;
}
Expand Down
3 changes: 2 additions & 1 deletion NitroxClient/GameLogic/LocalPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ public class LocalPlayer : ILocalNitroxPlayer
/// </summary>
public SessionId? SessionId => multiplayerSession.Reservation?.SessionId;
public PlayerSettings PlayerSettings => multiplayerSession.PlayerSettings;

public Perms Permissions { get; set; }
public IntroCinematicMode IntroCinematicMode { get; set; }
public bool KeepInventoryOnDeath { get; set; }
public bool MarkDeathPointsWithBeacon { get; set; }
Comment thread
Jannify marked this conversation as resolved.

public LocalPlayer(IMultiplayerSession multiplayerSession, IPacketSender packetSender, ThrottledPacketSender throttledPacketSender)
{
Expand All @@ -53,6 +53,7 @@ public LocalPlayer(IMultiplayerSession multiplayerSession, IPacketSender packetS
Permissions = Perms.PLAYER;
IntroCinematicMode = IntroCinematicMode.NONE;
KeepInventoryOnDeath = false;
MarkDeathPointsWithBeacon = false;
}

public void BroadcastLocation(Vector3 location, Vector3 velocity, Quaternion bodyRotation, Quaternion aimingRotation)
Expand Down
42 changes: 42 additions & 0 deletions NitroxClient/MonoBehaviours/Gui/InGame/DeathBeacon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Nitrox.Model.DataStructures.Unity;
using UnityEngine;

namespace NitroxClient.MonoBehaviours.Gui.InGame;

/// <summary>
/// Related to DeathMarker server setting.
/// </summary>
internal sealed class DeathBeacon : MonoBehaviour
{
private const float DESPAWN_DISTANCE = 20f;

private void OnTriggerEnter(Collider collider)
{
if (!collider.gameObject.IsLocalPlayer)
{
return;
}

Log.Debug($"{nameof(DeathBeacon)} '{name}' despawn trigger entered by {collider.name}");
Destroy(gameObject);
}

public static void SpawnDeathBeacon(NitroxVector3 location, string playerName)
{
GameObject beacon = new($"{playerName}{nameof(DeathBeacon)}");
Comment thread
dartasen marked this conversation as resolved.
beacon.transform.position = location.ToUnity();
beacon.layer = LayerID.Trigger | LayerID.OnlyVehicle;
beacon.AddComponent<DeathBeacon>();
PingInstance signal = beacon.AddComponent<PingInstance>();
signal.IsLocalOnly = true;
signal.pingType = PingType.Signal;
signal.origin = beacon.transform;
Comment thread
OhmV-IR marked this conversation as resolved.
signal.minDist = DESPAWN_DISTANCE + 15f;
signal._label = Language.main.Get("Nitrox_PlayerDeathBeaconLabel").Replace("{PLAYER}", playerName);
signal.displayPingInManager = true;
signal.Initialize();
SphereCollider collider = beacon.AddComponent<SphereCollider>();
collider.radius = DESPAWN_DISTANCE;
collider.isTrigger = true;
}
}
11 changes: 8 additions & 3 deletions NitroxClient/MonoBehaviours/PlayerDeathBroadcaster.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NitroxClient.GameLogic;
using NitroxClient.MonoBehaviours.Gui.InGame;
using UnityEngine;

namespace NitroxClient.MonoBehaviours;
Expand All @@ -11,16 +12,20 @@ public void Awake()
{
localPlayer = this.Resolve<LocalPlayer>();

Player.main.playerDeathEvent.AddHandler(this, PlayerDeath);
Player.main.playerDeathEvent.AddHandler(this, OnPlayerDeath);
}

private void PlayerDeath(Player player)
private void OnPlayerDeath(Player player)
{
if (localPlayer.MarkDeathPointsWithBeacon)
{
DeathBeacon.SpawnDeathBeacon(player.transform.position.ToDto(), localPlayer.PlayerName);
}
localPlayer.BroadcastDeath(player.transform.position);
}

public void OnDestroy()
{
Player.main.playerDeathEvent.RemoveHandler(this, PlayerDeath);
Player.main.playerDeathEvent.RemoveHandler(this, OnPlayerDeath);
}
}
3 changes: 2 additions & 1 deletion NitroxPatcher/Patches/Dynamic/Player_OnKill_Patch.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
Expand All @@ -11,6 +11,7 @@ public sealed partial class Player_OnKill_Patch : NitroxPatch, IDynamicPatch
private static readonly MethodInfo TARGET_METHOD = Reflect.Method((Player t) => t.OnKill(default(DamageType)));

private static readonly MethodInfo SKIP_METHOD = Reflect.Method(() => GameModeUtils.IsPermadeath());

public static IEnumerable<CodeInstruction> Transpiler(MethodBase original, IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> instructionList = instructions.ToList();
Expand Down
Loading