Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync footstep audio #2177

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d4e3ff2
Created packet and processors for footstep audio(not functional at al…
OhmV-IR Aug 28, 2024
7e0c624
prototype client-side footstep packet processing
OhmV-IR Aug 30, 2024
380d9ba
Switch to using player's AnimationController to find velocity
OhmV-IR Aug 30, 2024
e07a5c3
Footstep packets are forwarded from server to players within hearing …
OhmV-IR Sep 2, 2024
180bb35
Fixed FootstepPacket class to fit format(was crashing)
OhmV-IR Sep 2, 2024
26db96c
Removed duplicate var
OhmV-IR Sep 2, 2024
a772c16
cleanup
OhmV-IR Sep 2, 2024
eaf1122
Added transpiler to send packets when local client plays footstep sou…
OhmV-IR Sep 2, 2024
a879656
Lower volume of other players' footsteps(suggested by NinjaPedroX)
OhmV-IR Sep 2, 2024
8ef9842
Added same structure/submarine check for sending footstep packet to o…
OhmV-IR Sep 2, 2024
092af16
Removed logs
OhmV-IR Sep 7, 2024
c6814b3
Formatting changes
OhmV-IR Sep 7, 2024
737028e
Formatting changes
OhmV-IR Sep 7, 2024
103c14e
Newline changes
OhmV-IR Sep 18, 2024
5a5fc18
newline changes
OhmV-IR Sep 18, 2024
91936ec
Split long line into shorter lines
OhmV-IR Sep 18, 2024
6545583
Remove unneeded this keyword
OhmV-IR Sep 18, 2024
33d249c
Applying suggestions from Jannify's review
OhmV-IR Sep 18, 2024
8cc0956
Merge
OhmV-IR Sep 18, 2024
c1d0ef3
Added comment in packet processors to keep values updated, trying to …
OhmV-IR Sep 18, 2024
c4c851f
Pull sound range from .csv file(UNTESTED)
OhmV-IR Sep 18, 2024
756bece
Add whitespace
OhmV-IR Sep 24, 2024
30055b8
Add whitespace
OhmV-IR Sep 24, 2024
d26a186
Merged ifs and removed var type usage
OhmV-IR Sep 24, 2024
5c6f971
Merge branch 'sync-footstep-audio' of https://github.com/OhmV-IR/Nitr…
OhmV-IR Sep 24, 2024
38c6161
Merged else + if into else if
OhmV-IR Sep 24, 2024
9a5927b
Inverted if with early return condition
OhmV-IR Sep 24, 2024
38c1aa7
Compare subroot IDs using .Equals instead of 2 if statements
OhmV-IR Sep 26, 2024
6d4dcbf
Overriding .Equals in Optional<> for consistency and performance
OhmV-IR Sep 26, 2024
bef5717
Merged 2 if statements & fixed bad clause
OhmV-IR Sep 26, 2024
7e56f40
Fixed bug(was getting all players instead of only connected ones)
OhmV-IR Sep 28, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 20 additions & 1 deletion Nitrox.Test/Model/DataStructures/Util/OptionalTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand Down Expand Up @@ -100,6 +100,25 @@ public void OptionalHasValueDynamicChecks()
cAsObj.HasValue.Should().BeTrue();
((C)cAsObj.Value).Threshold.Should().Be(203);
}
[TestMethod]
public void OptionalEqualsCheck()
{
Optional<string> op = Optional.OfNullable<string>(null);
Optional<string> op1 = Optional.OfNullable<string>(null);
Optional<string> op2 = Optional.Of("Test");
Optional<string> op3 = Optional.Of("Test2");
Optional<string> 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
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<FootstepPacket>
{
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<FootstepSounds>();
whitelist.TryGetSoundData("event:/player/footstep_precursor_base", out SoundData soundData);
footstepAudioRadius = soundData.Radius;
}

public override void Process(FootstepPacket packet)
{
Optional<RemotePlayer> 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();
}
}
}
}
26 changes: 25 additions & 1 deletion NitroxModel/DataStructures/Optional.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Security.Permissions;
Expand Down Expand Up @@ -150,6 +150,30 @@ public static explicit operator T(Optional<T> value)
{
return value.Value;
}
public bool Equals(Optional<T> other)
{
return EqualityComparer<T>.Default.Equals(Value, other.Value);
}

public override bool Equals(object obj)
{
return obj is Optional<T> other && Equals(other);
}

public override int GetHashCode()
{
return EqualityComparer<T>.Default.GetHashCode(Value);
}

public static bool operator ==(Optional<T> left, Optional<T> right)
{
return left.Equals(right);
}

public static bool operator !=(Optional<T> left, Optional<T> right)
{
return !left.Equals(right);
}
}


Expand Down
23 changes: 23 additions & 0 deletions NitroxModel/Packets/FootstepPacket.cs
Original file line number Diff line number Diff line change
@@ -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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use this if PlayerID and playerID are not the same word

this.AssetIndex = assetIndex;
}

public enum StepSounds : byte
{
PRECURSOR_STEP_SOUND,
METAL_STEP_SOUND,
LAND_STEP_SOUND
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we're repeating twice "step sound" while writing StepSounds.X_STEP_SOUND while it could probably be better as StepSounds.X

}
46 changes: 43 additions & 3 deletions NitroxPatcher/Patches/Dynamic/FootstepSounds_OnStep_Patch.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,43 @@
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;

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));

public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
/*
From:

evt.setVolume(volume);

To:

evt.setVolume(CalculateVolume(volume, this, asset, xform));


From:
event.release();

To:
event.release();
SendFootstepPacket(asset);
*/

return new CodeMatcher(instructions)
Expand All @@ -40,6 +51,13 @@ public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstructio
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Call, Reflect.Method(() => 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();
}

Expand All @@ -61,4 +79,26 @@ private static float CalculateVolume(float originalVolume, FootstepSounds instan
Resolve<FMODWhitelist>().TryGetSoundData(EXO_STEP_SOUND_PATH, out SoundData soundData);
return soundData.Radius;
});

private static void SendFootstepPacket(FMODAsset asset)
{
if (Resolve<LocalPlayer>().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<LocalPlayer>().PlayerId.Value, assetIndex);
Resolve<IPacketSender>().Send(footstepPacket);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<FootstepPacket>
{
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);
}
}
}
}