Files
RobustToolbox/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs
T
Vera Aguilera PuertoandGitHub 2b589215aa Improves the Filter API. (#1747)
- Recipients is now of type `IEnumerable<ICommonSession>`.
    - This way, people need to use the Filter API to modify the recipients, instead of being allowed to modify the underlying collection directly.
- Underlying collection is now a HashSet.
    - We want no duplicates at all. This will ensure that while being quite performant.
- Removes `IFilter`.
    - Let's face it, everyone is gonna end up using `Filter` instead as it has a much more convenient API, and nothing else will EVER implement `IFilter`. For this reason, I've just gone ahead and removed it. Every method used `Filter` already, anyway.
- Adds EntitySystem `RaiseNetworkEvent` overload that takes in a `Filter`.
    - This deduplicates a lot of code that enumerated the recipients and raised a network event per each one.
- Made AddPlayersByPvs aware of `net.pvs` and `net.maxupdaterange`, has a range multiplier parameter.
    - Now it will simply add all players to the filter if PVS is disabled, and add all players in range if it's enabled.
    - The range multiplier parameter allows us to make the PVS filter a bit more permissive, and generally better. The range is doubled by default, as it was already before.
- Adds `AddInRange` method to filters.
    - This is useful for the AudioSystem, and the PVS methods use it, too!
- Adds `RemoveByVisibility` method to filters.
    - This will remove all recipients that don't have a visibility flag in their entity's EyeComponent from the filter.
    - (This will be VERY useful for Ghost Pointing, for example)
- Adds `Clone` method to filters.
    - This is useful in cases where methods take in a filter and want to modify it without modifying the original instance. (See AudioSystem)
2021-05-08 22:27:47 +02:00

139 lines
4.5 KiB
C#

using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Players;
namespace Robust.Server.GameObjects
{
[UsedImplicitly]
public class AudioSystem : EntitySystem, IAudioSystem
{
private const int AudioDistanceRange = 25;
private uint _streamIndex;
private class AudioSourceServer : IPlayingAudioStream
{
private readonly uint _id;
private readonly AudioSystem _audioSystem;
private readonly IEnumerable<ICommonSession>? _sessions;
internal AudioSourceServer(AudioSystem parent, uint identifier, IEnumerable<ICommonSession>? sessions = null)
{
_audioSystem = parent;
_id = identifier;
_sessions = sessions;
}
public void Stop()
{
_audioSystem.InternalStop(_id, _sessions);
}
}
/// <inheritdoc />
public override void Initialize()
{
SubscribeLocalEvent<SoundSystem.QueryAudioSystem>((ev => ev.Audio = this));
}
private void InternalStop(uint id, IEnumerable<ICommonSession>? sessions = null)
{
var msg = new StopAudioMessageClient
{
Identifier = id
};
if (sessions == null)
RaiseNetworkEvent(msg);
else
{
foreach (var session in sessions)
{
RaiseNetworkEvent(msg, session.ConnectedClient);
}
}
}
private uint CacheIdentifier()
{
return unchecked(_streamIndex++);
}
/// <inheritdoc />
public int DefaultSoundRange => AudioDistanceRange;
/// <inheritdoc />
public int OcclusionCollisionMask { get; set; }
/// <inheritdoc />
public IPlayingAudioStream Play(Filter playerFilter, string filename, AudioParams? audioParams = null)
{
var id = CacheIdentifier();
var msg = new PlayAudioGlobalMessage
{
FileName = filename,
AudioParams = audioParams ?? AudioParams.Default,
Identifier = id
};
RaiseNetworkEvent(msg, playerFilter);
return new AudioSourceServer(this, id, playerFilter.Recipients.ToArray());
}
/// <inheritdoc />
public IPlayingAudioStream Play(Filter playerFilter, string filename, IEntity entity, AudioParams? audioParams = null)
{
//TODO: Calculate this from PAS
var range = audioParams is null || audioParams.Value.MaxDistance <= 0 ? AudioDistanceRange : audioParams.Value.MaxDistance;
var id = CacheIdentifier();
var msg = new PlayAudioEntityMessage
{
FileName = filename,
Coordinates = entity.Transform.Coordinates,
EntityUid = entity.Uid,
AudioParams = audioParams ?? AudioParams.Default,
Identifier = id,
};
// We clone the filter here as to not modify the original instance.
if (range > 0.0f)
playerFilter = playerFilter.Clone().AddInRange(entity.Transform.MapPosition, range);
RaiseNetworkEvent(msg, playerFilter);
return new AudioSourceServer(this, id, playerFilter.Recipients.ToArray());
}
/// <inheritdoc />
public IPlayingAudioStream Play(Filter playerFilter, string filename, EntityCoordinates coordinates, AudioParams? audioParams = null)
{
//TODO: Calculate this from PAS
var range = audioParams is null || audioParams.Value.MaxDistance <= 0 ? AudioDistanceRange : audioParams.Value.MaxDistance;
var id = CacheIdentifier();
var msg = new PlayAudioPositionalMessage
{
FileName = filename,
Coordinates = coordinates,
AudioParams = audioParams ?? AudioParams.Default,
Identifier = id
};
// We clone the filter here as to not modify the original instance.
if (range > 0.0f)
playerFilter = playerFilter.Clone().AddInRange(coordinates.ToMap(EntityManager), range);
RaiseNetworkEvent(msg, playerFilter);
return new AudioSourceServer(this, id, playerFilter.Recipients.ToArray());
}
}
}