diff --git a/Nitrox.Assets.Subnautica/Resources/SoundWhitelist_Subnautica.csv b/Nitrox.Assets.Subnautica/Resources/SoundWhitelist_Subnautica.csv index 8126ea99e..865ae5be4 100644 --- a/Nitrox.Assets.Subnautica/Resources/SoundWhitelist_Subnautica.csv +++ b/Nitrox.Assets.Subnautica/Resources/SoundWhitelist_Subnautica.csv @@ -281,9 +281,9 @@ event:/player/enzyme_cure;false;false;0 event:/player/food_critical;false;false;0 event:/player/food_low;false;false;0 event:/player/food_very_low;false;false;0 -event:/player/footstep_dirt;false;false;0 -event:/player/footstep_metal;false;false;0 -event:/player/footstep_precursor_base;false;false;0 +event:/player/footstep_dirt;false;false;20 +event:/player/footstep_metal;false;false;20 +event:/player/footstep_precursor_base;false;false;20 event:/player/goal_airsack;false;false;0 event:/player/goal_BiomeKelpForest;false;false;0 event:/player/goal_BiomePrecursorGunUpper;false;false;0 diff --git a/Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs b/Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs index efe88723b..f0dce93df 100644 --- a/Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs +++ b/Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs @@ -1,4 +1,4 @@ -using System; +using System; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -100,6 +100,25 @@ public void OptionalHasValueDynamicChecks() cAsObj.HasValue.Should().BeTrue(); ((C)cAsObj.Value).Threshold.Should().Be(203); } + [TestMethod] + public void OptionalEqualsCheck() + { + Optional op = Optional.OfNullable(null); + Optional op1 = Optional.OfNullable(null); + Optional op2 = Optional.Of("Test"); + Optional op3 = Optional.Of("Test2"); + Optional op4 = Optional.Of("Test"); + + Assert.IsFalse(op.Equals(op2)); + Assert.IsFalse(op.Equals(op3)); + Assert.IsFalse(op2.Equals(op3)); + + Assert.IsTrue(op.Equals(op1)); + Assert.IsTrue(op2.Equals(op4)); + + Assert.IsTrue(op != op2); + Assert.IsTrue(op2 == op4); + } private class Base { diff --git a/NitroxClient/Communication/Packets/Processors/FootstepPacketProcessor.cs b/NitroxClient/Communication/Packets/Processors/FootstepPacketProcessor.cs new file mode 100644 index 000000000..e7fdb51f2 --- /dev/null +++ b/NitroxClient/Communication/Packets/Processors/FootstepPacketProcessor.cs @@ -0,0 +1,57 @@ +using FMOD; +using FMOD.Studio; +using FMODUnity; +using NitroxClient.Communication.Packets.Processors.Abstract; +using NitroxClient.GameLogic; +using NitroxClient.GameLogic.FMOD; +using NitroxModel.DataStructures.Util; +using NitroxModel.GameLogic.FMOD; +using NitroxModel.Packets; + +namespace NitroxClient.Communication.Packets.Processors; + +public class FootstepPacketProcessor : ClientPacketProcessor +{ + private readonly PlayerManager remotePlayerManager; + private readonly FootstepSounds localFootstepSounds; + private PARAMETER_ID fmodIndexSpeed = FMODUWE.invalidParameterId; + private readonly float footstepAudioRadius; // To modify this value, modify the last value in the SoundWhitelist_Subnautica.csv file + private const float footstepAudioMaxVolume = 0.5f; + + public FootstepPacketProcessor(PlayerManager remotePlayerManager, FMODWhitelist whitelist) + { + this.remotePlayerManager = remotePlayerManager; + localFootstepSounds = Player.mainObject.GetComponent(); + whitelist.TryGetSoundData("event:/player/footstep_precursor_base", out SoundData soundData); + footstepAudioRadius = soundData.Radius; + } + + public override void Process(FootstepPacket packet) + { + Optional player = remotePlayerManager.Find(packet.PlayerID); + if (player.HasValue) + { + FMODAsset asset = packet.AssetIndex switch + { + FootstepPacket.StepSounds.PRECURSOR_STEP_SOUND => localFootstepSounds.precursorInteriorSound, + FootstepPacket.StepSounds.METAL_STEP_SOUND => localFootstepSounds.metalSound, + FootstepPacket.StepSounds.LAND_STEP_SOUND => localFootstepSounds.landSound, + _ => null + }; + EventInstance evt = FMODUWE.GetEvent(asset); + if (evt.isValid()) + { + if (FMODUWE.IsInvalidParameterId(fmodIndexSpeed)) + { + fmodIndexSpeed = FMODUWE.GetEventInstanceParameterIndex(evt, "speed"); + } + ATTRIBUTES_3D attributes = player.Value.Body.To3DAttributes(); + evt.set3DAttributes(attributes); + evt.setParameterValueByIndex(fmodIndexSpeed, player.Value.AnimationController.Velocity.magnitude); + evt.setVolume(FMODSystem.CalculateVolume(Player.mainObject.transform.position, player.Value.Body.transform.position, footstepAudioRadius, footstepAudioMaxVolume)); + evt.start(); + evt.release(); + } + } + } +} diff --git a/NitroxModel/DataStructures/Optional.cs b/NitroxModel/DataStructures/Optional.cs index 657b9e68f..feb039c0e 100644 --- a/NitroxModel/DataStructures/Optional.cs +++ b/NitroxModel/DataStructures/Optional.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Security.Permissions; @@ -150,6 +150,30 @@ public static explicit operator T(Optional value) { return value.Value; } + public bool Equals(Optional other) + { + return EqualityComparer.Default.Equals(Value, other.Value); + } + + public override bool Equals(object obj) + { + return obj is Optional other && Equals(other); + } + + public override int GetHashCode() + { + return EqualityComparer.Default.GetHashCode(Value); + } + + public static bool operator ==(Optional left, Optional right) + { + return left.Equals(right); + } + + public static bool operator !=(Optional left, Optional right) + { + return !left.Equals(right); + } } diff --git a/NitroxModel/Packets/FootstepPacket.cs b/NitroxModel/Packets/FootstepPacket.cs new file mode 100644 index 000000000..fb4b1de94 --- /dev/null +++ b/NitroxModel/Packets/FootstepPacket.cs @@ -0,0 +1,23 @@ +using System; + +namespace NitroxModel.Packets; + +[Serializable] +public class FootstepPacket : Packet +{ + public ushort PlayerID { get; } + public StepSounds AssetIndex { get; } + + public FootstepPacket(ushort playerID, StepSounds assetIndex) + { + this.PlayerID = playerID; + this.AssetIndex = assetIndex; + } + + public enum StepSounds : byte + { + PRECURSOR_STEP_SOUND, + METAL_STEP_SOUND, + LAND_STEP_SOUND + } +} diff --git a/NitroxPatcher/Patches/Dynamic/FootstepSounds_OnStep_Patch.cs b/NitroxPatcher/Patches/Dynamic/FootstepSounds_OnStep_Patch.cs index d14a8124f..3eb67d140 100644 --- a/NitroxPatcher/Patches/Dynamic/FootstepSounds_OnStep_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/FootstepSounds_OnStep_Patch.cs @@ -1,12 +1,15 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using FMOD.Studio; using HarmonyLib; +using NitroxClient.Communication.Abstract; +using NitroxClient.GameLogic; using NitroxClient.GameLogic.FMOD; using NitroxModel.GameLogic.FMOD; using NitroxModel.Helper; +using NitroxModel.Packets; using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; @@ -14,6 +17,8 @@ namespace NitroxPatcher.Patches.Dynamic; public sealed partial class FootstepSounds_OnStep_Patch : NitroxPatch, IDynamicPatch { private const string EXO_STEP_SOUND_PATH = "event:/sub/exo/step"; + private const string PRECURSOR_STEP_SOUND_PATH = "event:/player/footstep_precursor_base"; + private const string METAL_STEP_SOUND_PATH = "event:/player/footstep_metal"; internal static readonly MethodInfo TARGET_METHOD = Reflect.Method((FootstepSounds t) => t.OnStep(default)); @@ -21,12 +26,18 @@ public static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable CalculateVolume(default, default, default, default))) ) + .MatchEndForward( + new CodeMatch(OpCodes.Call, Reflect.Method((EventInstance evt) => evt.release())), + new CodeMatch(OpCodes.Pop) + ) + .Advance(1) + .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_0)) + .Insert(new CodeInstruction(OpCodes.Call, Reflect.Method(() => SendFootstepPacket(default)))) .InstructionEnumeration(); } @@ -61,4 +79,26 @@ private static float CalculateVolume(float originalVolume, FootstepSounds instan Resolve().TryGetSoundData(EXO_STEP_SOUND_PATH, out SoundData soundData); return soundData.Radius; }); + + private static void SendFootstepPacket(FMODAsset asset) + { + if (Resolve().PlayerId.HasValue) + { + FootstepPacket.StepSounds assetIndex; + switch (asset.path) + { + case PRECURSOR_STEP_SOUND_PATH: + assetIndex = FootstepPacket.StepSounds.PRECURSOR_STEP_SOUND; + break; + case METAL_STEP_SOUND_PATH: + assetIndex = FootstepPacket.StepSounds.METAL_STEP_SOUND; + break; + default: + assetIndex = FootstepPacket.StepSounds.LAND_STEP_SOUND; + break; + } + FootstepPacket footstepPacket = new(Resolve().PlayerId.Value, assetIndex); + Resolve().Send(footstepPacket); + } + } } diff --git a/NitroxServer/Communication/Packets/Processors/FootstepPacketProcessor.cs b/NitroxServer/Communication/Packets/Processors/FootstepPacketProcessor.cs new file mode 100644 index 000000000..4f7d71b24 --- /dev/null +++ b/NitroxServer/Communication/Packets/Processors/FootstepPacketProcessor.cs @@ -0,0 +1,36 @@ +using NitroxModel.DataStructures.Unity; +using NitroxModel.GameLogic.FMOD; +using NitroxModel.Packets; +using NitroxServer.Communication.Packets.Processors.Abstract; +using NitroxServer.GameLogic; + +namespace NitroxServer.Communication.Packets.Processors; + +public class FootstepPacketProcessor : AuthenticatedPacketProcessor +{ + private readonly float footstepAudioRange; // To modify this value, modify the last value in the SoundWhitelist_Subnautica.csv file + private readonly PlayerManager playerManager; + + public FootstepPacketProcessor(PlayerManager playerManager, FMODWhitelist whitelist) + { + this.playerManager = playerManager; + whitelist.TryGetSoundData("event:/player/footstep_precursor_base", out SoundData soundData); + footstepAudioRange = soundData.Radius; + } + + public override void Process(FootstepPacket footstepPacket, Player sendingPlayer) + { + foreach (Player player in playerManager.GetConnectedPlayers()) + { + if (NitroxVector3.Distance(player.Position, sendingPlayer.Position) >= footstepAudioRange || + player == sendingPlayer) + { + continue; + } + if(player.SubRootId.Equals(sendingPlayer.SubRootId)) + { + player.SendPacket(footstepPacket); + } + } + } +}