Files
RobustToolbox/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs
metalgearsloth c17c8d7a11 Physics (#1605)
* Physics worlds

* Paul's a good boy

* Build working

* Ingame and not lagging to hell

* Why didn't you commit ahhhhh

* Hard collisions working

* Solver parity

* Decent broadphase work done

* BroadPhase outline done

* BroadPhase working

* waiting for pvs

* Fix static PVS AABB

* Stop static bodies from awakening

* Optimise a bunch of stuff

* Even more broadphase stuff

* I'm fucking stupid

* Optimise fixture updates

* Collision solver start

* Building

* A is for Argumentative

* Fix contact caching island flags

* Circle shapes actually workeded

* Damping

* DS2 consumables only

* Slightly more stable

* Even slightlier more stablier

* VV your heart out

* Initial joint support

* 90% of joints I just wanted to push as I'd scream if I lost progress

* JOINT PURGATORY

* Joints barely functional lmao

* Okay these joints slightly more functional

* Remove station FrictionJoint

* Also that

* Some Box2D ports

* Cleanup mass

* Edge shape

* Active contacts

* Fix active contacts

* Optimise active contacts even more

* Boxes be stacking

* I would die for smug oh my fucking god

* In which everything is fixed

* Distance joints working LETS GO

* Remove frequency on distancejoint

* Fix some stuff and break joints

* Crashing fixed mehbeh

* ICollideSpecial and more resilience

* auto-clear

* showbb vera

* Slap that TODO in there

* Fix restartround crash

* Random fixes

* Fix fixture networking

* Add intersection method for broadphase

* Fix contacts

* Licenses done

* Optimisations

* Fix wall clips

* Config caching for island

* allocations optimisations

* Optimise casts

* Optimise events queue for physics

* Contact manager optimisations

* Optimise controllers

* Sloth joint or something idk

* Controller graph

* Remove content cvar

* Random cleanup

* Finally remove VirtualController

* Manifold structs again

* Optimise this absolute retardation

* Optimise

* fix license

* Cleanup physics interface

* AHHHHHHHHHHHHH

* Fix collisions again

* snivybus

* Fix potential nasty manifold bug

* Tests go snivy

* Disable prediction for now

* Spans

* Fix ShapeTypes

* fixes

* ch ch changeesss

* Kinematic idea

* Prevent static bodies from waking

* Pass WorldAABB to MoveEvent

* Fix collisions

* manifold structs fucking WOOORRKKKINNGGG

* Better pushing

* Fix merge ickies

* Optimise MoveEvents

* Use event for collisions performance

* Fix content tests

* Do not research tests

* Fix most conflicts

* Paul's trying to kill me

* Maybe collisions work idk

* Make us whole again

* Smug is also trying to kill me

* nani

* shitty collisions

* Settling

* Do not research collisions

* SHIP IT

* Fix joints

* PVS moment

* Fix other assert

* Fix locker collisions

* serializable sleeptime

* Aether2D contacts

* Physics is no longer crashing (and burning)

* Add to the TODO list

Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
2021-03-08 03:19:01 +11:00

369 lines
14 KiB
C#

using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Client.Audio;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Player;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects
{
[UsedImplicitly]
public class AudioSystem : EntitySystem, IAudioSystem
{
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IClydeAudio _clyde = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
private readonly List<PlayingStream> _playingClydeStreams = new();
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<PlayAudioEntityMessage>(PlayAudioEntityHandler);
SubscribeNetworkEvent<PlayAudioGlobalMessage>(PlayAudioGlobalHandler);
SubscribeNetworkEvent<PlayAudioPositionalMessage>(PlayAudioPositionalHandler);
SubscribeNetworkEvent<StopAudioMessageClient>(StopAudioMessageHandler);
SubscribeLocalEvent<SoundSystem.QueryAudioSystem>((ev => ev.Audio = this));
_broadPhaseSystem = Get<SharedBroadPhaseSystem>();
}
private void StopAudioMessageHandler(StopAudioMessageClient ev)
{
var stream = _playingClydeStreams.Find(p => p.NetIdentifier == ev.Identifier);
if (stream == null)
{
return;
}
StreamDone(stream);
_playingClydeStreams.Remove(stream);
}
private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev)
{
var mapId = ev.Coordinates.GetMapId(_entityManager);
if (!_mapManager.MapExists(mapId))
{
Logger.Error(
$"Server tried to play sound on map {mapId}, which does not exist. Ignoring.");
return;
}
var stream = (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.AudioParams);
if (stream != null)
{
stream.NetIdentifier = ev.Identifier;
}
}
private void PlayAudioGlobalHandler(PlayAudioGlobalMessage ev)
{
var stream = (PlayingStream?) Play(ev.FileName, ev.AudioParams);
if (stream != null)
{
stream.NetIdentifier = ev.Identifier;
}
}
private void PlayAudioEntityHandler(PlayAudioEntityMessage ev)
{
var stream = EntityManager.TryGetEntity(ev.EntityUid, out var entity) ?
(PlayingStream?) Play(ev.FileName, entity, ev.AudioParams)
: (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.AudioParams);
if (stream != null)
{
stream.NetIdentifier = ev.Identifier;
}
}
public override void FrameUpdate(float frameTime)
{
// Update positions of streams every frame.
try
{
foreach (var stream in _playingClydeStreams)
{
if (!stream.Source.IsPlaying)
{
StreamDone(stream);
continue;
}
MapCoordinates? mapPos = null;
if (stream.TrackingCoordinates != null)
{
var coords = stream.TrackingCoordinates.Value;
if (_mapManager.MapExists(coords.GetMapId(_entityManager)))
{
mapPos = stream.TrackingCoordinates.Value.ToMap(_entityManager);
}
else
{
// Map no longer exists, delete stream.
StreamDone(stream);
continue;
}
}
else if (stream.TrackingEntity != null)
{
if (stream.TrackingEntity.Deleted)
{
StreamDone(stream);
continue;
}
mapPos = stream.TrackingEntity.Transform.MapPosition;
}
if (mapPos != null)
{
var pos = mapPos.Value;
if (pos.MapId != _eyeManager.CurrentMap)
{
stream.Source.SetVolume(-10000000);
}
else
{
var sourceRelative = _eyeManager.CurrentEye.Position.Position - pos.Position;
var occlusion = 0f;
if (sourceRelative.Length > 0)
{
occlusion = _broadPhaseSystem.IntersectRayPenetration(
pos.MapId,
new CollisionRay(
pos.Position,
sourceRelative.Normalized,
OcclusionCollisionMask),
sourceRelative.Length,
stream.TrackingEntity);
}
stream.Source.SetVolume(stream.Volume);
stream.Source.SetOcclusion(occlusion);
}
if (!stream.Source.SetPosition(pos.Position))
{
Logger.Warning("Interrupting positional audio, can't set position.");
stream.Source.StopPlaying();
}
}
}
}
finally
{
// if this doesn't get ran (exception...) then the list can fill up with disposed garbage.
// that will then throw on IsPlaying.
// meaning it'll break the entire audio system.
_playingClydeStreams.RemoveAll(p => p.Done);
}
}
private static void StreamDone(PlayingStream stream)
{
stream.Source.Dispose();
stream.Done = true;
}
/// <summary>
/// Play an audio file globally, without position.
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="audioParams"></param>
public IPlayingAudioStream? Play(string filename, AudioParams? audioParams = null)
{
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
{
return Play(audio, audioParams);
}
Logger.Error($"Server tried to play audio file {filename} which does not exist.");
return default;
}
/// <summary>
/// Play an audio stream globally, without position.
/// </summary>
/// <param name="stream">The audio stream to play.</param>
/// <param name="audioParams"></param>
public IPlayingAudioStream Play(AudioStream stream, AudioParams? audioParams = null)
{
var source = _clyde.CreateAudioSource(stream);
ApplyAudioParams(audioParams, source);
source.SetGlobal();
source.StartPlaying();
var playing = new PlayingStream
{
Source = source,
Volume = audioParams?.Volume ?? 0
};
_playingClydeStreams.Add(playing);
return playing;
}
/// <summary>
/// Play an audio file following an entity.
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="audioParams"></param>
public IPlayingAudioStream? Play(string filename, IEntity entity, AudioParams? audioParams = null)
{
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
{
return Play(audio, entity, audioParams);
}
Logger.Error($"Server tried to play audio file {filename} which does not exist.");
return default;
}
/// <summary>
/// Play an audio stream following an entity.
/// </summary>
/// <param name="stream">The audio stream to play.</param>
/// <param name="entity">The entity "emitting" the audio.</param>
/// <param name="audioParams"></param>
public IPlayingAudioStream? Play(AudioStream stream, IEntity entity, AudioParams? audioParams = null)
{
var source = _clyde.CreateAudioSource(stream);
if (!source.SetPosition(entity.Transform.WorldPosition))
{
source.Dispose();
Logger.Warning("Can't play positional audio, can't set position.");
return null;
}
ApplyAudioParams(audioParams, source);
source.StartPlaying();
var playing = new PlayingStream
{
Source = source,
TrackingEntity = entity,
Volume = audioParams?.Volume ?? 0
};
_playingClydeStreams.Add(playing);
return playing;
}
/// <summary>
/// Play an audio file at a static position.
/// </summary>
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
/// <param name="coordinates">The coordinates at which to play the audio.</param>
/// <param name="audioParams"></param>
public IPlayingAudioStream? Play(string filename, EntityCoordinates coordinates, AudioParams? audioParams = null)
{
if (_resourceCache.TryGetResource<AudioResource>(new ResourcePath(filename), out var audio))
{
return Play(audio, coordinates, audioParams);
}
Logger.Error($"Server tried to play audio file {filename} which does not exist.");
return default;
}
/// <summary>
/// Play an audio stream at a static position.
/// </summary>
/// <param name="stream">The audio stream to play.</param>
/// <param name="coordinates">The coordinates at which to play the audio.</param>
/// <param name="audioParams"></param>
public IPlayingAudioStream? Play(AudioStream stream, EntityCoordinates coordinates,
AudioParams? audioParams = null)
{
var source = _clyde.CreateAudioSource(stream);
if (!source.SetPosition(coordinates.ToMapPos(EntityManager)))
{
source.Dispose();
Logger.Warning("Can't play positional audio, can't set position.");
return null;
}
ApplyAudioParams(audioParams, source);
source.StartPlaying();
var playing = new PlayingStream
{
Source = source,
TrackingCoordinates = coordinates,
Volume = audioParams?.Volume ?? 0
};
_playingClydeStreams.Add(playing);
return playing;
}
private static void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source)
{
if (!audioParams.HasValue)
{
return;
}
source.SetPitch(audioParams.Value.PitchScale);
source.SetVolume(audioParams.Value.Volume);
source.SetPlaybackPosition(audioParams.Value.PlayOffsetSeconds);
source.IsLooping = audioParams.Value.Loop;
}
private class PlayingStream : IPlayingAudioStream
{
public uint? NetIdentifier;
public IClydeAudioSource Source = default!;
public IEntity TrackingEntity = default!;
public EntityCoordinates? TrackingCoordinates;
public bool Done;
public float Volume;
public void Stop()
{
Source.StopPlaying();
}
}
/// <inheritdoc />
public int DefaultSoundRange => 25;
/// <inheritdoc />
public int OcclusionCollisionMask { get; set; }
/// <inheritdoc />
public IPlayingAudioStream? Play(Filter playerFilter, string filename, AudioParams? audioParams = null)
{
return Play(filename, audioParams);
}
/// <inheritdoc />
public IPlayingAudioStream? Play(Filter playerFilter, string filename, IEntity entity, AudioParams? audioParams = null)
{
return Play(filename, entity, audioParams);
}
/// <inheritdoc />
public IPlayingAudioStream? Play(Filter playerFilter, string filename, EntityCoordinates coordinates, AudioParams? audioParams = null)
{
return Play(filename, coordinates, audioParams);
}
}
}