From e8de9b98d3ffff35d8eb71908d447bfbc7f3ce4a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 14 Mar 2024 21:54:43 +1100 Subject: [PATCH] Add basic audio limits (#4921) * Add basic audio limits * RN --------- Co-authored-by: Pieter-Jan Briers --- RELEASE-NOTES.md | 1 + Robust.Client/Audio/AudioSystem.Limits.cs | 54 +++++++++++++++++++++++ Robust.Client/Audio/AudioSystem.cs | 25 ++++++++--- Robust.Shared/CVars.cs | 6 +++ 4 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 Robust.Client/Audio/AudioSystem.Limits.cs diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 61a097332..9c4af9e9f 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -39,6 +39,7 @@ END TEMPLATE--> ### New features +* Add a basic default concurrent audio limit of 16 for a single filepath to avoid overflowing audio sources. * `NetConnectingArgs.Deny()` can now pass along structured data that will be received by the client. ### Bugfixes diff --git a/Robust.Client/Audio/AudioSystem.Limits.cs b/Robust.Client/Audio/AudioSystem.Limits.cs new file mode 100644 index 000000000..02d8c592d --- /dev/null +++ b/Robust.Client/Audio/AudioSystem.Limits.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Robust.Shared; +using Robust.Shared.GameObjects; + +namespace Robust.Client.Audio; + +public sealed partial class AudioSystem +{ + /* + * Handles limiting concurrent sounds for audio to avoid blowing the source budget on one sound getting spammed. + */ + + private readonly Dictionary _playingCount = new(); + + private int _maxConcurrent; + + private void InitializeLimit() + { + Subs.CVar(CfgManager, CVars.AudioDefaultConcurrent, SetConcurrentLimit); + } + + private void SetConcurrentLimit(int obj) + { + _maxConcurrent = obj; + } + + private bool TryAudioLimit(string sound) + { + ref var count = ref CollectionsMarshal.GetValueRefOrAddDefault(_playingCount, sound, out _); + + if (count >= _maxConcurrent) + return false; + + count++; + return true; + } + + private void RemoveAudioLimit(string sound) + { + if (!_playingCount.TryGetValue(sound, out var count)) + return; + + count--; + + if (count <= 0) + { + _playingCount.Remove(sound); + return; + } + + _playingCount[sound] = count; + } +} diff --git a/Robust.Client/Audio/AudioSystem.cs b/Robust.Client/Audio/AudioSystem.cs index f5d33beda..c34eb4d59 100644 --- a/Robust.Client/Audio/AudioSystem.cs +++ b/Robust.Client/Audio/AudioSystem.cs @@ -51,6 +51,7 @@ public sealed partial class AudioSystem : SharedAudioSystem private EntityUid? _listenerGrid; private UpdateAudioJob _updateAudioJob; + private EntityQuery _physicsQuery; private float _maxRayLength; @@ -108,6 +109,7 @@ public sealed partial class AudioSystem : SharedAudioSystem Subs.CVar(CfgManager, CVars.AudioAttenuation, OnAudioAttenuation, true); Subs.CVar(CfgManager, CVars.AudioRaycastLength, OnRaycastLengthChanged, true); + InitializeLimit(); } private void OnAudioState(EntityUid uid, AudioComponent component, ref AfterAutoHandleStateEvent args) @@ -169,16 +171,23 @@ public sealed partial class AudioSystem : SharedAudioSystem private void SetupSource(AudioComponent component, AudioResource audioResource, TimeSpan? length = null) { - var source = _audio.CreateAudioSource(audioResource); + var source = component.Source; - if (source == null) + if (TryAudioLimit(component.FileName)) { - Log.Error($"Error creating audio source for {audioResource}"); - DebugTools.Assert(false); - source = component.Source; - } + var newSource = _audio.CreateAudioSource(audioResource); - component.Source = source; + if (newSource == null) + { + Log.Error($"Error creating audio source for {audioResource}"); + DebugTools.Assert(false); + source = newSource; + } + else + { + component.Source = newSource; + } + } // Need to set all initial data for first frame. ApplyAudioParams(component.Params, component); @@ -202,6 +211,8 @@ public sealed partial class AudioSystem : SharedAudioSystem { // Breaks with prediction? component.Source.Dispose(); + + RemoveAudioLimit(component.FileName); } private void OnAudioAttenuation(int obj) diff --git a/Robust.Shared/CVars.cs b/Robust.Shared/CVars.cs index 1147d61f3..a98968983 100644 --- a/Robust.Shared/CVars.cs +++ b/Robust.Shared/CVars.cs @@ -1031,6 +1031,12 @@ namespace Robust.Shared * AUDIO */ + /// + /// Default limit for concurrently playing an audio file. + /// + public static readonly CVarDef AudioDefaultConcurrent = + CVarDef.Create("audio.default_concurrent", 16, CVar.CLIENTONLY | CVar.ARCHIVE); + public static readonly CVarDef AudioAttenuation = CVarDef.Create("audio.attenuation", (int) Attenuation.LinearDistanceClamped, CVar.REPLICATED | CVar.ARCHIVE);