EmitSound prediction (#13282)

This commit is contained in:
Kara
2023-01-04 12:56:35 -06:00
committed by GitHub
parent ba0510b62e
commit 07360a4c95
14 changed files with 186 additions and 141 deletions

View File

@@ -0,0 +1,17 @@
using Robust.Shared.Audio;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Base sound emitter which defines most of the data fields.
/// Accepts both single sounds and sound collections.
/// </summary>
public abstract class BaseEmitSoundComponent : Component
{
public static readonly AudioParams DefaultParams = AudioParams.Default.WithVolume(-2f);
[ViewVariables(VVAccess.ReadWrite)]
[DataField("sound", required: true)]
public SoundSpecifier? Sound;
}
}

View File

@@ -0,0 +1,23 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Simple sound emitter that emits sound on ActivateInWorld
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class EmitSoundOnActivateComponent : BaseEmitSoundComponent
{
/// <summary>
/// Whether or not to mark an interaction as handled after playing the sound. Useful if this component is
/// used to play sound for some other component with activation functionality.
/// </summary>
/// <remarks>
/// If false, you should be confident that the interaction will also be handled by some other system, as
/// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was
/// handled.
/// </remarks>
[DataField("handle")]
public bool Handle = true;
}
}

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Simple sound emitter that emits sound on entity drop
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class EmitSoundOnDropComponent : BaseEmitSoundComponent
{
}
}

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Simple sound emitter that emits sound on LandEvent
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class EmitSoundOnLandComponent : BaseEmitSoundComponent
{
}
}

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Simple sound emitter that emits sound on entity pickup
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class EmitSoundOnPickupComponent : BaseEmitSoundComponent
{
}
}

View File

@@ -0,0 +1,11 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components;
/// <summary>
/// Simple sound emitter that emits sound on entity spawn.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class EmitSoundOnSpawnComponent : BaseEmitSoundComponent
{
}

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Simple sound emitter that emits sound on ThrowEvent
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class EmitSoundOnThrowComponent : BaseEmitSoundComponent
{
}
}

View File

@@ -0,0 +1,23 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sound.Components
{
/// <summary>
/// Simple sound emitter that emits sound on UseInHand
/// </summary>
[RegisterComponent]
public sealed class EmitSoundOnUseComponent : BaseEmitSoundComponent
{
/// <summary>
/// Whether or not to mark an interaction as handled after playing the sound. Useful if this component is
/// used to play sound for some other component with on-use functionality
/// </summary>
/// <remarks>
/// If false, you should be confident that the interaction will also be handled by some other system, as
/// otherwise this might enable sound spamming, as use-delays are only initiated if the interaction was
/// handled.
/// </remarks>
[DataField("handle")]
public bool Handle = true;
}
}

View File

@@ -0,0 +1,111 @@
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Maps;
using Content.Shared.Popups;
using Content.Shared.Sound.Components;
using Content.Shared.Throwing;
using JetBrains.Annotations;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Random;
namespace Content.Shared.Sound
{
/// <summary>
/// Will play a sound on various events if the affected entity has a component derived from BaseEmitSoundComponent
/// </summary>
[UsedImplicitly]
public abstract class SharedEmitSoundSystem : EntitySystem
{
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefMan = default!;
[Dependency] protected readonly IRobustRandom Random = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] protected readonly SharedPopupSystem Popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EmitSoundOnSpawnComponent, ComponentInit>(HandleEmitSpawnOnInit);
SubscribeLocalEvent<EmitSoundOnLandComponent, LandEvent>(HandleEmitSoundOnLand);
SubscribeLocalEvent<EmitSoundOnUseComponent, UseInHandEvent>(HandleEmitSoundOnUseInHand);
SubscribeLocalEvent<EmitSoundOnThrowComponent, ThrownEvent>(HandleEmitSoundOnThrown);
SubscribeLocalEvent<EmitSoundOnActivateComponent, ActivateInWorldEvent>(HandleEmitSoundOnActivateInWorld);
SubscribeLocalEvent<EmitSoundOnPickupComponent, GotEquippedHandEvent>(HandleEmitSoundOnPickup);
SubscribeLocalEvent<EmitSoundOnDropComponent, DroppedEvent>(HandleEmitSoundOnDrop);
}
private void HandleEmitSpawnOnInit(EntityUid uid, EmitSoundOnSpawnComponent component, ComponentInit args)
{
TryEmitSound(component, predict: false);
}
private void HandleEmitSoundOnLand(EntityUid uid, BaseEmitSoundComponent component, LandEvent args)
{
if (!TryComp<TransformComponent>(uid, out var xform) ||
!_mapManager.TryGetGrid(xform.GridUid, out var grid))
return;
var tile = grid.GetTileRef(xform.Coordinates);
// Handle maps being grids (we'll still emit the sound).
if (xform.GridUid != xform.MapUid && tile.IsSpace(_tileDefMan))
return;
// hand throwing not predicted sadly
TryEmitSound(component, args.User, false);
}
private void HandleEmitSoundOnUseInHand(EntityUid eUI, EmitSoundOnUseComponent component, UseInHandEvent args)
{
// Intentionally not checking whether the interaction has already been handled.
TryEmitSound(component, args.User);
if (component.Handle)
args.Handled = true;
}
private void HandleEmitSoundOnThrown(EntityUid eUI, BaseEmitSoundComponent component, ThrownEvent args)
{
TryEmitSound(component, args.User, false);
}
private void HandleEmitSoundOnActivateInWorld(EntityUid eUI, EmitSoundOnActivateComponent component, ActivateInWorldEvent args)
{
// Intentionally not checking whether the interaction has already been handled.
TryEmitSound(component, args.User);
if (component.Handle)
args.Handled = true;
}
private void HandleEmitSoundOnPickup(EntityUid uid, EmitSoundOnPickupComponent component, GotEquippedHandEvent args)
{
TryEmitSound(component, args.User);
}
private void HandleEmitSoundOnDrop(EntityUid uid, EmitSoundOnDropComponent component, DroppedEvent args)
{
TryEmitSound(component, args.User);
}
protected void TryEmitSound(BaseEmitSoundComponent component, EntityUid? user=null, bool predict=true)
{
if (component.Sound == null)
return;
if (predict)
{
_audioSystem.PlayPredicted(component.Sound, component.Owner, user, component.Sound.Params.AddVolume(-2f));
}
else if (_netMan.IsServer)
{
// don't predict sounds that client couldn't have played already
_audioSystem.PlayPvs(component.Sound, component.Owner, component.Sound.Params.AddVolume(-2f));
}
}
}
}