diff --git a/MSBuild/Robust.Engine.Version.props b/MSBuild/Robust.Engine.Version.props index 8893015f20a..bb21edd8f8d 100644 --- a/MSBuild/Robust.Engine.Version.props +++ b/MSBuild/Robust.Engine.Version.props @@ -1,4 +1,4 @@ - 175.0.0 + 180.1.0 diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 48c725c110f..5b168756a6c 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -54,6 +54,123 @@ END TEMPLATE--> *None yet* +## 180.1.0 + +### New features + +* Add the map name to lsmap. +* Add net.pool_size to CVars to control the message data pool size in Lidgren and to also toggle pooling. + +### Bugfixes + +* Fix physics contraints causing enormous heap allocations. +* Fix potential error when writing a runtime log. +* Fix shape lookups for non-hard fixtures in EntityLookupSystem from 180.0.0 + + +## 180.0.0 + +### Breaking changes + +* Removed some obsolete methods from EntityLookupSystem. + +### New features + +* PhysicsSystem.TryGetNearest now supports chain shapes. +* Add IPhysShape methods to EntityLookupSystem rather than relying on AABB checks. +* Add some more helper methods to SharedTransformSystem. +* Add GetOrNew dictionary extension that also returns a bool on whether the key existed. +* Add a GetAnchoredEntities overload that takes in a list. + +### Other + +* Use NetEntities for the F3 debug panel to align with command usage. + + +## 179.0.0 + +### Breaking changes + +* EyeComponent.Eye is no longer nullable + +### New features + +* Light rendering can now be enabled or disable per eye. + +### Bugfixes + +* Deserializing old maps with empty grid chunks should now just ignore those chunks. + +### Other + +* UnknownPrototypeException now also tells you the prototype kind instead of just the unkown ID. +* Adding or removing networked components while resetting predicted entities now results in a more informative exception. + + +## 178.0.0 + +### Breaking changes + +* Most methods in ActorSystem have been moved to ISharedPlayerManager. +* Several actor/player related components and events have been moved to shared. + +### New features + +* Added `NetListAsArray.Value` to the sandbox whitelist + + +## 177.0.0 + +### Breaking changes + +* Removed toInsertXform and added containerXform in SharedContainerSystem.CanInsert. +* Removed EntityQuery parameters from SharedContainerSystem.IsEntityOrParentInContainer. +* Changed the signature of ContainsEntity in SharedTransformSystem to use Entity. +* Removed one obsoleted SharedTransformSystem.AnchorEntity method. +* Changed signature of SharedTransformSystem.SetCoordinates to use Entity. + +### New features + +* Added more Entity query methods. +* Added BeforeApplyState event to replay playback. + +### Bugfixes + +* Fixed inverted GetAllMapGrids map id check. +* Fixed transform test warnings. +* Fixed PlacementManager warnings. +* Fixed reparenting bug for entities that are being deleted. + +### Other + +* Changed VerticalAlignment of RichTextLabel to Center to be consistent with Label. +* Changed PVS error log to be a warning instead. +* Marked insert and remove container methods as obsolete, added container system methods to replace them. +* Marked TransformComponent.MapPosition as obsolete, added GetMapCoordinates system method to replace it. + +### Internal + +* Moved TryGetUi/TryToggleUi/ToggleUi/TryOpen/OpenUi/TryClose/CloseUi methods from UserInterfaceSystem to SharedUserInterfaceSystem. + + +## 176.0.0 + +### Breaking changes + +* Reverted audio rework temporarily until packaging is fixed. +* Changes to Robust.Packaging to facilitate Content.Packaging ports from the python packaging scripts. + +### New features + +* Add a cvar for max game state buffer size. +* Add an overload for GetEntitiesInRange that takes in a set. + +### Bugfixes + +* Fix PVS initial list capacity always being 0. +* Fix replay lerp error spam. + + ## 175.0.0 ### Breaking changes diff --git a/Resources/EnginePrototypes/audio_presets.yml b/Resources/EnginePrototypes/audio_presets.yml deleted file mode 100644 index d13767f021c..00000000000 --- a/Resources/EnginePrototypes/audio_presets.yml +++ /dev/null @@ -1,3076 +0,0 @@ -- type: audioPreset - id: Generic - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.8913 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.8300 - decayLfRatio: 1.0000 - reflectionsGain: 0.0500 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: PaddedCell - density: 0.1715 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0010 - gainLf: 1.0000 - decayTime: 0.1700 - decayHfRatio: 0.1000 - decayLfRatio: 1.0000 - reflectionsGain: 0.2500 - reflectionsDelay: 0.0010 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2691 - lateReverbDelay: 0.0020 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Room - density: 0.4287 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.5929 - gainLf: 1.0000 - decayTime: 0.4000 - decayHfRatio: 0.8300 - decayLfRatio: 1.0000 - reflectionsGain: 0.1503 - reflectionsDelay: 0.0020 - reflectionsPan: 0,0,0 - lateReverbGain: 1.0629 - lateReverbDelay: 0.0030 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Bathroom - density: 0.1715 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.2512 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.5400 - decayLfRatio: 1.0000 - reflectionsGain: 0.6531 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 3.2734 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: LivingRoom - density: 0.9766 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0010 - gainLf: 1.0000 - decayTime: 0.5000 - decayHfRatio: 0.1000 - decayLfRatio: 1.0000 - reflectionsGain: 0.2051 - reflectionsDelay: 0.0030 - reflectionsPan: 0,0,0 - lateReverbGain: 0.2805 - lateReverbDelay: 0.0040 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: StoneRoom - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 1.0000 - decayTime: 2.3100 - decayHfRatio: 0.6400 - decayLfRatio: 1.0000 - reflectionsGain: 0.4411 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1003 - lateReverbDelay: 0.0170 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Auditorium - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.5781 - gainLf: 1.0000 - decayTime: 4.3200 - decayHfRatio: 0.5900 - decayLfRatio: 1.0000 - reflectionsGain: 0.4032 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7170 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: ConcertHall - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 1.0000 - decayTime: 3.9200 - decayHfRatio: 0.7000 - decayLfRatio: 1.0000 - reflectionsGain: 0.2427 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 0.9977 - lateReverbDelay: 0.0290 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Cave - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 1.0000 - gainLf: 1.0000 - decayTime: 2.9100 - decayHfRatio: 1.3000 - decayLfRatio: 1.0000 - reflectionsGain: 0.5000 - reflectionsDelay: 0.0150 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7063 - lateReverbDelay: 0.0220 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: Arena - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.4477 - gainLf: 1.0000 - decayTime: 7.2400 - decayHfRatio: 0.3300 - decayLfRatio: 1.0000 - reflectionsGain: 0.2612 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 1.0186 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Hangar - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.3162 - gainLf: 1.0000 - decayTime: 10.0500 - decayHfRatio: 0.2300 - decayLfRatio: 1.0000 - reflectionsGain: 0.5000 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2560 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CarpetedHallway - density: 0.4287 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0100 - gainLf: 1.0000 - decayTime: 0.3000 - decayHfRatio: 0.1000 - decayLfRatio: 1.0000 - reflectionsGain: 0.1215 - reflectionsDelay: 0.0020 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1531 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Hallway - density: 0.3645 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.5900 - decayLfRatio: 1.0000 - reflectionsGain: 0.2458 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.6615 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: StoneCorridor - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.7612 - gainLf: 1.0000 - decayTime: 2.7000 - decayHfRatio: 0.7900 - decayLfRatio: 1.0000 - reflectionsGain: 0.2472 - reflectionsDelay: 0.0130 - reflectionsPan: 0,0,0 - lateReverbGain: 1.5758 - lateReverbDelay: 0.0200 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Alley - density: 1.0000 - diffusion: 0.3000 - gain: 0.3162 - gainHf: 0.7328 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.8600 - decayLfRatio: 1.0000 - reflectionsGain: 0.2500 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 0.9954 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.1250 - echoDepth: 0.9500 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Forest - density: 1.0000 - diffusion: 0.3000 - gain: 0.3162 - gainHf: 0.0224 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.5400 - decayLfRatio: 1.0000 - reflectionsGain: 0.0525 - reflectionsDelay: 0.1620 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7682 - lateReverbDelay: 0.0880 - lateReverbPan: 0,0,0 - echoTime: 0.1250 - echoDepth: 1.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: City - density: 1.0000 - diffusion: 0.5000 - gain: 0.3162 - gainHf: 0.3981 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.6700 - decayLfRatio: 1.0000 - reflectionsGain: 0.0730 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1427 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Mountains - density: 1.0000 - diffusion: 0.2700 - gain: 0.3162 - gainHf: 0.0562 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.2100 - decayLfRatio: 1.0000 - reflectionsGain: 0.0407 - reflectionsDelay: 0.3000 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1919 - lateReverbDelay: 0.1000 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 1.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: Quarry - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.3162 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.8300 - decayLfRatio: 1.0000 - reflectionsGain: 0.0000 - reflectionsDelay: 0.0610 - reflectionsPan: 0,0,0 - lateReverbGain: 1.7783 - lateReverbDelay: 0.0250 - lateReverbPan: 0,0,0 - echoTime: 0.1250 - echoDepth: 0.7000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Plain - density: 1.0000 - diffusion: 0.2100 - gain: 0.3162 - gainHf: 0.1000 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.5000 - decayLfRatio: 1.0000 - reflectionsGain: 0.0585 - reflectionsDelay: 0.1790 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1089 - lateReverbDelay: 0.1000 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 1.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: ParkingLot - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 1.0000 - gainLf: 1.0000 - decayTime: 1.6500 - decayHfRatio: 1.5000 - decayLfRatio: 1.0000 - reflectionsGain: 0.2082 - reflectionsDelay: 0.0080 - reflectionsPan: 0,0,0 - lateReverbGain: 0.2652 - lateReverbDelay: 0.0120 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: SewerPipe - density: 0.3071 - diffusion: 0.8000 - gain: 0.3162 - gainHf: 0.3162 - gainLf: 1.0000 - decayTime: 2.8100 - decayHfRatio: 0.1400 - decayLfRatio: 1.0000 - reflectionsGain: 1.6387 - reflectionsDelay: 0.0140 - reflectionsPan: 0,0,0 - lateReverbGain: 3.2471 - lateReverbDelay: 0.0210 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Underwater - density: 0.3645 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0100 - gainLf: 1.0000 - decayTime: 1.4900 - decayHfRatio: 0.1000 - decayLfRatio: 1.0000 - reflectionsGain: 0.5963 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 7.0795 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 1.1800 - modulationDepth: 0.3480 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Drugged - density: 0.4287 - diffusion: 0.5000 - gain: 0.3162 - gainHf: 1.0000 - gainLf: 1.0000 - decayTime: 8.3900 - decayHfRatio: 1.3900 - decayLfRatio: 1.0000 - reflectionsGain: 0.8760 - reflectionsDelay: 0.0020 - reflectionsPan: 0,0,0 - lateReverbGain: 3.1081 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 1.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: Dizzy - density: 0.3645 - diffusion: 0.6000 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 1.0000 - decayTime: 17.2300 - decayHfRatio: 0.5600 - decayLfRatio: 1.0000 - reflectionsGain: 0.1392 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 0.4937 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 1.0000 - modulationTime: 0.8100 - modulationDepth: 0.3100 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: Psychotic - density: 0.0625 - diffusion: 0.5000 - gain: 0.3162 - gainHf: 0.8404 - gainLf: 1.0000 - decayTime: 7.5600 - decayHfRatio: 0.9100 - decayLfRatio: 1.0000 - reflectionsGain: 0.4864 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 2.4378 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 4.0000 - modulationDepth: 1.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -# Castle Presets - -- type: audioPreset - id: CastleSmallRoom - density: 1.0000 - diffusion: 0.8900 - gain: 0.3162 - gainHf: 0.3981 - gainLf: 0.1000 - decayTime: 1.2200 - decayHfRatio: 0.8300 - decayLfRatio: 0.3100 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0220 - reflectionsPan: 0,0,0 - lateReverbGain: 1.9953 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.1380 - echoDepth: 0.0800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleShortPassage - density: 1.0000 - diffusion: 0.8900 - gain: 0.3162 - gainHf: 0.3162 - gainLf: 0.1000 - decayTime: 2.3200 - decayHfRatio: 0.8300 - decayLfRatio: 0.3100 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0230 - lateReverbPan: 0,0,0 - echoTime: 0.1380 - echoDepth: 0.0800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleMediumRoom - density: 1.0000 - diffusion: 0.9300 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.1000 - decayTime: 2.0400 - decayHfRatio: 0.8300 - decayLfRatio: 0.4600 - reflectionsGain: 0.6310 - reflectionsDelay: 0.0220 - reflectionsPan: 0,0,0 - lateReverbGain: 1.5849 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.1550 - echoDepth: 0.0300 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleLargeRoom - density: 1.0000 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.1259 - decayTime: 2.5300 - decayHfRatio: 0.8300 - decayLfRatio: 0.5000 - reflectionsGain: 0.4467 - reflectionsDelay: 0.0340 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0160 - lateReverbPan: 0,0,0 - echoTime: 0.1850 - echoDepth: 0.0700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleLongPassage - density: 1.0000 - diffusion: 0.8900 - gain: 0.3162 - gainHf: 0.3981 - gainLf: 0.1000 - decayTime: 3.4200 - decayHfRatio: 0.8300 - decayLfRatio: 0.3100 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0230 - lateReverbPan: 0,0,0 - echoTime: 0.1380 - echoDepth: 0.0800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleHall - density: 1.0000 - diffusion: 0.8100 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.1778 - decayTime: 3.1400 - decayHfRatio: 0.7900 - decayLfRatio: 0.6200 - reflectionsGain: 0.1778 - reflectionsDelay: 0.0560 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0240 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleCupboard - density: 1.0000 - diffusion: 0.8900 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.1000 - decayTime: 0.6700 - decayHfRatio: 0.8700 - decayLfRatio: 0.3100 - reflectionsGain: 1.4125 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 3.5481 - lateReverbDelay: 0.0070 - lateReverbPan: 0,0,0 - echoTime: 0.1380 - echoDepth: 0.0800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CastleCourtyard - density: 1.0000 - diffusion: 0.4200 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.1995 - decayTime: 2.1300 - decayHfRatio: 0.6100 - decayLfRatio: 0.2300 - reflectionsGain: 0.2239 - reflectionsDelay: 0.1600 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7079 - lateReverbDelay: 0.0360 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.3700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: CastleAlcove - density: 1.0000 - diffusion: 0.8900 - gain: 0.3162 - gainHf: 0.5012 - gainLf: 0.1000 - decayTime: 1.6400 - decayHfRatio: 0.8700 - decayLfRatio: 0.3100 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0340 - lateReverbPan: 0,0,0 - echoTime: 0.1380 - echoDepth: 0.0800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5168.6001 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Factory Presets - -- type: audioPreset - id: FactorySmallRoom - density: 0.3645 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.7943 - gainLf: 0.5012 - decayTime: 1.7200 - decayHfRatio: 0.6500 - decayLfRatio: 1.3100 - reflectionsGain: 0.7079 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.7783 - lateReverbDelay: 0.0240 - lateReverbPan: 0,0,0 - echoTime: 0.1190 - echoDepth: 0.0700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryShortPassage - density: 0.3645 - diffusion: 0.6400 - gain: 0.2512 - gainHf: 0.7943 - gainLf: 0.5012 - decayTime: 2.5300 - decayHfRatio: 0.6500 - decayLfRatio: 1.3100 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0380 - lateReverbPan: 0,0,0 - echoTime: 0.1350 - echoDepth: 0.2300 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryMediumRoom - density: 0.4287 - diffusion: 0.8200 - gain: 0.2512 - gainHf: 0.7943 - gainLf: 0.5012 - decayTime: 2.7600 - decayHfRatio: 0.6500 - decayLfRatio: 1.3100 - reflectionsGain: 0.2818 - reflectionsDelay: 0.0220 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0230 - lateReverbPan: 0,0,0 - echoTime: 0.1740 - echoDepth: 0.0700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryLargeRoom - density: 0.4287 - diffusion: 0.7500 - gain: 0.2512 - gainHf: 0.7079 - gainLf: 0.6310 - decayTime: 4.2400 - decayHfRatio: 0.5100 - decayLfRatio: 1.3100 - reflectionsGain: 0.1778 - reflectionsDelay: 0.0390 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0230 - lateReverbPan: 0,0,0 - echoTime: 0.2310 - echoDepth: 0.0700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryLongPassage - density: 0.3645 - diffusion: 0.6400 - gain: 0.2512 - gainHf: 0.7943 - gainLf: 0.5012 - decayTime: 4.0600 - decayHfRatio: 0.6500 - decayLfRatio: 1.3100 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0370 - lateReverbPan: 0,0,0 - echoTime: 0.1350 - echoDepth: 0.2300 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryHall - density: 0.4287 - diffusion: 0.7500 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.6310 - decayTime: 7.4300 - decayHfRatio: 0.5100 - decayLfRatio: 1.3100 - reflectionsGain: 0.0631 - reflectionsDelay: 0.0730 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0270 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryCupboard - density: 0.3071 - diffusion: 0.6300 - gain: 0.2512 - gainHf: 0.7943 - gainLf: 0.5012 - decayTime: 0.4900 - decayHfRatio: 0.6500 - decayLfRatio: 1.3100 - reflectionsGain: 1.2589 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.9953 - lateReverbDelay: 0.0320 - lateReverbPan: 0,0,0 - echoTime: 0.1070 - echoDepth: 0.0700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryCourtyard - density: 0.3071 - diffusion: 0.5700 - gain: 0.3162 - gainHf: 0.3162 - gainLf: 0.6310 - decayTime: 2.3200 - decayHfRatio: 0.2900 - decayLfRatio: 0.5600 - reflectionsGain: 0.2239 - reflectionsDelay: 0.1400 - reflectionsPan: 0,0,0 - lateReverbGain: 0.3981 - lateReverbDelay: 0.0390 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.2900 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: FactoryAlcove - density: 0.3645 - diffusion: 0.5900 - gain: 0.2512 - gainHf: 0.7943 - gainLf: 0.5012 - decayTime: 3.1400 - decayHfRatio: 0.6500 - decayLfRatio: 1.3100 - reflectionsGain: 1.4125 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.0000 - lateReverbDelay: 0.0380 - lateReverbPan: 0,0,0 - echoTime: 0.1140 - echoDepth: 0.1000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3762.6001 - lfReference: 362.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Ice Palace Presets - -- type: audioPreset - id: IcePalaceSmallRoom - density: 1.0000 - diffusion: 0.8400 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.2818 - decayTime: 1.5100 - decayHfRatio: 1.5300 - decayLfRatio: 0.2700 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.1640 - echoDepth: 0.1400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceShortPassage - density: 1.0000 - diffusion: 0.7500 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.2818 - decayTime: 1.7900 - decayHfRatio: 1.4600 - decayLfRatio: 0.2800 - reflectionsGain: 0.5012 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0190 - lateReverbPan: 0,0,0 - echoTime: 0.1770 - echoDepth: 0.0900 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceMediumRoom - density: 1.0000 - diffusion: 0.8700 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.4467 - decayTime: 2.2200 - decayHfRatio: 1.5300 - decayLfRatio: 0.3200 - reflectionsGain: 0.3981 - reflectionsDelay: 0.0390 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0270 - lateReverbPan: 0,0,0 - echoTime: 0.1860 - echoDepth: 0.1200 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceLargeRoom - density: 1.0000 - diffusion: 0.8100 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.4467 - decayTime: 3.1400 - decayHfRatio: 1.5300 - decayLfRatio: 0.3200 - reflectionsGain: 0.2512 - reflectionsDelay: 0.0390 - reflectionsPan: 0,0,0 - lateReverbGain: 1.0000 - lateReverbDelay: 0.0270 - lateReverbPan: 0,0,0 - echoTime: 0.2140 - echoDepth: 0.1100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceLongPassage - density: 1.0000 - diffusion: 0.7700 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.3981 - decayTime: 3.0100 - decayHfRatio: 1.4600 - decayLfRatio: 0.2800 - reflectionsGain: 0.7943 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0250 - lateReverbPan: 0,0,0 - echoTime: 0.1860 - echoDepth: 0.0400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceHall - density: 1.0000 - diffusion: 0.7600 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.5623 - decayTime: 5.4900 - decayHfRatio: 1.5300 - decayLfRatio: 0.3800 - reflectionsGain: 0.1122 - reflectionsDelay: 0.0540 - reflectionsPan: 0,0,0 - lateReverbGain: 0.6310 - lateReverbDelay: 0.0520 - lateReverbPan: 0,0,0 - echoTime: 0.2260 - echoDepth: 0.1100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceCupboard - density: 1.0000 - diffusion: 0.8300 - gain: 0.3162 - gainHf: 0.5012 - gainLf: 0.2239 - decayTime: 0.7600 - decayHfRatio: 1.5300 - decayLfRatio: 0.2600 - reflectionsGain: 1.1220 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.9953 - lateReverbDelay: 0.0160 - lateReverbPan: 0,0,0 - echoTime: 0.1430 - echoDepth: 0.0800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceCourtyard - density: 1.0000 - diffusion: 0.5900 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.3162 - decayTime: 2.0400 - decayHfRatio: 1.2000 - decayLfRatio: 0.3800 - reflectionsGain: 0.3162 - reflectionsDelay: 0.1730 - reflectionsPan: 0,0,0 - lateReverbGain: 0.3162 - lateReverbDelay: 0.0430 - lateReverbPan: 0,0,0 - echoTime: 0.2350 - echoDepth: 0.4800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: IcePalaceAlcove - density: 1.0000 - diffusion: 0.8400 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.2818 - decayTime: 2.7600 - decayHfRatio: 1.4600 - decayLfRatio: 0.2800 - reflectionsGain: 1.1220 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.1610 - echoDepth: 0.0900 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 12428.5000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Space Station Presets - -- type: audioPreset - id: SpaceStationSmallRoom - density: 0.2109 - diffusion: 0.7000 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.8913 - decayTime: 1.7200 - decayHfRatio: 0.8200 - decayLfRatio: 0.5500 - reflectionsGain: 0.7943 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0130 - lateReverbPan: 0,0,0 - echoTime: 0.1880 - echoDepth: 0.2600 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationShortPassage - density: 0.2109 - diffusion: 0.8700 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.8913 - decayTime: 3.5700 - decayHfRatio: 0.5000 - decayLfRatio: 0.5500 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0160 - lateReverbPan: 0,0,0 - echoTime: 0.1720 - echoDepth: 0.2000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationMediumRoom - density: 0.2109 - diffusion: 0.7500 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.8913 - decayTime: 3.0100 - decayHfRatio: 0.5000 - decayLfRatio: 0.5500 - reflectionsGain: 0.3981 - reflectionsDelay: 0.0340 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0350 - lateReverbPan: 0,0,0 - echoTime: 0.2090 - echoDepth: 0.3100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationLargeRoom - density: 0.3645 - diffusion: 0.8100 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.8913 - decayTime: 3.8900 - decayHfRatio: 0.3800 - decayLfRatio: 0.6100 - reflectionsGain: 0.3162 - reflectionsDelay: 0.0560 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0350 - lateReverbPan: 0,0,0 - echoTime: 0.2330 - echoDepth: 0.2800 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationLongPassage - density: 0.4287 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.8913 - decayTime: 4.6200 - decayHfRatio: 0.6200 - decayLfRatio: 0.5500 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0310 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.2300 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationHall - density: 0.4287 - diffusion: 0.8700 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.8913 - decayTime: 7.1100 - decayHfRatio: 0.3800 - decayLfRatio: 0.6100 - reflectionsGain: 0.1778 - reflectionsDelay: 0.1000 - reflectionsPan: 0,0,0 - lateReverbGain: 0.6310 - lateReverbDelay: 0.0470 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.2500 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationCupboard - density: 0.1715 - diffusion: 0.5600 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.8913 - decayTime: 0.7900 - decayHfRatio: 0.8100 - decayLfRatio: 0.5500 - reflectionsGain: 1.4125 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.7783 - lateReverbDelay: 0.0180 - lateReverbPan: 0,0,0 - echoTime: 0.1810 - echoDepth: 0.3100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SpaceStationAlcove - density: 0.2109 - diffusion: 0.7800 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.8913 - decayTime: 1.1600 - decayHfRatio: 0.8100 - decayLfRatio: 0.5500 - reflectionsGain: 1.4125 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 1.0000 - lateReverbDelay: 0.0180 - lateReverbPan: 0,0,0 - echoTime: 0.1920 - echoDepth: 0.2100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 3316.1001 - lfReference: 458.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Wooden Galleon Presets - -- type: audioPreset - id: WoodenSmallRoom - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1122 - gainLf: 0.3162 - decayTime: 0.7900 - decayHfRatio: 0.3200 - decayLfRatio: 0.8700 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0320 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0290 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenShortPassage - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1259 - gainLf: 0.3162 - decayTime: 1.7500 - decayHfRatio: 0.5000 - decayLfRatio: 0.8700 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 0.6310 - lateReverbDelay: 0.0240 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenMediumRoom - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1000 - gainLf: 0.2818 - decayTime: 1.4700 - decayHfRatio: 0.4200 - decayLfRatio: 0.8200 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0490 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0290 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenLargeRoom - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0891 - gainLf: 0.2818 - decayTime: 2.6500 - decayHfRatio: 0.3300 - decayLfRatio: 0.8200 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0660 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7943 - lateReverbDelay: 0.0490 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenLongPassage - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1000 - gainLf: 0.3162 - decayTime: 1.9900 - decayHfRatio: 0.4000 - decayLfRatio: 0.7900 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 0.4467 - lateReverbDelay: 0.0360 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenHall - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0794 - gainLf: 0.2818 - decayTime: 3.4500 - decayHfRatio: 0.3000 - decayLfRatio: 0.8200 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0880 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7943 - lateReverbDelay: 0.0630 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenCupboard - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1413 - gainLf: 0.3162 - decayTime: 0.5600 - decayHfRatio: 0.4600 - decayLfRatio: 0.9100 - reflectionsGain: 1.1220 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0280 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenCourtyard - density: 1.0000 - diffusion: 0.6500 - gain: 0.3162 - gainHf: 0.0794 - gainLf: 0.3162 - decayTime: 1.7900 - decayHfRatio: 0.3500 - decayLfRatio: 0.7900 - reflectionsGain: 0.5623 - reflectionsDelay: 0.1230 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1000 - lateReverbDelay: 0.0320 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: WoodenAlcove - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1259 - gainLf: 0.3162 - decayTime: 1.2200 - decayHfRatio: 0.6200 - decayLfRatio: 0.9100 - reflectionsGain: 1.1220 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7079 - lateReverbDelay: 0.0240 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4705.0000 - lfReference: 99.6000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Sports Presets - -- type: audioPreset - id: SportEmptyStadium - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.7943 - decayTime: 6.2600 - decayHfRatio: 0.5100 - decayLfRatio: 1.1000 - reflectionsGain: 0.0631 - reflectionsDelay: 0.1830 - reflectionsPan: 0,0,0 - lateReverbGain: 0.3981 - lateReverbDelay: 0.0380 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SportSquashCourt - density: 1.0000 - diffusion: 0.7500 - gain: 0.3162 - gainHf: 0.3162 - gainLf: 0.7943 - decayTime: 2.2200 - decayHfRatio: 0.9100 - decayLfRatio: 1.1600 - reflectionsGain: 0.4467 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7943 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.1260 - echoDepth: 0.1900 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 7176.8999 - lfReference: 211.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SportSmallSwimmingPool - density: 1.0000 - diffusion: 0.7000 - gain: 0.3162 - gainHf: 0.7943 - gainLf: 0.8913 - decayTime: 2.7600 - decayHfRatio: 1.2500 - decayLfRatio: 1.1400 - reflectionsGain: 0.6310 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7943 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.1790 - echoDepth: 0.1500 - modulationTime: 0.8950 - modulationDepth: 0.1900 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: SportLargeSwimmingPool - density: 1.0000 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.7943 - gainLf: 1.0000 - decayTime: 5.4900 - decayHfRatio: 1.3100 - decayLfRatio: 1.1400 - reflectionsGain: 0.4467 - reflectionsDelay: 0.0390 - reflectionsPan: 0,0,0 - lateReverbGain: 0.5012 - lateReverbDelay: 0.0490 - lateReverbPan: 0,0,0 - echoTime: 0.2220 - echoDepth: 0.5500 - modulationTime: 1.1590 - modulationDepth: 0.2100 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: SportGymnasium - density: 1.0000 - diffusion: 0.8100 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.8913 - decayTime: 3.1400 - decayHfRatio: 1.0600 - decayLfRatio: 1.3500 - reflectionsGain: 0.3981 - reflectionsDelay: 0.0290 - reflectionsPan: 0,0,0 - lateReverbGain: 0.5623 - lateReverbDelay: 0.0450 - lateReverbPan: 0,0,0 - echoTime: 0.1460 - echoDepth: 0.1400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 7176.8999 - lfReference: 211.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SportFullStadium - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0708 - gainLf: 0.7943 - decayTime: 5.2500 - decayHfRatio: 0.1700 - decayLfRatio: 0.8000 - reflectionsGain: 0.1000 - reflectionsDelay: 0.1880 - reflectionsPan: 0,0,0 - lateReverbGain: 0.2818 - lateReverbDelay: 0.0380 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SportStadiumTannoy - density: 1.0000 - diffusion: 0.7800 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.5012 - decayTime: 2.5300 - decayHfRatio: 0.8800 - decayLfRatio: 0.6800 - reflectionsGain: 0.2818 - reflectionsDelay: 0.2300 - reflectionsPan: 0,0,0 - lateReverbGain: 0.5012 - lateReverbDelay: 0.0630 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.2000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Prefab Presets - -- type: audioPreset - id: PrefabWorkshop - density: 0.4287 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1413 - gainLf: 0.3981 - decayTime: 0.7600 - decayHfRatio: 1.0000 - decayLfRatio: 1.0000 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0120 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: PrefabSchoolroom - density: 0.4022 - diffusion: 0.6900 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.5012 - decayTime: 0.9800 - decayHfRatio: 0.4500 - decayLfRatio: 0.1800 - reflectionsGain: 1.4125 - reflectionsDelay: 0.0170 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0150 - lateReverbPan: 0,0,0 - echoTime: 0.0950 - echoDepth: 0.1400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 7176.8999 - lfReference: 211.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: PrefabPractiseRoom - density: 0.4022 - diffusion: 0.8700 - gain: 0.3162 - gainHf: 0.3981 - gainLf: 0.5012 - decayTime: 1.1200 - decayHfRatio: 0.5600 - decayLfRatio: 0.1800 - reflectionsGain: 1.2589 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0110 - lateReverbPan: 0,0,0 - echoTime: 0.0950 - echoDepth: 0.1400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 7176.8999 - lfReference: 211.2000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: PrefabOuthouse - density: 1.0000 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.1122 - gainLf: 0.1585 - decayTime: 1.3800 - decayHfRatio: 0.3800 - decayLfRatio: 0.3500 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0240 - reflectionsPan: 0, 0, 0 - lateReverbGain: 0.6310 - lateReverbDelay: 0.0440 - lateReverbPan: 0,0,0 - echoTime: 0.1210 - echoDepth: 0.1700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 107.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: PrefabCaravan - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.0891 - gainLf: 0.1259 - decayTime: 0.4300 - decayHfRatio: 1.5000 - decayLfRatio: 1.0000 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0120 - reflectionsPan: 0,0,0 - lateReverbGain: 1.9953 - lateReverbDelay: 0.0120 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -# Dome and Pipe Presets - -- type: audioPreset - id: DomeTomb - density: 1.0000 - diffusion: 0.7900 - gain: 0.3162 - gainHf: 0.3548 - gainLf: 0.2239 - decayTime: 4.1800 - decayHfRatio: 0.2100 - decayLfRatio: 0.1000 - reflectionsGain: 0.3868 - reflectionsDelay: 0.0300 - reflectionsPan: 0,0,0 - lateReverbGain: 1.6788 - lateReverbDelay: 0.0220 - lateReverbPan: 0,0,0 - echoTime: 0.1770 - echoDepth: 0.1900 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 20.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: PipeSmall - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.3548 - gainLf: 0.2239 - decayTime: 5.0400 - decayHfRatio: 0.1000 - decayLfRatio: 0.1000 - reflectionsGain: 0.5012 - reflectionsDelay: 0.0320 - reflectionsPan: 0,0,0 - lateReverbGain: 2.5119 - lateReverbDelay: 0.0150 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 20.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: DomeSaintPauls - density: 1.0000 - diffusion: 0.8700 - gain: 0.3162 - gainHf: 0.3548 - gainLf: 0.2239 - decayTime: 10.4800 - decayHfRatio: 0.1900 - decayLfRatio: 0.1000 - reflectionsGain: 0.1778 - reflectionsDelay: 0.0900 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0420 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.1200 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 20.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: PipeLongThin - density: 0.2560 - diffusion: 0.9100 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.2818 - decayTime: 9.2100 - decayHfRatio: 0.1800 - decayLfRatio: 0.1000 - reflectionsGain: 0.7079 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7079 - lateReverbDelay: 0.0220 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 20.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: PipeLarge - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.3548 - gainLf: 0.2239 - decayTime: 8.4500 - decayHfRatio: 0.1000 - decayLfRatio: 0.1000 - reflectionsGain: 0.3981 - reflectionsDelay: 0.0460 - reflectionsPan: 0,0,0 - lateReverbGain: 1.5849 - lateReverbDelay: 0.0320 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 20.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: PipeResonant - density: 0.1373 - diffusion: 0.9100 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.2818 - decayTime: 6.8100 - decayHfRatio: 0.1800 - decayLfRatio: 0.1000 - reflectionsGain: 0.7079 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.0000 - lateReverbDelay: 0.0220 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 20.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -# Outdoors Presets - -- type: audioPreset - id: OutdoorsBackyard - density: 1.0000 - diffusion: 0.4500 - gain: 0.3162 - gainHf: 0.2512 - gainLf: 0.5012 - decayTime: 1.1200 - decayHfRatio: 0.3400 - decayLfRatio: 0.4600 - reflectionsGain: 0.4467 - reflectionsDelay: 0.0690 - reflectionsPan: 0, 0, 0 - lateReverbGain: 0.7079 - lateReverbDelay: 0.0230 - lateReverbPan: 0,0,0 - echoTime: 0.2180 - echoDepth: 0.3400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4399.1001 - lfReference: 242.9000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: OutdoorsRollingPlains - density: 1.0000 - diffusion: 0.0000 - gain: 0.3162 - gainHf: 0.0112 - gainLf: 0.6310 - decayTime: 2.1300 - decayHfRatio: 0.2100 - decayLfRatio: 0.4600 - reflectionsGain: 0.1778 - reflectionsDelay: 0.3000 - reflectionsPan: 0,0,0 - lateReverbGain: 0.4467 - lateReverbDelay: 0.0190 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 1.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4399.1001 - lfReference: 242.9000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: OutdoorsDeepCanyon - density: 1.0000 - diffusion: 0.7400 - gain: 0.3162 - gainHf: 0.1778 - gainLf: 0.6310 - decayTime: 3.8900 - decayHfRatio: 0.2100 - decayLfRatio: 0.4600 - reflectionsGain: 0.3162 - reflectionsDelay: 0.2230 - reflectionsPan: 0,0,0 - lateReverbGain: 0.3548 - lateReverbDelay: 0.0190 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 1.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4399.1001 - lfReference: 242.9000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: OutdoorsCreek - density: 1.0000 - diffusion: 0.3500 - gain: 0.3162 - gainHf: 0.1778 - gainLf: 0.5012 - decayTime: 2.1300 - decayHfRatio: 0.2100 - decayLfRatio: 0.4600 - reflectionsGain: 0.3981 - reflectionsDelay: 0.1150 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1995 - lateReverbDelay: 0.0310 - lateReverbPan: 0,0,0 - echoTime: 0.2180 - echoDepth: 0.3400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 4399.1001 - lfReference: 242.9000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: OutdoorsValley - density: 1.0000 - diffusion: 0.2800 - gain: 0.3162 - gainHf: 0.0282 - gainLf: 0.1585 - decayTime: 2.8800 - decayHfRatio: 0.2600 - decayLfRatio: 0.3500 - reflectionsGain: 0.1413 - reflectionsDelay: 0.2630 - reflectionsPan: 0,0,0 - lateReverbGain: 0.3981 - lateReverbDelay: 0.1000 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.3400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 107.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -# Mood Presets - -- type: audioPreset - id: MoodHeaven - density: 1.0000 - diffusion: 0.9400 - gain: 0.3162 - gainHf: 0.7943 - gainLf: 0.4467 - decayTime: 5.0400 - decayHfRatio: 1.1200 - decayLfRatio: 0.5600 - reflectionsGain: 0.2427 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0290 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0800 - modulationTime: 2.7420 - modulationDepth: 0.0500 - airAbsorptionGainHf: 0.9977 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: MoodHell - density: 1.0000 - diffusion: 0.5700 - gain: 0.3162 - gainHf: 0.3548 - gainLf: 0.4467 - decayTime: 3.5700 - decayHfRatio: 0.4900 - decayLfRatio: 2.0000 - reflectionsGain: 0.0000 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.1100 - echoDepth: 0.0400 - modulationTime: 2.1090 - modulationDepth: 0.5200 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 139.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: MoodMemory - density: 1.0000 - diffusion: 0.8500 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 0.3548 - decayTime: 4.0600 - decayHfRatio: 0.8200 - decayLfRatio: 0.5600 - reflectionsGain: 0.0398 - reflectionsDelay: 0.0000 - reflectionsPan: 0,0,0 - lateReverbGain: 1.1220 - lateReverbDelay: 0.0000 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.4740 - modulationDepth: 0.4500 - airAbsorptionGainHf: 0.9886 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -# Driving Presets - -- type: audioPreset - id: DrivingCommentator - density: 1.0000 - diffusion: 0.0000 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 0.5012 - decayTime: 2.4200 - decayHfRatio: 0.8800 - decayLfRatio: 0.6800 - reflectionsGain: 0.1995 - reflectionsDelay: 0.0930 - reflectionsPan: 0,0,0 - lateReverbGain: 0.2512 - lateReverbDelay: 0.0170 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 1.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9886 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: DrivingPitGarage - density: 0.4287 - diffusion: 0.5900 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.5623 - decayTime: 1.7200 - decayHfRatio: 0.9300 - decayLfRatio: 0.8700 - reflectionsGain: 0.5623 - reflectionsDelay: 0.0000 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0160 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.1100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: DrivingInCarRacer - density: 0.0832 - diffusion: 0.8000 - gain: 0.3162 - gainHf: 1.0000 - gainLf: 0.7943 - decayTime: 0.1700 - decayHfRatio: 2.0000 - decayLfRatio: 0.4100 - reflectionsGain: 1.7783 - reflectionsDelay: 0.0070 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7079 - lateReverbDelay: 0.0150 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 10268.2002 - lfReference: 251.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: DrivingInCarSports - density: 0.0832 - diffusion: 0.8000 - gain: 0.3162 - gainHf: 0.6310 - gainLf: 1.0000 - decayTime: 0.1700 - decayHfRatio: 0.7500 - decayLfRatio: 0.4100 - reflectionsGain: 1.0000 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 0.5623 - lateReverbDelay: 0.0000 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 10268.2002 - lfReference: 251.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: DrivingInCarLuxury - density: 0.2560 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.1000 - gainLf: 0.5012 - decayTime: 0.1300 - decayHfRatio: 0.4100 - decayLfRatio: 0.4600 - reflectionsGain: 0.7943 - reflectionsDelay: 0.0100 - reflectionsPan: 0,0,0 - lateReverbGain: 1.5849 - lateReverbDelay: 0.0100 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 10268.2002 - lfReference: 251.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: DrivingFullGrandstand - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.6310 - decayTime: 3.0100 - decayHfRatio: 1.3700 - decayLfRatio: 1.2800 - reflectionsGain: 0.3548 - reflectionsDelay: 0.0900 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1778 - lateReverbDelay: 0.0490 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 10420.2002 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: DrivingEmptyGrandstand - density: 1.0000 - diffusion: 1.0000 - gain: 0.3162 - gainHf: 1.0000 - gainLf: 0.7943 - decayTime: 4.6200 - decayHfRatio: 1.7500 - decayLfRatio: 1.4000 - reflectionsGain: 0.2082 - reflectionsDelay: 0.0900 - reflectionsPan: 0,0,0 - lateReverbGain: 0.2512 - lateReverbDelay: 0.0490 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 10420.2002 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: DrivingTunnel - density: 1.0000 - diffusion: 0.8100 - gain: 0.3162 - gainHf: 0.3981 - gainLf: 0.8913 - decayTime: 3.4200 - decayHfRatio: 0.9400 - decayLfRatio: 1.3100 - reflectionsGain: 0.7079 - reflectionsDelay: 0.0510 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7079 - lateReverbDelay: 0.0470 - lateReverbPan: 0,0,0 - echoTime: 0.2140 - echoDepth: 0.0500 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 155.3000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# City Presets - -- type: audioPreset - id: CityStreets - density: 1.0000 - diffusion: 0.7800 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.8913 - decayTime: 1.7900 - decayHfRatio: 1.1200 - decayLfRatio: 0.9100 - reflectionsGain: 0.2818 - reflectionsDelay: 0.0460 - reflectionsPan: 0,0,0 - lateReverbGain: 0.1995 - lateReverbDelay: 0.0280 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.2000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CitySubway - density: 1.0000 - diffusion: 0.7400 - gain: 0.3162 - gainHf: 0.7079 - gainLf: 0.8913 - decayTime: 3.0100 - decayHfRatio: 1.2300 - decayLfRatio: 0.9100 - reflectionsGain: 0.7079 - reflectionsDelay: 0.0460 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0280 - lateReverbPan: 0,0,0 - echoTime: 0.1250 - echoDepth: 0.2100 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CityMuseum - density: 1.0000 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.1778 - gainLf: 0.1778 - decayTime: 3.2800 - decayHfRatio: 1.4000 - decayLfRatio: 0.5700 - reflectionsGain: 0.2512 - reflectionsDelay: 0.0390 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0340 - lateReverbPan: 0,0,0 - echoTime: 0.1300 - echoDepth: 0.1700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 107.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: CityLibrary - density: 1.0000 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.2818 - gainLf: 0.0891 - decayTime: 2.7600 - decayHfRatio: 0.8900 - decayLfRatio: 0.4100 - reflectionsGain: 0.3548 - reflectionsDelay: 0.0290 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0200 - lateReverbPan: 0,0,0 - echoTime: 0.1300 - echoDepth: 0.1700 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9943 - hfReference: 2854.3999 - lfReference: 107.5000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - - -- type: audioPreset - id: CityUnderpass - density: 1.0000 - diffusion: 0.8200 - gain: 0.3162 - gainHf: 0.4467 - gainLf: 0.8913 - decayTime: 3.5700 - decayHfRatio: 1.1200 - decayLfRatio: 0.9100 - reflectionsGain: 0.3981 - reflectionsDelay: 0.0590 - reflectionsPan: 0,0,0 - lateReverbGain: 0.8913 - lateReverbDelay: 0.0370 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.1400 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9920 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: CityAbandoned - density: 1.0000 - diffusion: 0.6900 - gain: 0.3162 - gainHf: 0.7943 - gainLf: 0.8913 - decayTime: 3.2800 - decayHfRatio: 1.1700 - decayLfRatio: 0.9100 - reflectionsGain: 0.4467 - reflectionsDelay: 0.0440 - reflectionsPan: 0,0,0 - lateReverbGain: 0.2818 - lateReverbDelay: 0.0240 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.2000 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9966 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -# Misc. Presets - -- type: audioPreset - id: DustyRoom - density: 0.3645 - diffusion: 0.5600 - gain: 0.3162 - gainHf: 0.7943 - gainLf: 0.7079 - decayTime: 1.7900 - decayHfRatio: 0.3800 - decayLfRatio: 0.2100 - reflectionsGain: 0.5012 - reflectionsDelay: 0.0020 - reflectionsPan: 0,0,0 - lateReverbGain: 1.2589 - lateReverbDelay: 0.0060 - lateReverbPan: 0,0,0 - echoTime: 0.2020 - echoDepth: 0.0500 - modulationTime: 0.2500 - modulationDepth: 0.0000 - airAbsorptionGainHf: 0.9886 - hfReference: 13046.0000 - lfReference: 163.3000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: Chapel - density: 1.0000 - diffusion: 0.8400 - gain: 0.3162 - gainHf: 0.5623 - gainLf: 1.0000 - decayTime: 4.6200 - decayHfRatio: 0.6400 - decayLfRatio: 1.2300 - reflectionsGain: 0.4467 - reflectionsDelay: 0.0320 - reflectionsPan: 0,0,0 - lateReverbGain: 0.7943 - lateReverbDelay: 0.0490 - lateReverbPan: 0,0,0 - echoTime: 0.2500 - echoDepth: 0.0000 - modulationTime: 0.2500 - modulationDepth: 0.1100 - airAbsorptionGainHf: 0.9943 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 1 - - -- type: audioPreset - id: SmallWaterRoom - density: 1.0000 - diffusion: 0.7000 - gain: 0.3162 - gainHf: 0.4477 - gainLf: 1.0000 - decayTime: 1.5100 - decayHfRatio: 1.2500 - decayLfRatio: 1.1400 - reflectionsGain: 0.8913 - reflectionsDelay: 0.0200 - reflectionsPan: 0,0,0 - lateReverbGain: 1.4125 - lateReverbDelay: 0.0300 - lateReverbPan: 0,0,0 - echoTime: 0.1790 - echoDepth: 0.1500 - modulationTime: 0.8950 - modulationDepth: 0.1900 - airAbsorptionGainHf: 0.9920 - hfReference: 5000.0000 - lfReference: 250.0000 - roomRolloffFactor: 0.0000 - decayHfLimit: 0 - diff --git a/Resources/EnginePrototypes/entity.yml b/Resources/EnginePrototypes/entity.yml deleted file mode 100644 index 0013afe62f4..00000000000 --- a/Resources/EnginePrototypes/entity.yml +++ /dev/null @@ -1,5 +0,0 @@ -- type: entity - id: Audio - name: Audio - description: Audio entity used by engine - save: false \ No newline at end of file diff --git a/Robust.Client/Animations/AnimationTrackPlaySound.cs b/Robust.Client/Animations/AnimationTrackPlaySound.cs index be5051a4d8f..1ffd5f20779 100644 --- a/Robust.Client/Animations/AnimationTrackPlaySound.cs +++ b/Robust.Client/Animations/AnimationTrackPlaySound.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using Robust.Client.Audio; using Robust.Client.GameObjects; using Robust.Shared.Audio; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Player; namespace Robust.Client.Animations @@ -39,12 +37,7 @@ public override (int KeyFrameIndex, float FramePlayingTime) var keyFrame = KeyFrames[keyFrameIndex]; - var audioParams = keyFrame.AudioParamsFunc.Invoke(); - var audio = new SoundPathSpecifier(keyFrame.Resource) - { - Params = audioParams - }; - IoCManager.Resolve().GetEntitySystem().PlayEntity(audio, Filter.Local(), entity, true); + SoundSystem.Play(keyFrame.Resource, Filter.Local(), entity, keyFrame.AudioParamsFunc.Invoke()); } return (keyFrameIndex, playingTime); diff --git a/Robust.Client/Audio/AudioManager.ALDisposeQueues.cs b/Robust.Client/Audio/AudioManager.ALDisposeQueues.cs deleted file mode 100644 index c60448f07bf..00000000000 --- a/Robust.Client/Audio/AudioManager.ALDisposeQueues.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Concurrent; -using OpenTK.Audio.OpenAL; - -namespace Robust.Client.Audio; - -internal partial class AudioManager -{ - // Used to track audio sources that were disposed in the finalizer thread, - // so we need to properly send them off in the main thread. - private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _sourceDisposeQueue = new(); - private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _bufferedSourceDisposeQueue = new(); - private readonly ConcurrentQueue _bufferDisposeQueue = new(); - - public void FlushALDisposeQueues() - { - // Clear out finalized audio sources. - while (_sourceDisposeQueue.TryDequeue(out var handles)) - { - OpenALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle); - if (IsEfxSupported) RemoveEfx(handles); - AL.DeleteSource(handles.sourceHandle); - _checkAlError(); - _audioSources.Remove(handles.sourceHandle); - } - - // Clear out finalized buffered audio sources. - while (_bufferedSourceDisposeQueue.TryDequeue(out var handles)) - { - OpenALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle); - if (IsEfxSupported) RemoveEfx(handles); - AL.DeleteSource(handles.sourceHandle); - _checkAlError(); - _bufferedAudioSources.Remove(handles.sourceHandle); - } - - // Clear out finalized audio buffers. - while (_bufferDisposeQueue.TryDequeue(out var handle)) - { - AL.DeleteBuffer(handle); - _checkAlError(); - } - } - - internal void DeleteSourceOnMainThread(int sourceHandle, int filterHandle) - { - _sourceDisposeQueue.Enqueue((sourceHandle, filterHandle)); - } - - internal void DeleteBufferedSourceOnMainThread(int bufferedSourceHandle, int filterHandle) - { - _bufferedSourceDisposeQueue.Enqueue((bufferedSourceHandle, filterHandle)); - } - - internal void DeleteAudioBufferOnMainThread(int bufferHandle) - { - _bufferDisposeQueue.Enqueue(bufferHandle); - } -} diff --git a/Robust.Client/Audio/AudioManager.Public.cs b/Robust.Client/Audio/AudioManager.Public.cs deleted file mode 100644 index 23a715d2fe6..00000000000 --- a/Robust.Client/Audio/AudioManager.Public.cs +++ /dev/null @@ -1,338 +0,0 @@ -using System; -using System.IO; -using System.Numerics; -using System.Threading; -using OpenTK.Audio.OpenAL; -using Robust.Client.Audio.Sources; -using Robust.Client.Graphics; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Maths; - -namespace Robust.Client.Audio; - -internal partial class AudioManager -{ - private float _zOffset; - - public void SetZOffset(float offset) - { - _zOffset = offset; - } - - /// - public float GetAttenuationGain(float distance, float rolloffFactor, float referenceDistance, float maxDistance) - { - switch (_attenuation) - { - case Attenuation.LinearDistance: - return 1 - rolloffFactor * (distance - referenceDistance) / (maxDistance - referenceDistance); - case Attenuation.LinearDistanceClamped: - distance = MathF.Max(referenceDistance, MathF.Min(distance, maxDistance)); - return 1 - rolloffFactor * (distance - referenceDistance) / (maxDistance - referenceDistance); - default: - // TODO: If you see this you can implement - throw new NotImplementedException(); - } - } - - public void InitializePostWindowing() - { - _gameThread = Thread.CurrentThread; - InitializeAudio(); - } - - public void Shutdown() - { - DisposeAllAudio(); - - if (_openALContext != ALContext.Null) - { - ALC.MakeContextCurrent(ALContext.Null); - - ALC.DestroyContext(_openALContext); - } - - if (_openALDevice != IntPtr.Zero) - { - ALC.CloseDevice(_openALDevice); - } - } - - /// - public void SetPosition(Vector2 position) - { - AL.Listener(ALListener3f.Position, position.X, position.Y, _zOffset); - } - - /// - public void SetRotation(Angle angle) - { - var vec = angle.ToVec(); - - // Default orientation: at: (0, 0, -1) up: (0, 1, 0) - var at = new OpenTK.Mathematics.Vector3(0f, 0f, -1f); - var up = new OpenTK.Mathematics.Vector3(vec.Y, vec.X, 0f); - AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, vec.X, vec.Y, 0}); - AL.Listener(ALListenerfv.Orientation, ref at, ref up); - } - - /// - public override AudioStream LoadAudioOggVorbis(Stream stream, string? name = null) - { - var vorbis = _readOggVorbis(stream); - - var buffer = AL.GenBuffer(); - - ALFormat format; - // NVorbis only supports loading into floats. - // If this becomes a problem due to missing extension support (doubt it but ok), - // check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM. - if (vorbis.Channels == 1) - { - format = ALFormat.MonoFloat32Ext; - } - else if (vorbis.Channels == 2) - { - format = ALFormat.StereoFloat32Ext; - } - else - { - throw new InvalidOperationException("Unable to load audio with more than 2 channels."); - } - - unsafe - { - fixed (float* ptr = vorbis.Data.Span) - { - AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float), - (int) vorbis.SampleRate); - } - } - - _checkAlError(); - - var handle = new ClydeHandle(_audioSampleBuffers.Count); - _audioSampleBuffers.Add(new LoadedAudioSample(buffer)); - var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate); - return new AudioStream(handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist); - } - - /// - public override AudioStream LoadAudioWav(Stream stream, string? name = null) - { - var wav = _readWav(stream); - - var buffer = AL.GenBuffer(); - - ALFormat format; - if (wav.BitsPerSample == 16) - { - if (wav.NumChannels == 1) - { - format = ALFormat.Mono16; - } - else if (wav.NumChannels == 2) - { - format = ALFormat.Stereo16; - } - else - { - throw new InvalidOperationException("Unable to load audio with more than 2 channels."); - } - } - else if (wav.BitsPerSample == 8) - { - if (wav.NumChannels == 1) - { - format = ALFormat.Mono8; - } - else if (wav.NumChannels == 2) - { - format = ALFormat.Stereo8; - } - else - { - throw new InvalidOperationException("Unable to load audio with more than 2 channels."); - } - } - else - { - throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16"); - } - - unsafe - { - fixed (byte* ptr = wav.Data.Span) - { - AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate); - } - } - - _checkAlError(); - - var handle = new ClydeHandle(_audioSampleBuffers.Count); - _audioSampleBuffers.Add(new LoadedAudioSample(buffer)); - var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate); - return new AudioStream(handle, length, wav.NumChannels, name); - } - - /// - public override AudioStream LoadAudioRaw(ReadOnlySpan samples, int channels, int sampleRate, string? name = null) - { - var fmt = channels switch - { - 1 => ALFormat.Mono16, - 2 => ALFormat.Stereo16, - _ => throw new ArgumentOutOfRangeException( - nameof(channels), "Only stereo and mono is currently supported") - }; - - var buffer = AL.GenBuffer(); - _checkAlError(); - - unsafe - { - fixed (short* ptr = samples) - { - AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate); - } - } - - _checkAlError(); - - var handle = new ClydeHandle(_audioSampleBuffers.Count); - var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate); - _audioSampleBuffers.Add(new LoadedAudioSample(buffer)); - return new AudioStream(handle, length, channels, name); - } - - public void SetMasterVolume(float newVolume) - { - AL.Listener(ALListenerf.Gain, newVolume); - } - - public void SetAttenuation(Attenuation attenuation) - { - switch (attenuation) - { - case Attenuation.NoAttenuation: - AL.DistanceModel(ALDistanceModel.None); - break; - case Attenuation.InverseDistance: - AL.DistanceModel(ALDistanceModel.InverseDistance); - break; - case Attenuation.InverseDistanceClamped: - AL.DistanceModel(ALDistanceModel.InverseDistanceClamped); - break; - case Attenuation.LinearDistance: - AL.DistanceModel(ALDistanceModel.LinearDistance); - break; - case Attenuation.LinearDistanceClamped: - AL.DistanceModel(ALDistanceModel.LinearDistanceClamped); - break; - case Attenuation.ExponentDistance: - AL.DistanceModel(ALDistanceModel.ExponentDistance); - break; - case Attenuation.ExponentDistanceClamped: - AL.DistanceModel(ALDistanceModel.ExponentDistanceClamped); - break; - default: - throw new ArgumentOutOfRangeException($"No implementation to set {attenuation.ToString()} for DistanceModel!"); - } - - _attenuation = attenuation; - OpenALSawmill.Info($"Set audio attenuation to {attenuation.ToString()}"); - } - - internal void RemoveAudioSource(int handle) - { - _audioSources.Remove(handle); - } - - internal void RemoveBufferedAudioSource(int handle) - { - _bufferedAudioSources.Remove(handle); - } - - public IAudioSource? CreateAudioSource(AudioStream stream) - { - var source = AL.GenSource(); - - if (!AL.IsSource(source)) - { - OpenALSawmill.Error("Failed to generate source. Too many simultaneous audio streams? {0}", Environment.StackTrace); - return null; - } - - // ReSharper disable once PossibleInvalidOperationException - // TODO: This really shouldn't be indexing based on the ClydeHandle... - AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value].BufferHandle); - - var audioSource = new AudioSource(this, source, stream); - _audioSources.Add(source, new WeakReference(audioSource)); - return audioSource; - } - - public IBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false) - { - var source = AL.GenSource(); - - if (!AL.IsSource(source)) - { - OpenALSawmill.Error("Failed to generate source. Too many simultaneous audio streams? {0}", Environment.StackTrace); - } - - // ReSharper disable once PossibleInvalidOperationException - - var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio); - _bufferedAudioSources.Add(source, new WeakReference(audioSource)); - return audioSource; - } - - /// - public void StopAllAudio() - { - foreach (var source in _audioSources.Values) - { - if (source.TryGetTarget(out var target)) - { - target.Playing = false; - } - } - - foreach (var source in _bufferedAudioSources.Values) - { - if (source.TryGetTarget(out var target)) - { - target.Playing = false; - } - } - } - - public void DisposeAllAudio() - { - // TODO: Do we even need to stop? - foreach (var source in _audioSources.Values) - { - if (source.TryGetTarget(out var target)) - { - target.Playing = false; - target.Dispose(); - } - } - - _audioSources.Clear(); - - foreach (var source in _bufferedAudioSources.Values) - { - if (source.TryGetTarget(out var target)) - { - target.Playing = false; - target.Dispose(); - } - } - - _bufferedAudioSources.Clear(); - } -} diff --git a/Robust.Client/Audio/AudioManager.cs b/Robust.Client/Audio/AudioManager.cs deleted file mode 100644 index 3c89564733b..00000000000 --- a/Robust.Client/Audio/AudioManager.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using OpenTK.Audio.OpenAL; -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Client.Audio.Sources; -using Robust.Shared; -using Robust.Shared.Audio; -using Robust.Shared.Configuration; -using Robust.Shared.Log; -using Robust.Shared.Utility; - -namespace Robust.Client.Audio; - -internal sealed partial class AudioManager : SharedAudioManager, IAudioInternal -{ - [Shared.IoC.Dependency] private readonly IConfigurationManager _cfg = default!; - [Shared.IoC.Dependency] private readonly ILogManager _logMan = default!; - - private Thread? _gameThread; - - private ALDevice _openALDevice; - private ALContext _openALContext; - - private readonly List _audioSampleBuffers = new(); - - private readonly Dictionary> _audioSources = - new(); - - private readonly Dictionary> _bufferedAudioSources = - new(); - - private readonly HashSet _alcDeviceExtensions = new(); - private readonly HashSet _alContextExtensions = new(); - private Attenuation _attenuation; - - public bool HasAlDeviceExtension(string extension) => _alcDeviceExtensions.Contains(extension); - public bool HasAlContextExtension(string extension) => _alContextExtensions.Contains(extension); - - internal bool IsEfxSupported; - - internal ISawmill OpenALSawmill = default!; - - private void _audioCreateContext() - { - unsafe - { - _openALContext = ALC.CreateContext(_openALDevice, (int*) 0); - } - - ALC.MakeContextCurrent(_openALContext); - _checkAlcError(_openALDevice); - _checkAlError(); - - // Load up AL context extensions. - var s = ALC.GetString(ALDevice.Null, AlcGetString.Extensions) ?? ""; - foreach (var extension in s.Split(' ')) - { - _alContextExtensions.Add(extension); - } - - OpenALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor)); - OpenALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer)); - OpenALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version)); - } - - private bool _audioOpenDevice() - { - var preferredDevice = _cfg.GetCVar(CVars.AudioDevice); - - // Open device. - if (!string.IsNullOrEmpty(preferredDevice)) - { - _openALDevice = ALC.OpenDevice(preferredDevice); - if (_openALDevice == IntPtr.Zero) - { - OpenALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.", - preferredDevice, ALC.GetError(ALDevice.Null)); - - _openALDevice = ALC.OpenDevice(null); - } - } - else - { - _openALDevice = ALC.OpenDevice(null); - } - - _checkAlcError(_openALDevice); - - if (_openALDevice == IntPtr.Zero) - { - OpenALSawmill.Error("Unable to open OpenAL device! {1}", ALC.GetError(ALDevice.Null)); - return false; - } - - // Load up ALC extensions. - var s = ALC.GetString(_openALDevice, AlcGetString.Extensions) ?? ""; - foreach (var extension in s.Split(' ')) - { - _alcDeviceExtensions.Add(extension); - } - return true; - } - - private void InitializeAudio() - { - OpenALSawmill = _logMan.GetSawmill("clyde.oal"); - - if (!_audioOpenDevice()) - return; - - // Create OpenAL context. - _audioCreateContext(); - - IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX"); - - _cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true); - } - - internal bool IsMainThread() - { - return Thread.CurrentThread == _gameThread; - } - - private static void RemoveEfx((int sourceHandle, int filterHandle) handles) - { - if (handles.filterHandle != 0) - EFX.DeleteFilter(handles.filterHandle); - } - - private void _checkAlcError(ALDevice device, - [CallerMemberName] string callerMember = "", - [CallerLineNumber] int callerLineNumber = -1) - { - var error = ALC.GetError(device); - if (error != AlcError.NoError) - { - OpenALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error); - } - } - - public void _checkAlError([CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLineNumber = -1) - { - var error = AL.GetError(); - if (error != ALError.NoError) - { - OpenALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error); - } - } - - private sealed class LoadedAudioSample - { - public readonly int BufferHandle; - - public LoadedAudioSample(int bufferHandle) - { - BufferHandle = bufferHandle; - } - } -} diff --git a/Robust.Client/Audio/AudioOverlay.cs b/Robust.Client/Audio/AudioOverlay.cs deleted file mode 100644 index c6bb24e75ca..00000000000 --- a/Robust.Client/Audio/AudioOverlay.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System.Numerics; -using System.Text; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.Player; -using Robust.Client.ResourceManagement; -using Robust.Shared.Audio; -using Robust.Shared.Enums; -using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Maths; -using AudioComponent = Robust.Shared.Audio.Components.AudioComponent; - -namespace Robust.Client.Audio; - -/// -/// Debug overlay for audio. -/// -public sealed class AudioOverlay : Overlay -{ - public override OverlaySpace Space => OverlaySpace.ScreenSpace; - - private IEntityManager _entManager; - private IPlayerManager _playerManager; - private AudioSystem _audio; - private SharedTransformSystem _transform; - - private Font _font; - - public AudioOverlay(IEntityManager entManager, IPlayerManager playerManager, IClientResourceCache cache, AudioSystem audio, SharedTransformSystem transform) - { - _entManager = entManager; - _playerManager = playerManager; - _audio = audio; - _transform = transform; - - _font = new VectorFont(cache.GetResource("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10); - } - - protected internal override void Draw(in OverlayDrawArgs args) - { - var localPlayer = _playerManager.LocalPlayer?.ControlledEntity; - - if (args.ViewportControl == null || localPlayer == null) - return; - - var screenHandle = args.ScreenHandle; - var output = new StringBuilder(); - var listenerPos = _entManager.GetComponent(localPlayer.Value).MapPosition; - - if (listenerPos.MapId != args.MapId) - return; - - var query = _entManager.AllEntityQueryEnumerator(); - - while (query.MoveNext(out var uid, out var comp)) - { - var mapId = MapId.Nullspace; - var audioPos = Vector2.Zero; - - if (_entManager.TryGetComponent(uid, out var xform)) - { - mapId = xform.MapID; - audioPos = _transform.GetWorldPosition(uid); - } - - if (mapId != args.MapId) - continue; - - var screenPos = args.ViewportControl.WorldToScreen(audioPos); - var distance = audioPos - listenerPos.Position; - var posOcclusion = _audio.GetOcclusion(uid, listenerPos, distance, distance.Length()); - - output.Clear(); - output.AppendLine("Audio Source"); - output.AppendLine("Runtime:"); - output.AppendLine($"- Occlusion: {posOcclusion:0.0000}"); - output.AppendLine("Params:"); - output.AppendLine($"- Volume: {comp.Volume:0.0000}"); - output.AppendLine($"- Reference distance: {comp.ReferenceDistance}"); - output.AppendLine($"- Max distance: {comp.MaxDistance}"); - var outputText = output.ToString().Trim(); - var dimensions = screenHandle.GetDimensions(_font, outputText, 1f); - var buffer = new Vector2(3f, 3f); - screenHandle.DrawRect(new UIBox2(screenPos - buffer, screenPos + dimensions + buffer), new Color(39, 39, 48)); - screenHandle.DrawString(_font, screenPos, outputText); - } - } -} diff --git a/Robust.Shared/Audio/AudioStream.cs b/Robust.Client/Audio/AudioStream.cs similarity index 51% rename from Robust.Shared/Audio/AudioStream.cs rename to Robust.Client/Audio/AudioStream.cs index a5ff7399347..605e5f76056 100644 --- a/Robust.Shared/Audio/AudioStream.cs +++ b/Robust.Client/Audio/AudioStream.cs @@ -1,21 +1,18 @@ using System; -using Robust.Shared.Graphics; +using Robust.Client.Graphics; -namespace Robust.Shared.Audio; +namespace Robust.Client.Audio; -/// -/// Has the metadata for a particular audio stream as well as the relevant internal handle to it. -/// public sealed class AudioStream { public TimeSpan Length { get; } - internal IClydeHandle? ClydeHandle { get; } + internal ClydeHandle? ClydeHandle { get; } public string? Name { get; } public string? Title { get; } public string? Artist { get; } public int ChannelCount { get; } - internal AudioStream(IClydeHandle? handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null) + internal AudioStream(ClydeHandle handle, TimeSpan length, int channelCount, string? name = null, string? title = null, string? artist = null) { ClydeHandle = handle; Length = length; diff --git a/Robust.Client/Audio/AudioSystem.Effects.cs b/Robust.Client/Audio/AudioSystem.Effects.cs deleted file mode 100644 index 35706e4d768..00000000000 --- a/Robust.Client/Audio/AudioSystem.Effects.cs +++ /dev/null @@ -1,76 +0,0 @@ -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Client.Audio.Effects; -using Robust.Shared.Audio.Components; -using Robust.Shared.GameObjects; - -namespace Robust.Client.Audio; - -public sealed partial class AudioSystem -{ - protected override void InitializeEffect() - { - base.InitializeEffect(); - SubscribeLocalEvent(OnEffectAdd); - SubscribeLocalEvent(OnEffectShutdown); - - SubscribeLocalEvent(OnAuxiliaryAdd); - SubscribeLocalEvent(OnAuxiliaryAuto); - } - - private void OnEffectAdd(EntityUid uid, AudioEffectComponent component, ComponentAdd args) - { - var effect = new AudioEffect(_audio); - component.Effect = effect; - } - - private void OnEffectShutdown(EntityUid uid, AudioEffectComponent component, ComponentShutdown args) - { - if (component.Effect is AudioEffect effect) - { - effect.Dispose(); - } - } - - private void OnAuxiliaryAdd(EntityUid uid, AudioAuxiliaryComponent component, ComponentAdd args) - { - component.Auxiliary = new AuxiliaryAudio(); - } - - private void OnAuxiliaryAuto(EntityUid uid, AudioAuxiliaryComponent component, ref AfterAutoHandleStateEvent args) - { - if (TryComp(component.Effect, out var effectComp)) - { - component.Auxiliary.SetEffect(effectComp.Effect); - } - else - { - component.Auxiliary.SetEffect(null); - } - } - - public override void SetAuxiliary(EntityUid uid, AudioComponent audio, EntityUid? auxUid) - { - base.SetAuxiliary(uid, audio, auxUid); - if (TryComp(audio.Auxiliary, out var auxComp)) - { - audio.Source.SetAuxiliary(auxComp.Auxiliary); - } - else - { - audio.Source.SetAuxiliary(null); - } - } - - public override void SetEffect(EntityUid auxUid, AudioAuxiliaryComponent aux, EntityUid? effectUid) - { - base.SetEffect(auxUid, aux, effectUid); - if (TryComp(aux.Effect, out var effectComp)) - { - aux.Auxiliary.SetEffect(effectComp.Effect); - } - else - { - aux.Auxiliary.SetEffect(null); - } - } -} diff --git a/Robust.Client/Audio/AudioSystem.cs b/Robust.Client/Audio/AudioSystem.cs deleted file mode 100644 index b0fabaf9e1c..00000000000 --- a/Robust.Client/Audio/AudioSystem.cs +++ /dev/null @@ -1,596 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Numerics; -using System.Threading.Tasks; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.ResourceManagement; -using Robust.Shared; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Components; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Exceptions; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Player; -using Robust.Shared.Replays; -using Robust.Shared.ResourceManagement.ResourceTypes; -using Robust.Shared.Threading; -using Robust.Shared.Utility; -using AudioComponent = Robust.Shared.Audio.Components.AudioComponent; - -namespace Robust.Client.Audio; - -public sealed partial class AudioSystem : SharedAudioSystem -{ - /* - * There's still a lot more OpenAL can do in terms of filters, auxiliary slots, etc. - * but exposing the whole thing in an easy way is a lot of effort. - */ - - [Dependency] private readonly IReplayRecordingManager _replayRecording = default!; - [Dependency] private readonly IEyeManager _eyeManager = default!; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IParallelManager _parMan = default!; - [Dependency] private readonly IRuntimeLog _runtimeLog = default!; - [Dependency] private readonly IAudioInternal _audio = default!; - [Dependency] private readonly SharedTransformSystem _xformSys = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - - /// - /// Per-tick cache of relevant streams. - /// - private readonly List<(EntityUid Entity, AudioComponent Component, TransformComponent Xform)> _streams = new(); - private EntityUid? _listenerGrid; - - private EntityQuery _gridQuery; - private EntityQuery _physicsQuery; - private EntityQuery _xformQuery; - - private float _maxRayLength; - - /// - public override void Initialize() - { - base.Initialize(); - - UpdatesOutsidePrediction = true; - // Need to run after Eye updates so we have an accurate listener position. - UpdatesAfter.Add(typeof(EyeSystem)); - - _gridQuery = GetEntityQuery(); - _physicsQuery = GetEntityQuery(); - _xformQuery = GetEntityQuery(); - - SubscribeLocalEvent(OnAudioStartup); - SubscribeLocalEvent(OnAudioShutdown); - SubscribeLocalEvent(OnAudioPaused); - SubscribeLocalEvent(OnAudioState); - - // Replay stuff - SubscribeNetworkEvent(OnGlobalAudio); - SubscribeNetworkEvent(OnEntityAudio); - SubscribeNetworkEvent(OnEntityCoordinates); - - CfgManager.OnValueChanged(CVars.AudioAttenuation, OnAudioAttenuation, true); - CfgManager.OnValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged, true); - } - - private void OnAudioState(EntityUid uid, AudioComponent component, ref AfterAutoHandleStateEvent args) - { - ApplyAudioParams(component.Params, component); - component.Source.Global = component.Global; - - if (TryComp(component.Auxiliary, out var auxComp)) - { - component.Source.SetAuxiliary(auxComp.Auxiliary); - } - else - { - component.Source.SetAuxiliary(null); - } - } - - /// - /// Sets the volume for the entire game. - /// - public void SetMasterVolume(float value) - { - _audio.SetMasterVolume(value); - } - - protected override void SetZOffset(float value) - { - base.SetZOffset(value); - _audio.SetZOffset(value); - } - - public override void Shutdown() - { - CfgManager.UnsubValueChanged(CVars.AudioAttenuation, OnAudioAttenuation); - CfgManager.UnsubValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged); - base.Shutdown(); - } - - private void OnAudioPaused(EntityUid uid, AudioComponent component, ref EntityPausedEvent args) - { - component.Pause(); - } - - protected override void OnAudioUnpaused(EntityUid uid, AudioComponent component, ref EntityUnpausedEvent args) - { - base.OnAudioUnpaused(uid, component, ref args); - component.StartPlaying(); - } - - private void OnAudioStartup(EntityUid uid, AudioComponent component, ComponentStartup args) - { - if (!Timing.ApplyingState && !Timing.IsFirstTimePredicted) - { - return; - } - - if (!TryGetAudio(component.FileName, out var audioResource)) - { - Log.Error($"Error creating audio source for {audioResource}, can't find file {component.FileName}"); - component.Source = new DummyAudioSource(); - return; - } - - var source = _audio.CreateAudioSource(audioResource); - - if (source == null) - { - Log.Error($"Error creating audio source for {audioResource}"); - DebugTools.Assert(false); - source = new DummyAudioSource(); - } - - // Need to set all initial data for first frame. - component.Source = source; - ApplyAudioParams(component.Params, component); - component.Global = component.Global; - // Don't play until first frame so occlusion etc. are correct. - component.Gain = 0f; - - // If audio came into range then start playback at the correct position. - var offset = (Timing.CurTime - component.AudioStart).TotalSeconds % GetAudioLength(component.FileName).TotalSeconds; - - if (offset != 0) - { - component.PlaybackPosition = (float) offset; - } - } - - private void OnAudioShutdown(EntityUid uid, AudioComponent component, ComponentShutdown args) - { - // Breaks with prediction? - component.Source.Dispose(); - } - - private void OnAudioAttenuation(int obj) - { - _audio.SetAttenuation((Attenuation) obj); - } - - private void OnRaycastLengthChanged(float value) - { - _maxRayLength = value; - } - - public override void FrameUpdate(float frameTime) - { - var eye = _eyeManager.CurrentEye; - _audio.SetRotation(eye.Rotation); - _audio.SetPosition(eye.Position.Position); - - var ourPos = eye.Position; - var opts = new ParallelOptions { MaxDegreeOfParallelism = _parMan.ParallelProcessCount }; - - var query = AllEntityQuery(); - _streams.Clear(); - - while (query.MoveNext(out var uid, out var comp, out var xform)) - { - _streams.Add((uid, comp, xform)); - } - - _mapManager.TryFindGridAt(ourPos, out var gridUid, out _); - _listenerGrid = gridUid == EntityUid.Invalid ? null : gridUid; - - try - { - Parallel.ForEach(_streams, opts, comp => ProcessStream(comp.Entity, comp.Component, comp.Xform, ourPos)); - } - catch (Exception e) - { - Log.Error($"Caught exception while processing entity streams."); - _runtimeLog.LogException(e, $"{nameof(AudioSystem)}.{nameof(FrameUpdate)}"); - } - } - - private void ProcessStream(EntityUid entity, AudioComponent component, TransformComponent xform, MapCoordinates listener) - { - // TODO: - // I Originally tried to be fancier here but it caused audio issues so just trying - // to replicate the old behaviour for now. - if (!component.Started) - { - component.Started = true; - component.StartPlaying(); - } - - // If it's global but on another map (that isn't nullspace) then stop playing it. - if (component.Global) - { - if (xform.MapID != MapId.Nullspace && listener.MapId != xform.MapID) - { - component.Gain = 0f; - return; - } - - // Resume playing. - component.Volume = component.Params.Volume; - return; - } - - // Non-global sounds, stop playing if on another map. - // Not relevant to us. - if (listener.MapId != xform.MapID) - { - component.Gain = 0f; - return; - } - - Vector2 worldPos; - var gridUid = xform.ParentUid; - - // Handle grid audio differently by using nearest-edge instead of entity centre. - if (_gridQuery.HasComponent(gridUid)) - { - // It's our grid so max volume. - if (_listenerGrid == gridUid) - { - component.Volume = component.Params.Volume; - component.Occlusion = 0f; - component.Position = listener.Position; - return; - } - - // TODO: Need a grid-optimised version because this is gonna be expensive. - // Just to avoid clipping on and off grid or nearestPoint changing we'll - // always set the sound to listener's pos, we'll just manually do gain ourselves. - if (_physics.TryGetNearest(gridUid, listener, out _, out var gridDistance)) - { - // Out of range - if (gridDistance > component.MaxDistance) - { - component.Gain = 0f; - return; - } - - var paramsGain = MathF.Pow(10, component.Params.Volume / 10); - - // Thought I'd never have to manually calculate gain again but this is the least - // unpleasant audio I could get at the moment. - component.Gain = paramsGain * _audio.GetAttenuationGain( - gridDistance, - component.Params.RolloffFactor, - component.Params.ReferenceDistance, - component.Params.MaxDistance); - component.Position = listener.Position; - return; - } - - // Can't get nearest point so don't play anymore. - component.Gain = 0f; - return; - } - - worldPos = _xformSys.GetWorldPosition(entity); - component.Volume = component.Params.Volume; - - // Max distance check - var delta = worldPos - listener.Position; - var distance = delta.Length(); - - // Out of range so just clip it for us. - if (distance > component.MaxDistance) - { - // Still keeps the source playing, just with no volume. - component.Gain = 0f; - return; - } - - // Update audio occlusion - var occlusion = GetOcclusion(entity, listener, delta, distance); - component.Occlusion = occlusion; - - // Update audio positions. - component.Position = worldPos; - - // Make race cars go NYYEEOOOOOMMMMM - if (_physicsQuery.TryGetComponent(entity, out var physicsComp)) - { - // This actually gets the tracked entity's xform & iterates up though the parents for the second time. Bit - // inefficient. - var velocity = _physics.GetMapLinearVelocity(entity, physicsComp, xform, _xformQuery, _physicsQuery); - component.Velocity = velocity; - } - } - - internal float GetOcclusion(EntityUid entity, MapCoordinates listener, Vector2 delta, float distance) - { - float occlusion = 0; - - if (distance > 0.1) - { - var rayLength = MathF.Min(distance, _maxRayLength); - var ray = new CollisionRay(listener.Position, delta / distance, OcclusionCollisionMask); - occlusion = _physics.IntersectRayPenetration(listener.MapId, ray, rayLength, entity); - } - - return occlusion; - } - - private bool TryGetAudio(string filename, [NotNullWhen(true)] out AudioResource? audio) - { - if (_resourceCache.TryGetResource(new ResPath(filename), out audio)) - return true; - - Log.Error($"Server tried to play audio file {filename} which does not exist."); - return false; - } - - private bool TryCreateAudioSource(AudioStream stream, [NotNullWhen(true)] out IAudioSource? source) - { - if (!Timing.IsFirstTimePredicted) - { - source = null; - Log.Error($"Tried to create audio source outside of prediction!"); - DebugTools.Assert(false); - return false; - } - - source = _audio.CreateAudioSource(stream); - return source != null; - } - - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityCoordinates coordinates, - AudioParams? audioParams = null) - { - return PlayStatic(filename, Filter.Local(), coordinates, true, audioParams); - } - - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityUid uid, AudioParams? audioParams = null) - { - return PlayEntity(filename, Filter.Local(), uid, true, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(SoundSpecifier? sound, EntityUid source, EntityUid? user, AudioParams? audioParams = null) - { - if (Timing.IsFirstTimePredicted || sound == null) - return PlayEntity(sound, Filter.Local(), source, false, audioParams); - - return null; // uhh Lets hope predicted audio never needs to somehow store the playing audio.... - } - - public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(SoundSpecifier? sound, EntityCoordinates coordinates, EntityUid? user, AudioParams? audioParams = null) - { - if (Timing.IsFirstTimePredicted || sound == null) - return PlayStatic(sound, Filter.Local(), coordinates, false, audioParams); - - return null; - } - - /// - /// Play an audio file globally, without position. - /// - /// The resource path to the OGG Vorbis file to play. - /// - private (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, AudioParams? audioParams = null, bool recordReplay = true) - { - if (recordReplay && _replayRecording.IsRecording) - { - _replayRecording.RecordReplayMessage(new PlayAudioGlobalMessage - { - FileName = filename, - AudioParams = audioParams ?? AudioParams.Default - }); - } - - return TryGetAudio(filename, out var audio) ? PlayGlobal(audio, audioParams) : default; - } - - /// - /// Play an audio stream globally, without position. - /// - /// The audio stream to play. - /// - private (EntityUid Entity, AudioComponent Component)? PlayGlobal(AudioStream stream, AudioParams? audioParams = null) - { - var (entity, component) = CreateAndStartPlayingStream(audioParams, stream); - component.Global = true; - component.Source.Global = true; - Dirty(entity, component); - return (entity, component); - } - - /// - /// Play an audio file following an entity. - /// - /// The resource path to the OGG Vorbis file to play. - /// The entity "emitting" the audio. - private (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true) - { - if (recordReplay && _replayRecording.IsRecording) - { - _replayRecording.RecordReplayMessage(new PlayAudioEntityMessage - { - FileName = filename, - NetEntity = GetNetEntity(entity), - AudioParams = audioParams ?? AudioParams.Default - }); - } - - return TryGetAudio(filename, out var audio) ? PlayEntity(audio, entity, audioParams) : default; - } - - /// - /// Play an audio stream following an entity. - /// - /// The audio stream to play. - /// The entity "emitting" the audio. - /// - private (EntityUid Entity, AudioComponent Component)? PlayEntity(AudioStream stream, EntityUid entity, AudioParams? audioParams = null) - { - var playing = CreateAndStartPlayingStream(audioParams, stream); - _xformSys.SetCoordinates(playing.Entity, new EntityCoordinates(entity, Vector2.Zero)); - - return playing; - } - - /// - /// Play an audio file at a static position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The coordinates at which to play the audio. - /// - private (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true) - { - if (recordReplay && _replayRecording.IsRecording) - { - _replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage - { - FileName = filename, - Coordinates = GetNetCoordinates(coordinates), - AudioParams = audioParams ?? AudioParams.Default - }); - } - - return TryGetAudio(filename, out var audio) ? PlayStatic(audio, coordinates, audioParams) : default; - } - - /// - /// Play an audio stream at a static position. - /// - /// The audio stream to play. - /// The coordinates at which to play the audio. - /// - private (EntityUid Entity, AudioComponent Component)? PlayStatic(AudioStream stream, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - var playing = CreateAndStartPlayingStream(audioParams, stream); - _xformSys.SetCoordinates(playing.Entity, coordinates); - return playing; - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) - { - return PlayGlobal(filename, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null) - { - return PlayEntity(filename, entity, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) - { - return PlayStatic(filename, coordinates, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null) - { - return PlayGlobal(filename, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null) - { - return PlayGlobal(filename, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) - { - return PlayEntity(filename, uid, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) - { - return PlayEntity(filename, uid, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - return PlayStatic(filename, coordinates, audioParams); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - return PlayStatic(filename, coordinates, audioParams); - } - - private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, AudioStream stream) - { - var audioP = audioParams ?? AudioParams.Default; - var entity = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace); - var comp = SetupAudio(entity, stream.Name!, audioP); - EntityManager.InitializeAndStartEntity(entity); - var source = comp.Source; - - // TODO clamp the offset inside of SetPlaybackPosition() itself. - var offset = audioP.PlayOffsetSeconds; - offset = Math.Clamp(offset, 0f, (float) stream.Length.TotalSeconds - 0.01f); - source.PlaybackPosition = offset; - - ApplyAudioParams(audioP, comp); - comp.Params = audioP; - source.StartPlaying(); - return (entity, comp); - } - - /// - /// Applies the audioparams to the underlying audio source. - /// - private void ApplyAudioParams(AudioParams audioParams, IAudioSource source) - { - source.Pitch = audioParams.Pitch; - source.Volume = audioParams.Volume; - source.RolloffFactor = audioParams.RolloffFactor; - source.MaxDistance = audioParams.MaxDistance; - source.ReferenceDistance = audioParams.ReferenceDistance; - source.Looping = audioParams.Loop; - } - - private void OnEntityCoordinates(PlayAudioPositionalMessage ev) - { - PlayStatic(ev.FileName, GetCoordinates(ev.Coordinates), ev.AudioParams, false); - } - - private void OnEntityAudio(PlayAudioEntityMessage ev) - { - PlayEntity(ev.FileName, GetEntity(ev.NetEntity), ev.AudioParams, false); - } - - private void OnGlobalAudio(PlayAudioGlobalMessage ev) - { - PlayGlobal(ev.FileName, ev.AudioParams, false); - } -} diff --git a/Robust.Client/Audio/Effects/AudioEffect.cs b/Robust.Client/Audio/Effects/AudioEffect.cs deleted file mode 100644 index 8e563ddab43..00000000000 --- a/Robust.Client/Audio/Effects/AudioEffect.cs +++ /dev/null @@ -1,455 +0,0 @@ -using System; -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Effects; -using Robust.Shared.Maths; - -namespace Robust.Client.Audio.Effects; - -/// -internal sealed class AudioEffect : IAudioEffect -{ - internal int Handle; - - private readonly IAudioInternal _master; - - public AudioEffect(IAudioInternal manager) - { - Handle = EFX.GenEffect(); - _master = manager; - EFX.Effect(Handle, EffectInteger.EffectType, (int) EffectType.EaxReverb); - } - - public void Dispose() - { - if (Handle != 0) - { - EFX.DeleteEffect(Handle); - Handle = 0; - } - } - - private void _checkDisposed() - { - if (Handle == -1) - { - throw new ObjectDisposedException(nameof(AudioEffect)); - } - } - - /// - public float Density - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbDensity, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbDensity, value); - _master._checkAlError(); - } - } - - /// - public float Diffusion - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbDiffusion, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbDiffusion, value); - _master._checkAlError(); - } - } - - /// - public float Gain - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbGain, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbGain, value); - _master._checkAlError(); - } - } - - /// - public float GainHF - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbGainHF, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbGainHF, value); - _master._checkAlError(); - } - } - - /// - public float GainLF - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbGainLF, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbGainLF, value); - _master._checkAlError(); - } - } - - /// - public float DecayTime - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayTime, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbDecayTime, value); - _master._checkAlError(); - } - } - - /// - public float DecayHFRatio - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayHFRatio, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbDecayHFRatio, value); - _master._checkAlError(); - } - } - - /// - public float DecayLFRatio - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbDecayLFRatio, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbDecayLFRatio, value); - _master._checkAlError(); - } - } - - /// - public float ReflectionsGain - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsGain, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsGain, value); - _master._checkAlError(); - } - } - - /// - public float ReflectionsDelay - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbReflectionsDelay, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbReflectionsDelay, value); - _master._checkAlError(); - } - } - - /// - public Vector3 ReflectionsPan - { - get - { - _checkDisposed(); - var value = EFX.GetEffect(Handle, EffectVector3.EaxReverbReflectionsPan); - _master._checkAlError(); - return new Vector3(value.X, value.Z, value.Y); - } - set - { - _checkDisposed(); - var openVec = new OpenTK.Mathematics.Vector3(value.X, value.Y, value.Z); - EFX.Effect(Handle, EffectVector3.EaxReverbReflectionsPan, ref openVec); - _master._checkAlError(); - } - } - - /// - public float LateReverbGain - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbGain, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbGain, value); - _master._checkAlError(); - } - } - - /// - public float LateReverbDelay - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbLateReverbDelay, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbLateReverbDelay, value); - _master._checkAlError(); - } - } - - /// - public Vector3 LateReverbPan - { - get - { - _checkDisposed(); - var value = EFX.GetEffect(Handle, EffectVector3.EaxReverbLateReverbPan); - _master._checkAlError(); - return new Vector3(value.X, value.Z, value.Y); - } - set - { - _checkDisposed(); - var openVec = new OpenTK.Mathematics.Vector3(value.X, value.Y, value.Z); - EFX.Effect(Handle, EffectVector3.EaxReverbLateReverbPan, ref openVec); - _master._checkAlError(); - } - } - - /// - public float EchoTime - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoTime, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbEchoTime, value); - _master._checkAlError(); - } - } - - /// - public float EchoDepth - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbEchoDepth, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbEchoDepth, value); - _master._checkAlError(); - } - } - - /// - public float ModulationTime - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationTime, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbModulationTime, value); - _master._checkAlError(); - } - } - - /// - public float ModulationDepth - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbModulationDepth, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbModulationDepth, value); - _master._checkAlError(); - } - } - - /// - public float AirAbsorptionGainHF - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbAirAbsorptionGainHF, value); - _master._checkAlError(); - } - } - - /// - public float HFReference - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbHFReference, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbHFReference, value); - _master._checkAlError(); - } - } - - /// - public float LFReference - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbLFReference, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbLFReference, value); - _master._checkAlError(); - } - } - - /// - public float RoomRolloffFactor - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectFloat.EaxReverbRoomRolloffFactor, value); - _master._checkAlError(); - } - } - - /// - public int DecayHFLimit - { - get - { - _checkDisposed(); - EFX.GetEffect(Handle, EffectInteger.EaxReverbDecayHFLimit, out var value); - _master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - EFX.Effect(Handle, EffectInteger.EaxReverbDecayHFLimit, value); - _master._checkAlError(); - } - } -} diff --git a/Robust.Client/Audio/Effects/AuxiliaryAudio.cs b/Robust.Client/Audio/Effects/AuxiliaryAudio.cs deleted file mode 100644 index a638a390bb4..00000000000 --- a/Robust.Client/Audio/Effects/AuxiliaryAudio.cs +++ /dev/null @@ -1,32 +0,0 @@ -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Shared.Audio.Effects; - -namespace Robust.Client.Audio.Effects; - -/// -internal sealed class AuxiliaryAudio : IAuxiliaryAudio -{ - internal int Handle = EFX.GenAuxiliaryEffectSlot(); - - public void Dispose() - { - if (Handle != -1) - { - EFX.DeleteAuxiliaryEffectSlot(Handle); - Handle = -1; - } - } - - /// - public void SetEffect(IAudioEffect? effect) - { - if (effect is AudioEffect audEffect) - { - EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, audEffect.Handle); - } - else - { - EFX.AuxiliaryEffectSlot(Handle, EffectSlotInteger.Effect, 0); - } - } -} diff --git a/Robust.Client/Audio/Midi/IMidiRenderer.cs b/Robust.Client/Audio/Midi/IMidiRenderer.cs index 216fe2c09d2..96cf7a2f4bb 100644 --- a/Robust.Client/Audio/Midi/IMidiRenderer.cs +++ b/Robust.Client/Audio/Midi/IMidiRenderer.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Robust.Client.Graphics; using Robust.Shared.Audio.Midi; -using Robust.Shared.Audio.Sources; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -21,7 +20,7 @@ public interface IMidiRenderer : IDisposable /// /// The buffered audio source of this renderer. /// - internal IBufferedAudioSource Source { get; } + internal IClydeBufferedAudioSource Source { get; } /// /// Whether this renderer has been disposed or not. diff --git a/Robust.Client/Audio/Midi/MidiManager.cs b/Robust.Client/Audio/Midi/MidiManager.cs index a3b0363461e..19d827aed7d 100644 --- a/Robust.Client/Audio/Midi/MidiManager.cs +++ b/Robust.Client/Audio/Midi/MidiManager.cs @@ -10,7 +10,6 @@ using Robust.Client.ResourceManagement; using Robust.Shared; using Robust.Shared.Asynchronous; -using Robust.Shared.Audio; using Robust.Shared.Audio.Midi; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; @@ -42,10 +41,10 @@ internal sealed partial class MidiManager : IMidiManager [ViewVariables] private TimeSpan _nextPositionUpdate = TimeSpan.Zero; [Dependency] private readonly IEyeManager _eyeManager = default!; - [Dependency] private readonly IResourceManager _resourceManager = default!; + [Dependency] private readonly IResourceCacheInternal _resourceManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IConfigurationManager _cfgMan = default!; - [Dependency] private readonly IAudioInternal _audio = default!; + [Dependency] private readonly IClydeAudio _clydeAudio = default!; [Dependency] private readonly ITaskManager _taskManager = default!; [Dependency] private readonly ILogManager _logger = default!; [Dependency] private readonly IParallelManager _parallel = default!; @@ -274,7 +273,7 @@ private void LoggerDelegate(NFluidsynth.Logger.LogLevel level, string message, I { soundfontLoader.SetCallbacks(_soundfontLoaderCallbacks); - var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _audio, _taskManager, _midiSawmill); + var renderer = new MidiRenderer(_settings!, soundfontLoader, mono, this, _clydeAudio, _taskManager, _midiSawmill); _midiSawmill.Debug($"Loading fallback soundfont {FallbackSoundfont}"); // Since the last loaded soundfont takes priority, we load the fallback soundfont before the soundfont. @@ -352,7 +351,7 @@ private void LoggerDelegate(NFluidsynth.Logger.LogLevel level, string message, I renderer.LoadSoundfont(file.ToString()); } - renderer.Source.Volume = _volume; + renderer.Source.SetVolume(Volume); lock (_renderers) { @@ -375,7 +374,6 @@ public void FrameUpdate(float frameTime) // Update positions of streams every frame. // This has a lot of code duplication with AudioSystem.FrameUpdate(), and they should probably be combined somehow. - // so TRUE lock (_renderers) { @@ -417,13 +415,11 @@ private void UpdateRenderer(IMidiRenderer renderer, EntityQuery _rendererState; - public IBufferedAudioSource Source { get; set; } - IBufferedAudioSource IMidiRenderer.Source => Source; + public IClydeBufferedAudioSource Source { get; set; } + IClydeBufferedAudioSource IMidiRenderer.Source => Source; [ViewVariables] public bool Disposed { get; private set; } = false; @@ -249,7 +247,7 @@ public IMidiRenderer? Master public event Action? OnMidiPlayerFinished; internal MidiRenderer(Settings settings, SoundFontLoader soundFontLoader, bool mono, - IMidiManager midiManager, IAudioInternal clydeAudio, ITaskManager taskManager, ISawmill midiSawmill) + IMidiManager midiManager, IClydeAudio clydeAudio, ITaskManager taskManager, ISawmill midiSawmill) { _midiManager = midiManager; _taskManager = taskManager; @@ -490,7 +488,7 @@ private void Render(int length = SampleRate / 250) } } - Source.StartPlaying(); + if (!Source.IsPlaying) Source.StartPlaying(); } public void ApplyState(MidiRendererState state, bool filterChannels = false) diff --git a/Robust.Client/Audio/ShowAudioCommand.cs b/Robust.Client/Audio/ShowAudioCommand.cs deleted file mode 100644 index 897db432a7a..00000000000 --- a/Robust.Client/Audio/ShowAudioCommand.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Robust.Client.Audio; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.Player; -using Robust.Client.ResourceManagement; -using Robust.Shared.Console; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; - -namespace Robust.Client.Commands; - -/// -/// Shows a debug overlay for audio sources. -/// -public sealed class ShowAudioCommand : LocalizedCommands -{ - [Dependency] private readonly IClientResourceCache _client = default!; - [Dependency] private readonly IEntityManager _entManager = default!; - [Dependency] private readonly IOverlayManager _overlayManager = default!; - [Dependency] private readonly IPlayerManager _playerMgr = default!; - public override string Command => "showaudio"; - public override void Execute(IConsoleShell shell, string argStr, string[] args) - { - if (_overlayManager.HasOverlay()) - _overlayManager.RemoveOverlay(); - else - _overlayManager.AddOverlay(new AudioOverlay( - _entManager, - _playerMgr, - _client, - _entManager.System(), - _entManager.System())); - } -} diff --git a/Robust.Client/Audio/Sources/AudioSource.cs b/Robust.Client/Audio/Sources/AudioSource.cs deleted file mode 100644 index 0558f29f36e..00000000000 --- a/Robust.Client/Audio/Sources/AudioSource.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Numerics; -using OpenTK.Audio.OpenAL; -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Shared.Audio; -using Robust.Shared.Maths; -using Robust.Shared.Utility; - -namespace Robust.Client.Audio.Sources; - -internal sealed class AudioSource : BaseAudioSource -{ - /// - /// Underlying stream to the audio. - /// - private readonly AudioStream _sourceStream; - -#if DEBUG - private bool _didPositionWarning; -#endif - - public AudioSource(AudioManager master, int sourceHandle, AudioStream sourceStream) : base(master, sourceHandle) - { - _sourceStream = sourceStream; - } - - /// - public override Vector2 Position - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSource3f.Position, out var x, out var y, out _); - Master._checkAlError(); - return new Vector2(x, y); - } - set - { - _checkDisposed(); - - var (x, y) = value; - - if (!AreFinite(x, y)) - { - return; - } -#if DEBUG - // OpenAL doesn't seem to want to play stereo positionally. - // Log a warning if people try to. - if (_sourceStream.ChannelCount > 1 && !_didPositionWarning) - { - _didPositionWarning = true; - Master.OpenALSawmill.Warning("Attempting to set position on audio source with multiple audio channels! Stream: '{0}'. Make sure the audio is MONO, not stereo.", - _sourceStream.Name); - // warning isn't enough, people just ignore it :( - DebugTools.Assert(false, $"Attempting to set position on audio source with multiple audio channels! Stream: '{_sourceStream.Name}'. Make sure the audio is MONO, not stereo."); - } -#endif - - AL.Source(SourceHandle, ALSource3f.Position, x, y, 0); - Master._checkAlError(); - } - } - - ~AudioSource() - { - Dispose(false); - } - - protected override void Dispose(bool disposing) - { - if (!disposing) - { - // We can't run this code inside the finalizer thread so tell Clyde to clear it up later. - Master.DeleteSourceOnMainThread(SourceHandle, FilterHandle); - } - else - { - if (FilterHandle != 0) - EFX.DeleteFilter(FilterHandle); - - AL.DeleteSource(SourceHandle); - Master.RemoveAudioSource(SourceHandle); - Master._checkAlError(); - } - - FilterHandle = 0; - SourceHandle = -1; - } -} diff --git a/Robust.Client/Audio/Sources/BaseAudioSource.cs b/Robust.Client/Audio/Sources/BaseAudioSource.cs deleted file mode 100644 index 38c9fdb3ec3..00000000000 --- a/Robust.Client/Audio/Sources/BaseAudioSource.cs +++ /dev/null @@ -1,390 +0,0 @@ -using System; -using System.Numerics; -using OpenTK.Audio.OpenAL; -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Client.Audio.Effects; -using Robust.Shared.Audio.Effects; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Maths; - -namespace Robust.Client.Audio.Sources; - -internal abstract class BaseAudioSource : IAudioSource -{ - /* - * This may look weird having all these methods here however - * we need to handle disposing plus checking for errors hence we get this. - */ - - /// - /// Handle to the AL source. - /// - protected int SourceHandle; - - /// - /// Source to the EFX filter if applicable. - /// - protected int FilterHandle; - - protected readonly AudioManager Master; - - /// - /// Prior gain that was set. - /// - private float _gain; - - private bool IsEfxSupported => Master.IsEfxSupported; - - protected BaseAudioSource(AudioManager master, int sourceHandle) - { - Master = master; - SourceHandle = sourceHandle; - AL.GetSource(SourceHandle, ALSourcef.Gain, out _gain); - } - - public void Pause() - { - AL.SourcePause(SourceHandle); - } - - /// - public void StartPlaying() - { - if (Playing) - return; - - Playing = true; - } - - /// - public void StopPlaying() - { - if (!Playing) - return; - - Playing = false; - } - - /// - public virtual bool Playing - { - get - { - _checkDisposed(); - var state = AL.GetSourceState(SourceHandle); - Master._checkAlError(); - return state == ALSourceState.Playing; - } - set - { - _checkDisposed(); - - if (value) - { - AL.SourcePlay(SourceHandle); - } - else - { - AL.SourceStop(SourceHandle); - } - - - Master._checkAlError(); - } - } - - /// - public bool Looping - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourceb.Looping, out var ret); - Master._checkAlError(); - return ret; - } - set - { - _checkDisposed(); - AL.Source(SourceHandle, ALSourceb.Looping, value); - Master._checkAlError(); - } - } - - /// - public bool Global - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourceb.SourceRelative, out var value); - Master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - AL.Source(SourceHandle, ALSourceb.SourceRelative, value); - Master._checkAlError(); - } - } - - /// - public virtual Vector2 Position - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSource3f.Position, out var x, out var y, out _); - Master._checkAlError(); - return new Vector2(x, y); - } - set - { - _checkDisposed(); - - var (x, y) = value; - - if (!AreFinite(x, y)) - { - return; - } - - AL.Source(SourceHandle, ALSource3f.Position, x, y, 0); - Master._checkAlError(); - } - } - - /// - public float Pitch { get; set; } - - /// - public float Volume - { - get - { - var gain = Gain; - var volume = 10f * MathF.Log10(gain); - return volume; - } - set => Gain = MathF.Pow(10, value / 10); - } - - /// - public float Gain - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourcef.Gain, out var gain); - Master._checkAlError(); - return gain; - } - set - { - _checkDisposed(); - var priorOcclusion = 1f; - if (!IsEfxSupported) - { - AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain); - priorOcclusion = priorGain / _gain; - } - - _gain = value; - AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion); - Master._checkAlError(); - } - } - - /// - public float MaxDistance - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourcef.MaxDistance, out var value); - Master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - AL.Source(SourceHandle, ALSourcef.MaxDistance, value); - Master._checkAlError(); - } - } - - /// - public float RolloffFactor - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourcef.RolloffFactor, out var value); - Master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - AL.Source(SourceHandle, ALSourcef.RolloffFactor, value); - Master._checkAlError(); - } - } - - /// - public float ReferenceDistance - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourcef.ReferenceDistance, out var value); - Master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - AL.Source(SourceHandle, ALSourcef.ReferenceDistance, value); - Master._checkAlError(); - } - } - - /// - public float Occlusion - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourcef.MaxDistance, out var value); - Master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - var cutoff = MathF.Exp(-value * 1); - var gain = MathF.Pow(cutoff, 0.1f); - if (IsEfxSupported) - { - SetOcclusionEfx(gain, cutoff); - } - else - { - gain *= gain * gain; - AL.Source(SourceHandle, ALSourcef.Gain, _gain * gain); - } - Master._checkAlError(); - } - } - - /// - public float PlaybackPosition - { - get - { - _checkDisposed(); - AL.GetSource(SourceHandle, ALSourcef.SecOffset, out var value); - Master._checkAlError(); - return value; - } - set - { - _checkDisposed(); - AL.Source(SourceHandle, ALSourcef.SecOffset, value); - Master._checkAlError(); - } - } - - /// - public Vector2 Velocity - { - get - { - _checkDisposed(); - - AL.GetSource(SourceHandle, ALSource3f.Velocity, out var x, out var y, out _); - Master._checkAlError(); - return new Vector2(x, y); - } - set - { - _checkDisposed(); - - var (x, y) = value; - - if (!AreFinite(x, y)) - { - return; - } - - AL.Source(SourceHandle, ALSource3f.Velocity, x, y, 0); - Master._checkAlError(); - } - } - - public void SetAuxiliary(IAuxiliaryAudio? audio) - { - _checkDisposed(); - - if (audio is AuxiliaryAudio impAudio) - { - EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, impAudio.Handle, 0, 0); - } - else - { - EFX.Source(SourceHandle, EFXSourceInteger3.AuxiliarySendFilter, 0, 0, 0); - } - - Master._checkAlError(); - } - - private void SetOcclusionEfx(float gain, float cutoff) - { - if (FilterHandle == 0) - { - FilterHandle = EFX.GenFilter(); - EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass); - } - - EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain); - EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff); - AL.Source(SourceHandle, ALSourcei.EfxDirectFilter, FilterHandle); - } - - protected static bool AreFinite(float x, float y) - { - if (float.IsFinite(x) && float.IsFinite(y)) - { - return true; - } - - return false; - } - - ~BaseAudioSource() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected abstract void Dispose(bool disposing); - - protected bool _isDisposed() - { - return SourceHandle == -1; - } - - protected void _checkDisposed() - { - if (SourceHandle == -1) - { - throw new ObjectDisposedException(nameof(BaseAudioSource)); - } - } -} diff --git a/Robust.Client/Audio/Sources/BufferedAudioSource.cs b/Robust.Client/Audio/Sources/BufferedAudioSource.cs deleted file mode 100644 index 31ed004b883..00000000000 --- a/Robust.Client/Audio/Sources/BufferedAudioSource.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using OpenTK.Audio.OpenAL; -using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; -using Robust.Client.Graphics; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Maths; - -namespace Robust.Client.Audio.Sources; - -internal sealed class BufferedAudioSource : BaseAudioSource, IBufferedAudioSource -{ - private int? SourceHandle = null; - private int[] BufferHandles; - private Dictionary BufferMap = new(); - private readonly AudioManager _master; - private bool _mono = true; - private bool _float = false; - private int FilterHandle; - - private float _gain; - - public int SampleRate { get; set; } = 44100; - - private bool IsEfxSupported => _master.IsEfxSupported; - - public BufferedAudioSource(AudioManager master, int sourceHandle, int[] bufferHandles, bool floatAudio = false) : base(master, sourceHandle) - { - _master = master; - SourceHandle = sourceHandle; - BufferHandles = bufferHandles; - for (int i = 0; i < BufferHandles.Length; i++) - { - var bufferHandle = BufferHandles[i]; - BufferMap[bufferHandle] = i; - } - _float = floatAudio; - AL.GetSource(sourceHandle, ALSourcef.Gain, out _gain); - } - - /// - public override bool Playing - { - get - { - _checkDisposed(); - var state = AL.GetSourceState(SourceHandle!.Value); - _master._checkAlError(); - return state == ALSourceState.Playing; - } - set - { - if (value) - { - _checkDisposed(); - // IDK why this stackallocs but gonna leave it for now. - AL.SourcePlay(stackalloc int[] {SourceHandle!.Value}); - _master._checkAlError(); - } - else - { - if (_isDisposed()) - return; - - AL.SourceStop(SourceHandle!.Value); - _master._checkAlError(); - } - } - } - - ~BufferedAudioSource() - { - Dispose(false); - } - - protected override void Dispose(bool disposing) - { - if (SourceHandle == null) - return; - - if (!_master.IsMainThread()) - { - // We can't run this code inside another thread so tell Clyde to clear it up later. - _master.DeleteBufferedSourceOnMainThread(SourceHandle.Value, FilterHandle); - - foreach (var handle in BufferHandles) - { - _master.DeleteAudioBufferOnMainThread(handle); - } - } - else - { - if (FilterHandle != 0) - EFX.DeleteFilter(FilterHandle); - - AL.DeleteSource(SourceHandle.Value); - AL.DeleteBuffers(BufferHandles); - _master.RemoveBufferedAudioSource(SourceHandle.Value); - _master._checkAlError(); - } - - FilterHandle = 0; - SourceHandle = null; - } - - public int GetNumberOfBuffersProcessed() - { - _checkDisposed(); - // ReSharper disable once PossibleInvalidOperationException - AL.GetSource(SourceHandle!.Value, ALGetSourcei.BuffersProcessed, out var buffersProcessed); - return buffersProcessed; - } - - public unsafe void GetBuffersProcessed(Span handles) - { - _checkDisposed(); - var entries = Math.Min(Math.Min(handles.Length, BufferHandles.Length), GetNumberOfBuffersProcessed()); - fixed (int* ptr = handles) - { - AL.SourceUnqueueBuffers(SourceHandle!.Value, entries, ptr); - } - - for (var i = 0; i < entries; i++) - { - handles[i] = BufferMap[handles[i]]; - } - } - - public unsafe void WriteBuffer(int handle, ReadOnlySpan data) - { - _checkDisposed(); - - if(_float) - throw new InvalidOperationException("Can't write ushort numbers to buffers when buffer type is float!"); - - if (handle >= BufferHandles.Length) - { - throw new ArgumentOutOfRangeException(nameof(handle), - $"Got {handle}. Expected less than {BufferHandles.Length}"); - } - - fixed (ushort* ptr = data) - { - AL.BufferData(BufferHandles[handle], _mono ? ALFormat.Mono16 : ALFormat.Stereo16, (IntPtr) ptr, - _mono ? data.Length / 2 * sizeof(ushort) : data.Length * sizeof(ushort), SampleRate); - } - } - - public unsafe void WriteBuffer(int handle, ReadOnlySpan data) - { - _checkDisposed(); - - if(!_float) - throw new InvalidOperationException("Can't write float numbers to buffers when buffer type is ushort!"); - - if (handle >= BufferHandles.Length) - { - throw new ArgumentOutOfRangeException(nameof(handle), - $"Got {handle}. Expected less than {BufferHandles.Length}"); - } - - fixed (float* ptr = data) - { - AL.BufferData(BufferHandles[handle], _mono ? ALFormat.MonoFloat32Ext : ALFormat.StereoFloat32Ext, (IntPtr) ptr, - _mono ? data.Length / 2 * sizeof(float) : data.Length * sizeof(float), SampleRate); - } - } - - public unsafe void QueueBuffers(ReadOnlySpan handles) - { - _checkDisposed(); - - Span realHandles = stackalloc int[handles.Length]; - handles.CopyTo(realHandles); - - for (var i = 0; i < realHandles.Length; i++) - { - var handle = realHandles[i]; - if (handle >= BufferHandles.Length) - throw new ArgumentOutOfRangeException(nameof(handles), $"Invalid handle with index {i}!"); - realHandles[i] = BufferHandles[handle]; - } - - fixed (int* ptr = realHandles) - // ReSharper disable once PossibleInvalidOperationException - { - AL.SourceQueueBuffers(SourceHandle!.Value, handles.Length, ptr); - } - } - - public unsafe void EmptyBuffers() - { - _checkDisposed(); - var length = SampleRate / BufferHandles.Length * (_mono ? 1 : 2); - - Span handles = stackalloc int[BufferHandles.Length]; - - if (_float) - { - var empty = new float[length]; - var span = (Span) empty; - - for (var i = 0; i < BufferHandles.Length; i++) - { - WriteBuffer(BufferMap[BufferHandles[i]], span); - handles[i] = BufferMap[BufferHandles[i]]; - } - } - else - { - var empty = new ushort[length]; - var span = (Span) empty; - - for (var i = 0; i < BufferHandles.Length; i++) - { - WriteBuffer(BufferMap[BufferHandles[i]], span); - handles[i] = BufferMap[BufferHandles[i]]; - } - } - - QueueBuffers(handles); - } -} diff --git a/Robust.Client/ClientIoC.cs b/Robust.Client/ClientIoC.cs index da5064ef0cf..4e6f377d19c 100644 --- a/Robust.Client/ClientIoC.cs +++ b/Robust.Client/ClientIoC.cs @@ -1,5 +1,4 @@ using System; -using Robust.Client.Audio; using Robust.Client.Audio.Midi; using Robust.Client.Configuration; using Robust.Client.Console; @@ -7,6 +6,7 @@ using Robust.Client.GameObjects; using Robust.Client.GameStates; using Robust.Client.Graphics; +using Robust.Client.Graphics.Audio; using Robust.Client.Graphics.Clyde; using Robust.Client.Input; using Robust.Client.Map; @@ -29,7 +29,6 @@ using Robust.Client.Utility; using Robust.Client.ViewVariables; using Robust.Shared; -using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; @@ -73,11 +72,10 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle deps.Register(); deps.Register(); deps.Register(); - deps.Register(); - deps.Register(); - deps.Register(); - deps.Register(); + deps.Register(); + deps.Register(); deps.Register(); + deps.Register(); deps.Register(); deps.Register(); deps.Register(); @@ -109,8 +107,8 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle deps.Register(); deps.Register(); deps.Register(); - deps.Register(); - deps.Register(); + deps.Register(); + deps.Register(); deps.Register(); deps.Register(); deps.Register(); @@ -119,8 +117,8 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle deps.Register(); deps.Register(); deps.Register(); - deps.Register(); - deps.Register(); + deps.Register(); + deps.Register(); deps.Register(); deps.Register(); deps.Register(); diff --git a/Robust.Client/Console/Commands/Debug.cs b/Robust.Client/Console/Commands/Debug.cs index ee19401ff12..3540aeeeae4 100644 --- a/Robust.Client/Console/Commands/Debug.cs +++ b/Robust.Client/Console/Commands/Debug.cs @@ -15,7 +15,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Asynchronous; -using Robust.Shared.Audio; using Robust.Shared.Console; using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; @@ -355,7 +354,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) internal sealed class LoadResource : LocalizedCommands { - [Dependency] private readonly IClientResourceCache _res = default!; + [Dependency] private readonly IResourceCache _res = default!; [Dependency] private readonly IReflectionManager _reflection = default!; public override string Command => "ldrsc"; @@ -392,7 +391,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) internal sealed class ReloadResource : LocalizedCommands { - [Dependency] private readonly IClientResourceCache _res = default!; + [Dependency] private readonly IResourceCache _res = default!; [Dependency] private readonly IReflectionManager _reflection = default!; public override string Command => "rldrsc"; @@ -459,13 +458,13 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) internal sealed class GuiDumpCommand : LocalizedCommands { [Dependency] private readonly IUserInterfaceManager _ui = default!; - [Dependency] private readonly IResourceManager _resManager = default!; + [Dependency] private readonly IResourceCache _res = default!; public override string Command => "guidump"; public override void Execute(IConsoleShell shell, string argStr, string[] args) { - using var writer = _resManager.UserData.OpenWriteText(new ResPath("/guidump.txt")); + using var writer = _res.UserData.OpenWriteText(new ResPath("/guidump.txt")); foreach (var root in _ui.AllRoots) { @@ -645,8 +644,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) internal sealed class ReloadShadersCommand : LocalizedCommands { - [Dependency] private readonly IResourceCache _cache = default!; - [Dependency] private readonly IResourceManagerInternal _resManager = default!; + [Dependency] private readonly IResourceCacheInternal _res = default!; [Dependency] private readonly ITaskManager _taskManager = default!; public override string Command => "rldshader"; @@ -657,7 +655,7 @@ internal sealed class ReloadShadersCommand : LocalizedCommands public override void Execute(IConsoleShell shell, string argStr, string[] args) { - var resC = _resManager; + var resC = _res; if (args.Length == 1) { if (args[0] == "+watch") @@ -681,9 +679,9 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) var shaderCount = 0; var created = 0; var dirs = new ConcurrentDictionary>(stringComparer); - foreach (var (path, src) in _cache.GetAllResources()) + foreach (var (path, src) in resC.GetAllResources()) { - if (!_resManager.TryGetDiskFilePath(path, out var fullPath)) + if (!resC.TryGetDiskFilePath(path, out var fullPath)) { throw new NotImplementedException(); } @@ -732,7 +730,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) { try { - _cache.ReloadResource(resPath); + resC.ReloadResource(resPath); shell.WriteLine($"Reloaded shader: {resPath}"); } catch (Exception) @@ -793,11 +791,11 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) shell.WriteLine("Reloading content shader resources..."); - foreach (var (path, _) in _cache.GetAllResources()) + foreach (var (path, _) in resC.GetAllResources()) { try { - _cache.ReloadResource(path); + resC.ReloadResource(path); } catch (Exception) { diff --git a/Robust.Client/Debugging/DebugPhysicsSystem.cs b/Robust.Client/Debugging/DebugPhysicsSystem.cs index 9e9ec237ad6..ec4bb760ac0 100644 --- a/Robust.Client/Debugging/DebugPhysicsSystem.cs +++ b/Robust.Client/Debugging/DebugPhysicsSystem.cs @@ -96,7 +96,7 @@ public PhysicsDebugFlags Flags IoCManager.Resolve(), IoCManager.Resolve(), IoCManager.Resolve(), - IoCManager.Resolve(), + IoCManager.Resolve(), this, Get(), Get())); @@ -208,7 +208,7 @@ internal sealed class PhysicsDebugOverlay : Overlay private HashSet _drawnJoints = new(); private List> _grids = new(); - public PhysicsDebugOverlay(IEntityManager entityManager, IEyeManager eyeManager, IInputManager inputManager, IMapManager mapManager, IPlayerManager playerManager, IClientResourceCache cache, DebugPhysicsSystem system, EntityLookupSystem lookup, SharedPhysicsSystem physicsSystem) + public PhysicsDebugOverlay(IEntityManager entityManager, IEyeManager eyeManager, IInputManager inputManager, IMapManager mapManager, IPlayerManager playerManager, IResourceCache cache, DebugPhysicsSystem system, EntityLookupSystem lookup, SharedPhysicsSystem physicsSystem) { _entityManager = entityManager; _eyeManager = eyeManager; diff --git a/Robust.Client/GameController/GameController.cs b/Robust.Client/GameController/GameController.cs index 308caaa39f1..6e5e2ad876a 100644 --- a/Robust.Client/GameController/GameController.cs +++ b/Robust.Client/GameController/GameController.cs @@ -4,7 +4,6 @@ using System.Net; using System.Runtime; using System.Threading.Tasks; -using Robust.Client.Audio; using Robust.Client.Audio.Midi; using Robust.Client.Console; using Robust.Client.GameObjects; @@ -25,7 +24,6 @@ using Robust.LoaderApi; using Robust.Shared; using Robust.Shared.Asynchronous; -using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.ContentPack; using Robust.Shared.Exceptions; @@ -50,8 +48,7 @@ namespace Robust.Client internal sealed partial class GameController : IGameControllerInternal { [Dependency] private readonly INetConfigurationManagerInternal _configurationManager = default!; - [Dependency] private readonly IClientResourceCacheInternal _resourceCache = default!; - [Dependency] private readonly IResourceManagerInternal _resManager = default!; + [Dependency] private readonly IResourceCacheInternal _resourceCache = default!; [Dependency] private readonly IRobustSerializer _serializer = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IClientNetManager _networkManager = default!; @@ -71,7 +68,7 @@ internal sealed partial class GameController : IGameControllerInternal [Dependency] private readonly IClientViewVariablesManagerInternal _viewVariablesManager = default!; [Dependency] private readonly IDiscordRichPresence _discord = default!; [Dependency] private readonly IClydeInternal _clyde = default!; - [Dependency] private readonly IAudioInternal _audio = default!; + [Dependency] private readonly IClydeAudioInternal _clydeAudio = default!; [Dependency] private readonly IFontManagerInternal _fontManager = default!; [Dependency] private readonly IModLoaderInternal _modLoader = default!; [Dependency] private readonly IScriptClient _scriptClient = default!; @@ -114,7 +111,7 @@ internal bool StartupContinue(DisplayMode displayMode) DebugTools.AssertNotNull(_resourceManifest); _clyde.InitializePostWindowing(); - _audio.InitializePostWindowing(); + _clydeAudio.InitializePostWindowing(); _clyde.SetWindowTitle( Options.DefaultWindowTitle ?? _resourceManifest!.DefaultWindowTitle ?? "RobustToolbox"); @@ -151,7 +148,7 @@ internal bool StartupContinue(DisplayMode displayMode) // Start bad file extensions check after content init, // in case content screws with the VFS. var checkBadExtensions = ProgramShared.CheckBadFileExtensions( - _resManager, + _resourceCache, _configurationManager, _logManager.GetSawmill("res")); @@ -363,13 +360,13 @@ internal bool StartupSystemSplash( _parallelMgr.Initialize(); _prof.Initialize(); - _resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null); + _resourceCache.Initialize(Options.LoadConfigAndUserData ? userDataDir : null); var mountOptions = _commandLineArgs != null ? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions) : Options.MountOptions; - ProgramShared.DoMounts(_resManager, mountOptions, Options.ContentBuildDirectory, + ProgramShared.DoMounts(_resourceCache, mountOptions, Options.ContentBuildDirectory, Options.AssemblyDirectory, Options.LoadContentResources, _loaderArgs != null && !Options.ResourceMountDisabled, ContentStart); @@ -379,16 +376,16 @@ internal bool StartupSystemSplash( { foreach (var (api, prefix) in mounts) { - _resourceCache.MountLoaderApi(_resManager, api, "", new(prefix)); + _resourceCache.MountLoaderApi(api, "", new(prefix)); } } _stringSerializer.EnableCaching = false; - _resourceCache.MountLoaderApi(_resManager, _loaderArgs.FileApi, "Resources/"); + _resourceCache.MountLoaderApi(_loaderArgs.FileApi, "Resources/"); _modLoader.VerifierExtraLoadHandler = VerifierExtraLoadHandler; } - _resourceManifest = ResourceManifestData.LoadResourceManifest(_resManager); + _resourceManifest = ResourceManifestData.LoadResourceManifest(_resourceCache); { // Handle GameControllerOptions implicit CVar overrides. @@ -570,6 +567,11 @@ private void Update(FrameEventArgs frameEventArgs) } } + using (_prof.Group("ClydeAudio")) + { + _clydeAudio.FrameProcess(frameEventArgs); + } + using (_prof.Group("Clyde")) { _clyde.FrameProcess(frameEventArgs); @@ -708,7 +710,7 @@ internal void CleanupGameThread() internal void CleanupWindowThread() { _clyde.Shutdown(); - _audio.Shutdown(); + _clydeAudio.Shutdown(); } public event Action? TickUpdateOverride; diff --git a/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs b/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs index 9499e8ca419..dcd434ed3dd 100644 --- a/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs +++ b/Robust.Client/GameObjects/Components/Renderable/SpriteComponent.cs @@ -36,7 +36,7 @@ namespace Robust.Client.GameObjects [RegisterComponent] public sealed partial class SpriteComponent : Component, IComponentDebug, ISerializationHooks, IComponentTreeEntry, IAnimationProperties { - [Dependency] private readonly IClientResourceCache resourceCache = default!; + [Dependency] private readonly IResourceCache resourceCache = default!; [Dependency] private readonly IPrototypeManager prototypes = default!; [Dependency] private readonly IEntityManager entities = default!; [Dependency] private readonly IReflectionManager reflection = default!; @@ -1379,7 +1379,7 @@ private void QueueUpdateIsInert() } [Obsolete("Use SpriteSystem instead.")] - internal static RSI.State GetFallbackState(IClientResourceCache cache) + internal static RSI.State GetFallbackState(IResourceCache cache) { var rsi = cache.GetResource("/Textures/error.rsi").RSI; return rsi["error"]; @@ -2101,12 +2101,12 @@ public IRsiStateLike? Icon } } - public static IEnumerable GetPrototypeTextures(EntityPrototype prototype, IClientResourceCache resourceCache) + public static IEnumerable GetPrototypeTextures(EntityPrototype prototype, IResourceCache resourceCache) { return GetPrototypeTextures(prototype, resourceCache, out var _); } - public static IEnumerable GetPrototypeTextures(EntityPrototype prototype, IClientResourceCache resourceCache, out bool noRot) + public static IEnumerable GetPrototypeTextures(EntityPrototype prototype, IResourceCache resourceCache, out bool noRot) { var results = new List(); noRot = false; @@ -2161,7 +2161,7 @@ public static IEnumerable GetPrototypeTextures(Enti } [Obsolete("Use SpriteSystem")] - public static IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IClientResourceCache resourceCache) + public static IRsiStateLike GetPrototypeIcon(EntityPrototype prototype, IResourceCache resourceCache) { // TODO when moving to a non-static method in a system, pass in IComponentFactory if (prototype.TryGetComponent(out IconComponent? icon)) diff --git a/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs b/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs new file mode 100644 index 00000000000..70ac0c61eb8 --- /dev/null +++ b/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs @@ -0,0 +1,626 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Robust.Client.Audio; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Shared; +using Robust.Shared.Audio; +using Robust.Shared.Exceptions; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Random; +using Robust.Shared.Replays; +using Robust.Shared.Threading; +using Robust.Shared.Timing; +using Robust.Shared.Utility; + +namespace Robust.Client.GameObjects; + +[UsedImplicitly] +public sealed class AudioSystem : SharedAudioSystem +{ + [Dependency] private readonly IReplayRecordingManager _replayRecording = default!; + [Dependency] private readonly SharedPhysicsSystem _broadPhaseSystem = default!; + [Dependency] private readonly IClydeAudio _clyde = default!; + [Dependency] private readonly IEyeManager _eyeManager = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IParallelManager _parMan = default!; + [Dependency] private readonly SharedTransformSystem _xformSys = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly IRuntimeLog _runtimeLog = default!; + [Dependency] private readonly ILogManager _logManager = default!; + + private readonly List _playingClydeStreams = new(); + + private ISawmill _sawmill = default!; + + private float _maxRayLength; + + /// + public override void Initialize() + { + base.Initialize(); + SubscribeNetworkEvent(PlayAudioEntityHandler); + SubscribeNetworkEvent(PlayAudioGlobalHandler); + SubscribeNetworkEvent(PlayAudioPositionalHandler); + SubscribeNetworkEvent(StopAudioMessageHandler); + + _sawmill = _logManager.GetSawmill("audio"); + + CfgManager.OnValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged, true); + } + + public override void Shutdown() + { + CfgManager.UnsubValueChanged(CVars.AudioRaycastLength, OnRaycastLengthChanged); + foreach (var stream in _playingClydeStreams) + { + stream.Source.Dispose(); + } + _playingClydeStreams.Clear(); + + base.Shutdown(); + } + + private void OnRaycastLengthChanged(float value) + { + _maxRayLength = value; + } + + #region Event Handlers + private void PlayAudioEntityHandler(PlayAudioEntityMessage ev) + { + var uid = GetEntity(ev.NetEntity); + var coords = GetCoordinates(ev.Coordinates); + var fallback = GetCoordinates(ev.FallbackCoordinates); + + var stream = EntityManager.EntityExists(uid) + ? (PlayingStream?) Play(ev.FileName, uid, fallback, ev.AudioParams, false) + : (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false); + + if (stream != null) + stream.NetIdentifier = ev.Identifier; + } + + private void PlayAudioGlobalHandler(PlayAudioGlobalMessage ev) + { + var stream = (PlayingStream?) Play(ev.FileName, ev.AudioParams, false); + if (stream != null) + stream.NetIdentifier = ev.Identifier; + } + + private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev) + { + var coords = GetCoordinates(ev.Coordinates); + var fallback = GetCoordinates(ev.FallbackCoordinates); + + var stream = (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false); + if (stream != null) + stream.NetIdentifier = ev.Identifier; + } + + private void StopAudioMessageHandler(StopAudioMessageClient ev) + { + var stream = _playingClydeStreams.Find(p => p.NetIdentifier == ev.Identifier); + if (stream == null) + return; + + stream.Done = true; + stream.Source.Dispose(); + _playingClydeStreams.Remove(stream); + } + #endregion + + public override void FrameUpdate(float frameTime) + { + var xforms = GetEntityQuery(); + var physics = GetEntityQuery(); + var ourPos = _eyeManager.CurrentEye.Position; + var opts = new ParallelOptions { MaxDegreeOfParallelism = _parMan.ParallelProcessCount }; + + try + { + Parallel.ForEach(_playingClydeStreams, opts, (stream) => ProcessStream(stream, ourPos, xforms, physics)); + } + catch (Exception e) + { + _sawmill.Error($"Caught exception while processing entity streams."); + _runtimeLog.LogException(e, $"{nameof(AudioSystem)}.{nameof(FrameUpdate)}"); + } + finally + { + + for (var i = _playingClydeStreams.Count - 1; i >= 0; i--) + { + var stream = _playingClydeStreams[i]; + if (stream.Done) + { + stream.Source.Dispose(); + _playingClydeStreams.RemoveSwap(i); + } + } + } + } + + private void ProcessStream(PlayingStream stream, + MapCoordinates listener, + EntityQuery xforms, + EntityQuery physics) + { + if (!stream.Source.IsPlaying) + { + stream.Done = true; + return; + } + + if (stream.Source.IsGlobal) + { + DebugTools.Assert(stream.TrackingCoordinates == null + && stream.TrackingEntity == null + && stream.TrackingFallbackCoordinates == null); + + return; + } + + DebugTools.Assert(stream.TrackingCoordinates != null + || stream.TrackingEntity != null + || stream.TrackingFallbackCoordinates != null); + + // Get audio Position + if (!TryGetStreamPosition(stream, xforms, out var mapPos) + || mapPos == MapCoordinates.Nullspace + || mapPos.Value.MapId != listener.MapId) + { + stream.Done = true; + return; + } + + // Max distance check + var delta = mapPos.Value.Position - listener.Position; + var distance = delta.Length(); + if (distance > stream.MaxDistance) + { + stream.Source.SetVolumeDirect(0); + return; + } + + // Update audio occlusion + float occlusion = 0; + if (distance > 0.1) + { + var rayLength = MathF.Min(distance, _maxRayLength); + var ray = new CollisionRay(listener.Position, delta/distance, OcclusionCollisionMask); + occlusion = _broadPhaseSystem.IntersectRayPenetration(listener.MapId, ray, rayLength, stream.TrackingEntity); + } + stream.Source.SetOcclusion(occlusion); + + // Update attenuation dependent volume. + UpdatePositionalVolume(stream, distance); + + // Update audio positions. + var audioPos = stream.Attenuation != Attenuation.NoAttenuation ? mapPos.Value : listener; + if (!stream.Source.SetPosition(audioPos.Position)) + { + _sawmill.Warning("Interrupting positional audio, can't set position."); + stream.Source.StopPlaying(); + return; + } + + // Make race cars go NYYEEOOOOOMMMMM + if (stream.TrackingEntity != null && physics.TryGetComponent(stream.TrackingEntity, out var physicsComp)) + { + // This actually gets the tracked entity's xform & iterates up though the parents for the second time. Bit + // inefficient. + var velocity = _physics.GetMapLinearVelocity(stream.TrackingEntity.Value, physicsComp, null, xforms, physics); + stream.Source.SetVelocity(velocity); + } + } + + private void UpdatePositionalVolume(PlayingStream stream, float distance) + { + // OpenAL also limits the distance to <= AL_MAX_DISTANCE, but since we cull + // sources that are further away than stream.MaxDistance, we don't do that. + distance = MathF.Max(stream.ReferenceDistance, distance); + float gain; + + // Technically these are formulas for gain not decibels but EHHHHHHHH. + switch (stream.Attenuation) + { + case Attenuation.Default: + gain = 1f; + break; + // You thought I'd implement clamping per source? Hell no that's just for the overall OpenAL setting + // I didn't even wanna implement this much for linear but figured it'd be cleaner. + case Attenuation.InverseDistanceClamped: + case Attenuation.InverseDistance: + gain = stream.ReferenceDistance + / (stream.ReferenceDistance + + stream.RolloffFactor * (distance - stream.ReferenceDistance)); + + break; + case Attenuation.LinearDistanceClamped: + case Attenuation.LinearDistance: + gain = 1f + - stream.RolloffFactor + * (distance - stream.ReferenceDistance) + / (stream.MaxDistance - stream.ReferenceDistance); + + break; + case Attenuation.ExponentDistanceClamped: + case Attenuation.ExponentDistance: + gain = MathF.Pow(distance / stream.ReferenceDistance, -stream.RolloffFactor); + break; + default: + throw new ArgumentOutOfRangeException( + $"No implemented attenuation for {stream.Attenuation}"); + } + + var volume = MathF.Pow(10, stream.Volume / 10); + var actualGain = MathF.Max(0f, volume * gain); + stream.Source.SetVolumeDirect(actualGain); + } + + private bool TryGetStreamPosition(PlayingStream stream, EntityQuery xformQuery, [NotNullWhen(true)] out MapCoordinates? mapPos) + { + if (stream.TrackingCoordinates != null) + { + mapPos = stream.TrackingCoordinates.Value.ToMap(EntityManager); + if (mapPos != MapCoordinates.Nullspace) + return true; + } + + if (xformQuery.TryGetComponent(stream.TrackingEntity, out var xform) + && xform.MapID != MapId.Nullspace) + { + mapPos = new MapCoordinates(_xformSys.GetWorldPosition(xform, xformQuery), xform.MapID); + return true; + } + + if (stream.TrackingFallbackCoordinates != null) + { + mapPos = stream.TrackingFallbackCoordinates.Value.ToMap(EntityManager); + return mapPos != MapCoordinates.Nullspace; + } + + mapPos = MapCoordinates.Nullspace; + return false; + } + + #region Play AudioStream + private bool TryGetAudio(string filename, [NotNullWhen(true)] out AudioResource? audio) + { + if (_resourceCache.TryGetResource(new ResPath(filename), out audio)) + return true; + + _sawmill.Error($"Server tried to play audio file {filename} which does not exist."); + return false; + } + + private bool TryCreateAudioSource(AudioStream stream, [NotNullWhen(true)] out IClydeAudioSource? source) + { + if (!_timing.IsFirstTimePredicted) + { + source = null; + _sawmill.Error($"Tried to create audio source outside of prediction!"); + DebugTools.Assert(false); + return false; + } + + source = _clyde.CreateAudioSource(stream); + return source != null; + } + + private PlayingStream CreateAndStartPlayingStream(IClydeAudioSource source, AudioParams? audioParams, AudioStream stream) + { + ApplyAudioParams(audioParams, source, stream); + source.StartPlaying(); + var playing = new PlayingStream + { + Source = source, + Attenuation = audioParams?.Attenuation ?? Attenuation.Default, + MaxDistance = audioParams?.MaxDistance ?? float.MaxValue, + ReferenceDistance = audioParams?.ReferenceDistance ?? 1f, + RolloffFactor = audioParams?.RolloffFactor ?? 1f, + Volume = audioParams?.Volume ?? 0 + }; + _playingClydeStreams.Add(playing); + return playing; + } + + /// + /// Play an audio file globally, without position. + /// + /// The resource path to the OGG Vorbis file to play. + /// + private IPlayingAudioStream? Play(string filename, AudioParams? audioParams = null, bool recordReplay = true) + { + if (recordReplay && _replayRecording.IsRecording) + { + _replayRecording.RecordReplayMessage(new PlayAudioGlobalMessage + { + FileName = filename, + AudioParams = audioParams ?? AudioParams.Default + }); + } + + return TryGetAudio(filename, out var audio) ? Play(audio, audioParams) : default; + } + + /// + /// Play an audio stream globally, without position. + /// + /// The audio stream to play. + /// + private IPlayingAudioStream? Play(AudioStream stream, AudioParams? audioParams = null) + { + if (!TryCreateAudioSource(stream, out var source)) + { + _sawmill.Error($"Error setting up global audio for {stream.Name}: {0}", Environment.StackTrace); + return null; + } + + source.SetGlobal(); + + return CreateAndStartPlayingStream(source, audioParams, stream); + } + + /// + /// Play an audio file following an entity. + /// + /// The resource path to the OGG Vorbis file to play. + /// The entity "emitting" the audio. + /// The map or grid coordinates at which to play the audio when entity is invalid. + /// + private IPlayingAudioStream? Play(string filename, EntityUid entity, EntityCoordinates? fallbackCoordinates, + AudioParams? audioParams = null, bool recordReplay = true) + { + if (recordReplay && _replayRecording.IsRecording) + { + _replayRecording.RecordReplayMessage(new PlayAudioEntityMessage + { + FileName = filename, + NetEntity = GetNetEntity(entity), + FallbackCoordinates = GetNetCoordinates(fallbackCoordinates) ?? default, + AudioParams = audioParams ?? AudioParams.Default + }); + } + + return TryGetAudio(filename, out var audio) ? Play(audio, entity, fallbackCoordinates, audioParams) : default; + } + + /// + /// Play an audio stream following an entity. + /// + /// The audio stream to play. + /// The entity "emitting" the audio. + /// The map or grid coordinates at which to play the audio when entity is invalid. + /// + private IPlayingAudioStream? Play(AudioStream stream, EntityUid entity, EntityCoordinates? fallbackCoordinates = null, + AudioParams? audioParams = null) + { + if (!TryCreateAudioSource(stream, out var source)) + { + _sawmill.Error($"Error setting up entity audio for {stream.Name} / {ToPrettyString(entity)}: {0}", Environment.StackTrace); + return null; + } + + var query = GetEntityQuery(); + var xform = query.GetComponent(entity); + var worldPos = _xformSys.GetWorldPosition(xform, query); + fallbackCoordinates ??= GetFallbackCoordinates(new MapCoordinates(worldPos, xform.MapID)); + + if (!source.SetPosition(worldPos)) + return Play(stream, fallbackCoordinates.Value, fallbackCoordinates.Value, audioParams); + + var playing = CreateAndStartPlayingStream(source, audioParams, stream); + playing.TrackingEntity = entity; + playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null; + return playing; + } + + /// + /// Play an audio file at a static position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The coordinates at which to play the audio. + /// The map or grid coordinates at which to play the audio when coordinates are invalid. + /// + private IPlayingAudioStream? Play(string filename, EntityCoordinates coordinates, + EntityCoordinates fallbackCoordinates, AudioParams? audioParams = null, bool recordReplay = true) + { + if (recordReplay && _replayRecording.IsRecording) + { + _replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage + { + FileName = filename, + Coordinates = GetNetCoordinates(coordinates), + FallbackCoordinates = GetNetCoordinates(fallbackCoordinates), + AudioParams = audioParams ?? AudioParams.Default + }); + } + + return TryGetAudio(filename, out var audio) ? Play(audio, coordinates, fallbackCoordinates, audioParams) : default; + } + + /// + /// Play an audio stream at a static position. + /// + /// The audio stream to play. + /// The coordinates at which to play the audio. + /// The map or grid coordinates at which to play the audio when coordinates are invalid. + /// + private IPlayingAudioStream? Play(AudioStream stream, EntityCoordinates coordinates, + EntityCoordinates fallbackCoordinates, AudioParams? audioParams = null) + { + if (!TryCreateAudioSource(stream, out var source)) + { + _sawmill.Error($"Error setting up coordinates audio for {stream.Name} / {coordinates}: {0}", Environment.StackTrace); + return null; + } + + if (!source.SetPosition(fallbackCoordinates.Position)) + { + source.Dispose(); + _sawmill.Warning($"Can't play positional audio \"{stream.Name}\", can't set position."); + return null; + } + + var playing = CreateAndStartPlayingStream(source, audioParams, stream); + playing.TrackingCoordinates = coordinates; + playing.TrackingFallbackCoordinates = fallbackCoordinates != EntityCoordinates.Invalid ? fallbackCoordinates : null; + return playing; + } + #endregion + + /// + public override IPlayingAudioStream? PlayPredicted(SoundSpecifier? sound, EntityUid source, EntityUid? user, + AudioParams? audioParams = null) + { + if (_timing.IsFirstTimePredicted || sound == null) + return Play(sound, Filter.Local(), source, false, audioParams); + return null; // uhh Lets hope predicted audio never needs to somehow store the playing audio.... + } + + public override IPlayingAudioStream? PlayPredicted(SoundSpecifier? sound, EntityCoordinates coordinates, EntityUid? user, + AudioParams? audioParams = null) + { + if (_timing.IsFirstTimePredicted || sound == null) + return Play(sound, Filter.Local(), coordinates, false, audioParams); + return null; + } + + private void ApplyAudioParams(AudioParams? audioParams, IClydeAudioSource source, AudioStream audio) + { + if (!audioParams.HasValue) + return; + + if (audioParams.Value.Variation.HasValue) + source.SetPitch(audioParams.Value.PitchScale + * (float) RandMan.NextGaussian(1, audioParams.Value.Variation.Value)); + else + source.SetPitch(audioParams.Value.PitchScale); + + source.SetVolume(audioParams.Value.Volume); + source.SetRolloffFactor(audioParams.Value.RolloffFactor); + source.SetMaxDistance(audioParams.Value.MaxDistance); + source.SetReferenceDistance(audioParams.Value.ReferenceDistance); + source.IsLooping = audioParams.Value.Loop; + + // TODO clamp the offset inside of SetPlaybackPosition() itself. + var offset = audioParams.Value.PlayOffsetSeconds; + offset = Math.Clamp(offset, 0f, (float) audio.Length.TotalSeconds); + source.SetPlaybackPosition(offset); + } + + public sealed class PlayingStream : IPlayingAudioStream + { + public uint? NetIdentifier; + public IClydeAudioSource Source = default!; + public EntityUid? TrackingEntity; + public EntityCoordinates? TrackingCoordinates; + public EntityCoordinates? TrackingFallbackCoordinates; + public bool Done; + + public float Volume + { + get => _volume; + set + { + _volume = value; + Source.SetVolume(value); + } + } + + private float _volume; + + public float MaxDistance; + public float ReferenceDistance; + public float RolloffFactor; + + public Attenuation Attenuation + { + get => _attenuation; + set + { + if (value == _attenuation) return; + _attenuation = value; + if (_attenuation != Attenuation.Default) + { + // Need to disable default attenuation when using a custom one + // Damn Sloth wanting linear ambience sounds so they smoothly cut-off and are short-range + Source.SetRolloffFactor(0f); + } + } + } + private Attenuation _attenuation = Attenuation.Default; + + public void Stop() + { + Source.StopPlaying(); + } + } + + /// + public override IPlayingAudioStream? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) + { + return Play(filename, audioParams); + } + + /// + public override IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null) + { + return Play(filename, entity, null, audioParams); + } + + /// + public override IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) + { + return Play(filename, coordinates, GetFallbackCoordinates(coordinates.ToMap(EntityManager)), audioParams); + } + + /// + public override IPlayingAudioStream? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null) + { + return Play(filename, audioParams); + } + + /// + public override IPlayingAudioStream? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null) + { + return Play(filename, audioParams); + } + + /// + public override IPlayingAudioStream? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) + { + return Play(filename, uid, null, audioParams); + } + + /// + public override IPlayingAudioStream? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) + { + return Play(filename, uid, null, audioParams); + } + + /// + public override IPlayingAudioStream? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + return Play(filename, coordinates, GetFallbackCoordinates(coordinates.ToMap(EntityManager)), audioParams); + } + + /// + public override IPlayingAudioStream? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + return Play(filename, coordinates, GetFallbackCoordinates(coordinates.ToMap(EntityManager)), audioParams); + } +} diff --git a/Robust.Client/GameObjects/EntitySystems/EyeSystem.cs b/Robust.Client/GameObjects/EntitySystems/EyeSystem.cs index a30b73aab44..f2a7bba70c6 100644 --- a/Robust.Client/GameObjects/EntitySystems/EyeSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/EyeSystem.cs @@ -1,9 +1,8 @@ using Robust.Client.Graphics; using Robust.Client.Physics; -using Robust.Client.Player; using Robust.Shared.GameObjects; -using Robust.Shared.Graphics; using Robust.Shared.IoC; +using Robust.Shared.Player; namespace Robust.Client.GameObjects; @@ -26,17 +25,13 @@ public override void Initialize() private void OnEyeAutoState(EntityUid uid, EyeComponent component, ref AfterAutoHandleStateEvent args) { - UpdateEye(component); + UpdateEye((uid, component)); } private void OnEyeAttached(EntityUid uid, EyeComponent component, LocalPlayerAttachedEvent args) { - // TODO: This probably shouldn't be nullable bruv. - if (component._eye != null) - { - _eyeManager.CurrentEye = component._eye; - } - + UpdateEye((uid, component)); + _eyeManager.CurrentEye = component.Eye; var ev = new EyeAttachedEvent(uid, component); RaiseLocalEvent(uid, ref ev, true); } @@ -48,13 +43,7 @@ private void OnEyeDetached(EntityUid uid, EyeComponent component, LocalPlayerDet private void OnInit(EntityUid uid, EyeComponent component, ComponentInit args) { - component._eye = new Eye - { - Position = Transform(uid).MapPosition, - Zoom = component.Zoom, - DrawFov = component.DrawFov, - Rotation = component.Rotation, - }; + UpdateEye((uid, component)); } /// @@ -64,7 +53,7 @@ public override void FrameUpdate(float frameTime) while (query.MoveNext(out var uid, out var eyeComponent)) { - if (eyeComponent._eye == null) + if (eyeComponent.Eye == null) continue; if (!TryComp(eyeComponent.Target, out var xform)) @@ -73,7 +62,7 @@ public override void FrameUpdate(float frameTime) eyeComponent.Target = null; } - eyeComponent._eye.Position = xform.MapPosition; + eyeComponent.Eye.Position = xform.MapPosition; } } } diff --git a/Robust.Client/GameObjects/EntitySystems/MapSystem.cs b/Robust.Client/GameObjects/EntitySystems/MapSystem.cs index e57dbc7bb6b..b9ed64b92d9 100644 --- a/Robust.Client/GameObjects/EntitySystems/MapSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/MapSystem.cs @@ -11,7 +11,7 @@ public sealed class MapSystem : SharedMapSystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IOverlayManager _overlayManager = default!; - [Dependency] private readonly IClientResourceCache _resource = default!; + [Dependency] private readonly IResourceCache _resource = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; public override void Initialize() diff --git a/Robust.Client/GameObjects/EntitySystems/PointLightSystem.cs b/Robust.Client/GameObjects/EntitySystems/PointLightSystem.cs index c70a21d1c84..a29a7d01537 100644 --- a/Robust.Client/GameObjects/EntitySystems/PointLightSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/PointLightSystem.cs @@ -10,7 +10,7 @@ namespace Robust.Client.GameObjects { public sealed class PointLightSystem : SharedPointLightSystem { - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly LightTreeSystem _lightTree = default!; public override void Initialize() diff --git a/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs b/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs index 4c1f541c100..0067b886387 100644 --- a/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/SpriteSystem.cs @@ -32,7 +32,7 @@ public sealed partial class SpriteSystem : EntitySystem [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly ILogManager _logManager = default!; private readonly Queue _inertUpdateQueue = new(); diff --git a/Robust.Client/GameObjects/EntitySystems/TransformSystem.cs b/Robust.Client/GameObjects/EntitySystems/TransformSystem.cs index 91540803b05..9f997d74e49 100644 --- a/Robust.Client/GameObjects/EntitySystems/TransformSystem.cs +++ b/Robust.Client/GameObjects/EntitySystems/TransformSystem.cs @@ -54,9 +54,10 @@ public override void ActivateLerp(EntityUid uid, TransformComponent xform) // should show the entity lerping. // - If the client predicts an entity will move while already lerping due to a state-application, it should // clear the state's lerp, under the assumption that the client predicted the state and already rendered - // the entity in the final position. + // the entity in the state's final position. // - If the client predicts that an entity moves, then we only lerp if this is the first time that the tick - // was predicted. I.e., we assume the entity was already rendered in it's final of that lerp. + // was predicted. I.e., we assume the entity was already rendered in the final position that was + // previously predicted. // - If the client predicts that an entity should lerp twice in the same tick, then we need to combine them. // I.e. moving from a->b then b->c, the client should lerp from a->c. diff --git a/Robust.Client/GameStates/ClientGameStateManager.cs b/Robust.Client/GameStates/ClientGameStateManager.cs index ef84e427dd7..0756bf8ac53 100644 --- a/Robust.Client/GameStates/ClientGameStateManager.cs +++ b/Robust.Client/GameStates/ClientGameStateManager.cs @@ -129,6 +129,8 @@ public sealed class ClientGameStateManager : IClientGameStateManager public bool DropStates; #endif + private bool _resettingPredictedEntities; + /// public void Initialize() { @@ -151,6 +153,7 @@ public void Initialize() _config.OnValueChanged(CVars.NetPredictLagBias, i => PredictLagBias = i, true); _config.OnValueChanged(CVars.NetStateBufMergeThreshold, i => StateBufferMergeThreshold = i, true); _config.OnValueChanged(CVars.NetPVSEntityExitBudget, i => _pvsDetachBudget = i, true); + _config.OnValueChanged(CVars.NetMaxBufferSize, i => _processor.MaxBufferSize = i, true); _processor.Interpolation = _config.GetCVar(CVars.NetInterp); _processor.BufferSize = _config.GetCVar(CVars.NetBufferSize); @@ -165,6 +168,8 @@ public void Initialize() _conHost.RegisterCommand("localdelete", Loc.GetString("cmd-local-delete-desc"), Loc.GetString("cmd-local-delete-help"), LocalDeleteEntCommand); _conHost.RegisterCommand("fullstatereset", Loc.GetString("cmd-full-state-reset-desc"), Loc.GetString("cmd-full-state-reset-help"), (_,_,_) => RequestFullState()); + _entities.ComponentAdded += OnComponentAdded; + var metaId = _compFactory.GetRegistration(typeof(MetaDataComponent)).NetID; if (!metaId.HasValue) throw new InvalidOperationException("MetaDataComponent does not have a NetId."); @@ -172,6 +177,23 @@ public void Initialize() _metaCompNetId = metaId.Value; } + private void OnComponentAdded(AddedComponentEventArgs args) + { + if (_resettingPredictedEntities) + { + var comp = args.ComponentType; + + if (comp.NetID == null) + return; + + _sawmill.Error($""" + Added component {comp.Name} with net id {comp.NetID} while resetting predicted entities. + Stack trace: + {Environment.StackTrace} + """); + } + } + /// public void Reset() { @@ -543,40 +565,50 @@ public void ResetPredictedEntities() countReset += 1; - foreach (var (netId, comp) in meta.NetComponents) + try { - if (!comp.NetSyncEnabled) - continue; + _resettingPredictedEntities = true; - // Was this component added during prediction? - if (comp.CreationTick > _timing.LastRealTick) + foreach (var (netId, comp) in meta.NetComponents) { - if (last.ContainsKey(netId)) + if (!comp.NetSyncEnabled) + continue; + + // Was this component added during prediction? + if (comp.CreationTick > _timing.LastRealTick) { - // Component was probably removed and then re-addedd during a single prediction run - // Just reset state as normal. - comp.ClearCreationTick(); + if (last.ContainsKey(netId)) + { + // Component was probably removed and then re-addedd during a single prediction run + // Just reset state as normal. + comp.ClearCreationTick(); + } + else + { + toRemove.Add(comp); + if (_sawmill.Level <= LogLevel.Debug) + _sawmill.Debug($" A new component was added: {comp.GetType()}"); + continue; + } } - else + + if (comp.LastModifiedTick <= _timing.LastRealTick || + !last.TryGetValue(netId, out var compState)) { - toRemove.Add(comp); - if (_sawmill.Level <= LogLevel.Debug) - _sawmill.Debug($" A new component was added: {comp.GetType()}"); continue; } - } - if (comp.LastModifiedTick <= _timing.LastRealTick || !last.TryGetValue(netId, out var compState)) - { - continue; - } - - if (_sawmill.Level <= LogLevel.Debug) - _sawmill.Debug($" A component was dirtied: {comp.GetType()}"); + if (_sawmill.Level <= LogLevel.Debug) + _sawmill.Debug($" A component was dirtied: {comp.GetType()}"); - var handleState = new ComponentHandleState(compState, null); - _entities.EventBus.RaiseComponentEvent(comp, ref handleState); - comp.LastModifiedTick = _timing.LastRealTick; + var handleState = new ComponentHandleState(compState, null); + _entities.EventBus.RaiseComponentEvent(comp, ref handleState); + comp.LastModifiedTick = _timing.LastRealTick; + } + } + finally + { + _resettingPredictedEntities = false; } if (toRemove.Count > 0) diff --git a/Robust.Client/GameStates/GameStateProcessor.cs b/Robust.Client/GameStates/GameStateProcessor.cs index cfe9b9210c0..e69e8b401b5 100644 --- a/Robust.Client/GameStates/GameStateProcessor.cs +++ b/Robust.Client/GameStates/GameStateProcessor.cs @@ -14,8 +14,6 @@ namespace Robust.Client.GameStates /// internal sealed class GameStateProcessor : IGameStateProcessor { - public const int MaxBufferSize = 512; - private readonly IClientGameTiming _timing; private readonly IClientGameStateManager _state; private readonly ISawmill _logger; @@ -28,6 +26,8 @@ internal sealed class GameStateProcessor : IGameStateProcessor public (GameTick Tick, DateTime Time)? LastFullStateRequested { get; private set; } = (GameTick.Zero, DateTime.MaxValue); private int _bufferSize; + private int _maxBufferSize = 512; + public const int MinimumMaxBufferSize = 256; /// /// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset. @@ -48,7 +48,14 @@ internal readonly Dictionary> _las public int BufferSize { get => _bufferSize; - set => _bufferSize = value < 0 ? 0 : value; + set => _bufferSize = Math.Max(value, 0); + } + + public int MaxBufferSize + { + get => _maxBufferSize; + // We place a lower bound on the maximum size to avoid spamming servers with full game state requests. + set => _maxBufferSize = Math.Max(value, MinimumMaxBufferSize); } /// @@ -100,21 +107,21 @@ public bool AddNewState(GameState state) return true; } - if (LastFullState == null && state.FromSequence == GameTick.Zero && state.ToSequence >= LastFullStateRequested!.Value.Tick) + if (LastFullState == null && state.FromSequence == GameTick.Zero) { - LastFullState = state; - - if (Logging) + if (state.ToSequence >= LastFullStateRequested!.Value.Tick) + { + LastFullState = state; _logger.Info($"Received Full GameState: to={state.ToSequence}, sz={state.PayloadSize}"); + return true; + } - return true; + _logger.Info($"Received a late full game state. Received: {state.ToSequence}. Requested: {LastFullStateRequested.Value.Tick}"); } if (LastFullState != null && state.ToSequence <= LastFullState.ToSequence) { - if (Logging) - _logger.Info($"While waiting for full, received late GameState with lower to={state.ToSequence} than the last full state={LastFullState.ToSequence}"); - + _logger.Info($"While waiting for full, received late GameState with lower to={state.ToSequence} than the last full state={LastFullState.ToSequence}"); return false; } diff --git a/Robust.Client/GameStates/NetEntityOverlay.cs b/Robust.Client/GameStates/NetEntityOverlay.cs index ff527e7ebaf..e63c3e33af4 100644 --- a/Robust.Client/GameStates/NetEntityOverlay.cs +++ b/Robust.Client/GameStates/NetEntityOverlay.cs @@ -40,7 +40,7 @@ sealed class NetEntityOverlay : Overlay public NetEntityOverlay() { IoCManager.InjectDependencies(this); - var cache = IoCManager.Resolve(); + var cache = IoCManager.Resolve(); _font = new VectorFont(cache.GetResource("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10); _lineHeight = _font.GetLineHeight(1); diff --git a/Robust.Client/GameStates/NetGraphOverlay.cs b/Robust.Client/GameStates/NetGraphOverlay.cs index 38bbce15217..26c559b9097 100644 --- a/Robust.Client/GameStates/NetGraphOverlay.cs +++ b/Robust.Client/GameStates/NetGraphOverlay.cs @@ -53,7 +53,7 @@ internal sealed class NetGraphOverlay : Overlay public NetGraphOverlay() { IoCManager.InjectDependencies(this); - var cache = IoCManager.Resolve(); + var cache = IoCManager.Resolve(); _font = new VectorFont(cache.GetResource("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10); _gameStateManager.GameStateApplied += HandleGameStateApplied; diff --git a/Robust.Client/Graphics/Audio/ClydeAudio.ALDisposeQueues.cs b/Robust.Client/Graphics/Audio/ClydeAudio.ALDisposeQueues.cs new file mode 100644 index 00000000000..10d0abc4f3b --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudio.ALDisposeQueues.cs @@ -0,0 +1,59 @@ +using System.Collections.Concurrent; +using OpenTK.Audio.OpenAL; + +namespace Robust.Client.Graphics.Audio +{ + internal partial class ClydeAudio + { + // Used to track audio sources that were disposed in the finalizer thread, + // so we need to properly send them off in the main thread. + private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _sourceDisposeQueue = new(); + private readonly ConcurrentQueue<(int sourceHandle, int filterHandle)> _bufferedSourceDisposeQueue = new(); + private readonly ConcurrentQueue _bufferDisposeQueue = new(); + + private void _flushALDisposeQueues() + { + // Clear out finalized audio sources. + while (_sourceDisposeQueue.TryDequeue(out var handles)) + { + OpenALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle); + if (IsEfxSupported) RemoveEfx(handles); + AL.DeleteSource(handles.sourceHandle); + _checkAlError(); + _audioSources.Remove(handles.sourceHandle); + } + + // Clear out finalized buffered audio sources. + while (_bufferedSourceDisposeQueue.TryDequeue(out var handles)) + { + OpenALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle); + if (IsEfxSupported) RemoveEfx(handles); + AL.DeleteSource(handles.sourceHandle); + _checkAlError(); + _bufferedAudioSources.Remove(handles.sourceHandle); + } + + // Clear out finalized audio buffers. + while (_bufferDisposeQueue.TryDequeue(out var handle)) + { + AL.DeleteBuffer(handle); + _checkAlError(); + } + } + + private void DeleteSourceOnMainThread(int sourceHandle, int filterHandle) + { + _sourceDisposeQueue.Enqueue((sourceHandle, filterHandle)); + } + + private void DeleteBufferedSourceOnMainThread(int bufferedSourceHandle, int filterHandle) + { + _bufferedSourceDisposeQueue.Enqueue((bufferedSourceHandle, filterHandle)); + } + + private void DeleteAudioBufferOnMainThread(int bufferHandle) + { + _bufferDisposeQueue.Enqueue(bufferHandle); + } + } +} diff --git a/Robust.Client/Graphics/Audio/ClydeAudio.AudioSources.cs b/Robust.Client/Graphics/Audio/ClydeAudio.AudioSources.cs new file mode 100644 index 00000000000..3ef560a82b2 --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudio.AudioSources.cs @@ -0,0 +1,680 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using OpenTK.Audio.OpenAL; +using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; +using OpenTK.Mathematics; +using Robust.Client.Audio; +using Robust.Shared; +using Robust.Shared.Configuration; +using Robust.Shared.IoC; +using Robust.Shared.Audio; +using Robust.Shared.Log; +using Robust.Shared.Maths; +using Vector2 = System.Numerics.Vector2; +using Robust.Shared.Utility; + +namespace Robust.Client.Graphics.Audio +{ + internal partial class ClydeAudio + { + private sealed class AudioSource : IClydeAudioSource + { + private int SourceHandle; + private readonly ClydeAudio _master; + private readonly AudioStream _sourceStream; + private int FilterHandle; +#if DEBUG + private bool _didPositionWarning; +#endif + + private float _gain; + + private bool IsEfxSupported => _master.IsEfxSupported; + + public AudioSource(ClydeAudio master, int sourceHandle, AudioStream sourceStream) + { + _master = master; + SourceHandle = sourceHandle; + _sourceStream = sourceStream; + AL.GetSource(SourceHandle, ALSourcef.Gain, out _gain); + } + + public void StartPlaying() + { + _checkDisposed(); + AL.SourcePlay(SourceHandle); + _master._checkAlError(); + } + + public void StopPlaying() + { + if (_isDisposed()) return; + AL.SourceStop(SourceHandle); + _master._checkAlError(); + } + + public bool IsPlaying + { + get + { + _checkDisposed(); + var state = AL.GetSourceState(SourceHandle); + return state == ALSourceState.Playing; + } + } + + public bool IsLooping + { + get + { + _checkDisposed(); + AL.GetSource(SourceHandle, ALSourceb.Looping, out var ret); + _master._checkAlError(); + return ret; + } + set + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourceb.Looping, value); + _master._checkAlError(); + } + } + + public bool IsGlobal + { + get + { + _checkDisposed(); + AL.GetSource(SourceHandle, ALSourceb.SourceRelative, out var value); + _master._checkAlError(); + return value; + } + } + + public void SetGlobal() + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourceb.SourceRelative, true); + _master._checkAlError(); + } + + public void SetVolume(float decibels) + { + _checkDisposed(); + var priorOcclusion = 1f; + if (!IsEfxSupported) + { + AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain); + priorOcclusion = priorGain / _gain; + } + _gain = MathF.Pow(10, decibels / 10); + AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion); + _master._checkAlError(); + } + + public void SetVolumeDirect(float gain) + { + _checkDisposed(); + var priorOcclusion = 1f; + if (!IsEfxSupported) + { + AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain); + priorOcclusion = priorGain / _gain; + } + _gain = gain; + AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion); + _master._checkAlError(); + } + + public void SetMaxDistance(float distance) + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourcef.MaxDistance, distance); + _master._checkAlError(); + } + + public void SetRolloffFactor(float rolloffFactor) + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourcef.RolloffFactor, rolloffFactor); + _master._checkAlError(); + } + + public void SetReferenceDistance(float refDistance) + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourcef.ReferenceDistance, refDistance); + _master._checkAlError(); + } + + public void SetOcclusion(float blocks) + { + _checkDisposed(); + var cutoff = MathF.Exp(-blocks * 1); + var gain = MathF.Pow(cutoff, 0.1f); + if (IsEfxSupported) + { + SetOcclusionEfx(gain, cutoff); + } + else + { + gain *= gain * gain; + AL.Source(SourceHandle, ALSourcef.Gain, _gain * gain); + } + _master._checkAlError(); + } + + private void SetOcclusionEfx(float gain, float cutoff) + { + if (FilterHandle == 0) + { + FilterHandle = EFX.GenFilter(); + EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass); + } + + EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain); + EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff); + AL.Source(SourceHandle, ALSourcei.EfxDirectFilter, FilterHandle); + } + + public void SetPlaybackPosition(float seconds) + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourcef.SecOffset, seconds); + _master._checkAlError(); + } + + public bool SetPosition(Vector2 position) + { + _checkDisposed(); + + var (x, y) = position; + + if (!AreFinite(x, y)) + { + return false; + } +#if DEBUG + // OpenAL doesn't seem to want to play stereo positionally. + // Log a warning if people try to. + if (_sourceStream.ChannelCount > 1 && !_didPositionWarning) + { + _didPositionWarning = true; + _master.OpenALSawmill.Warning("Attempting to set position on audio source with multiple audio channels! Stream: '{0}'. Make sure the audio is MONO, not stereo.", + _sourceStream.Name); + // warning isn't enough, people just ignore it :( + DebugTools.Assert(false, $"Attempting to set position on audio source with multiple audio channels! Stream: '{_sourceStream.Name}'. Make sure the audio is MONO, not stereo."); + } +#endif + + AL.Source(SourceHandle, ALSource3f.Position, x, y, 0); + _master._checkAlError(); + return true; + } + + private static bool AreFinite(float x, float y) + { + if (float.IsFinite(x) && float.IsFinite(y)) + { + return true; + } + + return false; + } + + public void SetVelocity(Vector2 velocity) + { + _checkDisposed(); + + var (x, y) = velocity; + + if (!AreFinite(x, y)) + { + return; + } + + AL.Source(SourceHandle, ALSource3f.Velocity, x, y, 0); + + _master._checkAlError(); + } + + public void SetPitch(float pitch) + { + _checkDisposed(); + AL.Source(SourceHandle, ALSourcef.Pitch, pitch); + _master._checkAlError(); + } + + ~AudioSource() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!disposing) + { + // We can't run this code inside the finalizer thread so tell Clyde to clear it up later. + _master.DeleteSourceOnMainThread(SourceHandle, FilterHandle); + } + else + { + if (FilterHandle != 0) EFX.DeleteFilter(FilterHandle); + AL.DeleteSource(SourceHandle); + _master._audioSources.Remove(SourceHandle); + _master._checkAlError(); + } + + SourceHandle = -1; + } + + private bool _isDisposed() + { + return SourceHandle == -1; + } + + private void _checkDisposed() + { + if (SourceHandle == -1) + { + throw new ObjectDisposedException(nameof(AudioSource)); + } + } + } + + private sealed class BufferedAudioSource : IClydeBufferedAudioSource + { + private int? SourceHandle = null; + private int[] BufferHandles; + private Dictionary BufferMap = new(); + private readonly ClydeAudio _master; + private bool _mono = true; + private bool _float = false; + private int FilterHandle; + + private float _gain; + + public int SampleRate { get; set; } = 44100; + + private bool IsEfxSupported => _master.IsEfxSupported; + + public BufferedAudioSource(ClydeAudio master, int sourceHandle, int[] bufferHandles, bool floatAudio = false) + { + _master = master; + SourceHandle = sourceHandle; + BufferHandles = bufferHandles; + for (int i = 0; i < BufferHandles.Length; i++) + { + var bufferHandle = BufferHandles[i]; + BufferMap[bufferHandle] = i; + } + _float = floatAudio; + AL.GetSource(sourceHandle, ALSourcef.Gain, out _gain); + } + + public void StartPlaying() + { + _checkDisposed(); + // ReSharper disable once PossibleInvalidOperationException + AL.SourcePlay(stackalloc int[] {SourceHandle!.Value}); + _master._checkAlError(); + } + + public void StopPlaying() + { + if (_isDisposed()) return; + // ReSharper disable once PossibleInvalidOperationException + AL.SourceStop(SourceHandle!.Value); + _master._checkAlError(); + } + + public bool IsPlaying + { + get + { + _checkDisposed(); + // ReSharper disable once PossibleInvalidOperationException + var state = AL.GetSourceState(SourceHandle!.Value); + return state == ALSourceState.Playing; + } + } + + public bool IsLooping + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public void SetGlobal() + { + _checkDisposed(); + _mono = false; + // ReSharper disable once PossibleInvalidOperationException + AL.Source(SourceHandle!.Value, ALSourceb.SourceRelative, true); + _master._checkAlError(); + } + + public void SetLooping() + { + // TODO?waaaaddDDDDD + } + + public void SetVolume(float decibels) + { + _checkDisposed(); + var priorOcclusion = 1f; + if (!IsEfxSupported) + { + AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain); + priorOcclusion = priorGain / _gain; + } + _gain = MathF.Pow(10, decibels / 10); + AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion); + _master._checkAlError(); + } + + public void SetVolumeDirect(float gain) + { + _checkDisposed(); + var priorOcclusion = 1f; + if (!IsEfxSupported) + { + AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain); + priorOcclusion = priorGain / _gain; + } + _gain = gain; + AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion); + _master._checkAlError(); + } + + public void SetMaxDistance(float distance) + { + _checkDisposed(); + AL.Source(SourceHandle!.Value, ALSourcef.MaxDistance, distance); + _master._checkAlError(); + } + + public void SetRolloffFactor(float rolloffFactor) + { + _checkDisposed(); + AL.Source(SourceHandle!.Value, ALSourcef.RolloffFactor, rolloffFactor); + _master._checkAlError(); + } + + public void SetReferenceDistance(float refDistance) + { + _checkDisposed(); + AL.Source(SourceHandle!.Value, ALSourcef.ReferenceDistance, refDistance); + _master._checkAlError(); + } + + public void SetOcclusion(float blocks) + { + _checkDisposed(); + var cutoff = MathF.Exp(-blocks * 1.5f); + var gain = MathF.Pow(cutoff, 0.1f); + if (IsEfxSupported) + { + SetOcclusionEfx(gain, cutoff); + } + else + { + gain *= gain * gain; + AL.Source(SourceHandle!.Value, ALSourcef.Gain, gain * _gain); + } + + _master._checkAlError(); + } + + private void SetOcclusionEfx(float gain, float cutoff) + { + if (FilterHandle == 0) + { + FilterHandle = EFX.GenFilter(); + EFX.Filter(FilterHandle, FilterInteger.FilterType, (int) FilterType.Lowpass); + } + EFX.Filter(FilterHandle, FilterFloat.LowpassGain, gain); + EFX.Filter(FilterHandle, FilterFloat.LowpassGainHF, cutoff); + AL.Source(SourceHandle!.Value, ALSourcei.EfxDirectFilter, FilterHandle); + } + + public void SetPlaybackPosition(float seconds) + { + _checkDisposed(); + // ReSharper disable once PossibleInvalidOperationException + AL.Source(SourceHandle!.Value, ALSourcef.SecOffset, seconds); + _master._checkAlError(); + } + + public bool IsGlobal + { + get + { + _checkDisposed(); + AL.GetSource(SourceHandle!.Value, ALSourceb.SourceRelative, out var value); + _master._checkAlError(); + return value; + } + } + + public bool SetPosition(Vector2 position) + { + _checkDisposed(); + + var (x, y) = position; + + if (!AreFinite(x, y)) + { + return false; + } + + _mono = true; + // ReSharper disable once PossibleInvalidOperationException + AL.Source(SourceHandle!.Value, ALSource3f.Position, x, y, 0); + _master._checkAlError(); + return true; + } + + private static bool AreFinite(float x, float y) + { + if (float.IsFinite(x) && float.IsFinite(y)) + { + return true; + } + + return false; + } + + public void SetVelocity(Vector2 velocity) + { + _checkDisposed(); + + var (x, y) = velocity; + + if (!AreFinite(x, y)) + { + return; + } + + AL.Source(SourceHandle!.Value, ALSource3f.Velocity, x, y, 0); + + _master._checkAlError(); + } + + public void SetPitch(float pitch) + { + _checkDisposed(); + // ReSharper disable once PossibleInvalidOperationException + AL.Source(SourceHandle!.Value, ALSourcef.Pitch, pitch); + _master._checkAlError(); + } + + ~BufferedAudioSource() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (SourceHandle == null) return; + + if (!_master.IsMainThread()) + { + // We can't run this code inside another thread so tell Clyde to clear it up later. + _master.DeleteBufferedSourceOnMainThread(SourceHandle.Value, FilterHandle); + for (var i = 0; i < BufferHandles.Length; i++) + _master.DeleteAudioBufferOnMainThread(BufferHandles[i]); + } + else + { + if (FilterHandle != 0) EFX.DeleteFilter(FilterHandle); + AL.DeleteSource(SourceHandle.Value); + AL.DeleteBuffers(BufferHandles); + _master._bufferedAudioSources.Remove(SourceHandle.Value); + _master._checkAlError(); + } + + SourceHandle = null; + } + + private bool _isDisposed() + { + return SourceHandle == null; + } + + private void _checkDisposed() + { + if (SourceHandle == null) + { + throw new ObjectDisposedException(nameof(AudioSource)); + } + } + + public int GetNumberOfBuffersProcessed() + { + _checkDisposed(); + // ReSharper disable once PossibleInvalidOperationException + AL.GetSource(SourceHandle!.Value, ALGetSourcei.BuffersProcessed, out var buffersProcessed); + return buffersProcessed; + } + + public unsafe void GetBuffersProcessed(Span handles) + { + _checkDisposed(); + var entries = Math.Min(Math.Min(handles.Length, BufferHandles.Length), GetNumberOfBuffersProcessed()); + fixed (int* ptr = handles) + // ReSharper disable once PossibleInvalidOperationException + AL.SourceUnqueueBuffers(SourceHandle!.Value, entries, ptr); + + for (var i = 0; i < entries; i++) + handles[i] = BufferMap[handles[i]]; + } + + public unsafe void WriteBuffer(int handle, ReadOnlySpan data) + { + _checkDisposed(); + + if(_float) + throw new InvalidOperationException("Can't write ushort numbers to buffers when buffer type is float!"); + + if (handle >= BufferHandles.Length) + throw new ArgumentOutOfRangeException(nameof(handle), + $"Got {handle}. Expected less than {BufferHandles.Length}"); + + fixed (ushort* ptr = data) + { + AL.BufferData(BufferHandles[handle], _mono ? ALFormat.Mono16 : ALFormat.Stereo16, (IntPtr) ptr, + _mono ? data.Length / 2 * sizeof(ushort) : data.Length * sizeof(ushort), SampleRate); + } + } + + public unsafe void WriteBuffer(int handle, ReadOnlySpan data) + { + _checkDisposed(); + + if(!_float) + throw new InvalidOperationException("Can't write float numbers to buffers when buffer type is ushort!"); + + if (handle >= BufferHandles.Length) + throw new ArgumentOutOfRangeException(nameof(handle), + $"Got {handle}. Expected less than {BufferHandles.Length}"); + + fixed (float* ptr = data) + { + AL.BufferData(BufferHandles[handle], _mono ? ALFormat.MonoFloat32Ext : ALFormat.StereoFloat32Ext, (IntPtr) ptr, + _mono ? data.Length / 2 * sizeof(float) : data.Length * sizeof(float), SampleRate); + } + } + + public unsafe void QueueBuffers(ReadOnlySpan handles) + { + _checkDisposed(); + + Span realHandles = stackalloc int[handles.Length]; + handles.CopyTo(realHandles); + + for (var i = 0; i < realHandles.Length; i++) + { + var handle = realHandles[i]; + if (handle >= BufferHandles.Length) + throw new ArgumentOutOfRangeException(nameof(handles), $"Invalid handle with index {i}!"); + realHandles[i] = BufferHandles[handle]; + } + + fixed (int* ptr = realHandles) + // ReSharper disable once PossibleInvalidOperationException + AL.SourceQueueBuffers(SourceHandle!.Value, handles.Length, ptr); + } + + public unsafe void EmptyBuffers() + { + _checkDisposed(); + var length = (SampleRate / BufferHandles.Length) * (_mono ? 1 : 2); + + Span handles = stackalloc int[BufferHandles.Length]; + + if (_float) + { + var empty = new float[length]; + var span = (Span) empty; + + for (var i = 0; i < BufferHandles.Length; i++) + { + WriteBuffer(BufferMap[BufferHandles[i]], span); + handles[i] = BufferMap[BufferHandles[i]]; + } + } + else + { + var empty = new ushort[length]; + var span = (Span) empty; + + for (var i = 0; i < BufferHandles.Length; i++) + { + WriteBuffer(BufferMap[BufferHandles[i]], span); + handles[i] = BufferMap[BufferHandles[i]]; + } + } + + QueueBuffers(handles); + } + } + } +} diff --git a/Robust.Client/Graphics/Audio/ClydeAudio.Formats.OggVorbis.cs b/Robust.Client/Graphics/Audio/ClydeAudio.Formats.OggVorbis.cs new file mode 100644 index 00000000000..2a84f7da46c --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudio.Formats.OggVorbis.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; + +namespace Robust.Client.Graphics.Audio +{ + internal partial class ClydeAudio + { + private OggVorbisData _readOggVorbis(Stream stream) + { + using (var vorbis = new NVorbis.VorbisReader(stream, false)) + { + var sampleRate = vorbis.SampleRate; + var channels = vorbis.Channels; + var totalSamples = vorbis.TotalSamples; + + var readSamples = 0; + var buffer = new float[totalSamples * channels]; + + while (readSamples < totalSamples) + { + var read = vorbis.ReadSamples(buffer, readSamples * channels, buffer.Length - readSamples); + if (read == 0) + { + break; + } + + readSamples += read; + } + + return new OggVorbisData(totalSamples, sampleRate, channels, buffer, vorbis.Tags.Title, vorbis.Tags.Artist); + } + } + + private readonly struct OggVorbisData + { + public readonly long TotalSamples; + public readonly long SampleRate; + public readonly long Channels; + public readonly ReadOnlyMemory Data; + public readonly string Title; + public readonly string Artist; + + public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory data, string title, string artist) + { + TotalSamples = totalSamples; + SampleRate = sampleRate; + Channels = channels; + Data = data; + Title = title; + Artist = artist; + } + } + } +} diff --git a/Robust.Client/Graphics/Audio/ClydeAudio.Formats.Wav.cs b/Robust.Client/Graphics/Audio/ClydeAudio.Formats.Wav.cs new file mode 100644 index 00000000000..7394716534e --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudio.Formats.Wav.cs @@ -0,0 +1,144 @@ +using System; +using System.IO; +using JetBrains.Annotations; +using Robust.Shared.Utility; + +namespace Robust.Client.Graphics.Audio +{ + internal partial class ClydeAudio + { + /// + /// Load up a WAVE file. + /// + private static WavData _readWav(Stream stream) + { + var reader = new BinaryReader(stream, EncodingHelpers.UTF8, true); + + void SkipChunk() + { + var length = reader.ReadUInt32(); + stream.Position += length; + } + + // Read outer most chunks. + Span fourCc = stackalloc byte[4]; + while (true) + { + _readFourCC(reader, fourCc); + + if (!fourCc.SequenceEqual("RIFF"u8)) + { + SkipChunk(); + continue; + } + + return _readRiffChunk(reader); + } + } + + private static void _skipChunk(BinaryReader reader) + { + var length = reader.ReadUInt32(); + reader.BaseStream.Position += length; + } + + private static void _readFourCC(BinaryReader reader, Span fourCc) + { + fourCc[0] = reader.ReadByte(); + fourCc[1] = reader.ReadByte(); + fourCc[2] = reader.ReadByte(); + fourCc[3] = reader.ReadByte(); + } + + private static WavData _readRiffChunk(BinaryReader reader) + { + Span format = stackalloc byte[4]; + reader.ReadUInt32(); + _readFourCC(reader, format); + if (!format.SequenceEqual("WAVE"u8)) + { + throw new InvalidDataException("File is not a WAVE file."); + } + + _readFourCC(reader, format); + if (!format.SequenceEqual("fmt "u8)) + { + throw new InvalidDataException("Expected fmt chunk."); + } + + // Read fmt chunk. + + var size = reader.ReadInt32(); + var afterFmtPos = reader.BaseStream.Position + size; + + var audioType = (WavAudioFormatType) reader.ReadInt16(); + var channels = reader.ReadInt16(); + var sampleRate = reader.ReadInt32(); + var byteRate = reader.ReadInt32(); + var blockAlign = reader.ReadInt16(); + var bitsPerSample = reader.ReadInt16(); + + if (audioType != WavAudioFormatType.PCM) + { + throw new NotImplementedException("Unable to support audio types other than PCM."); + } + + DebugTools.Assert(byteRate == sampleRate * channels * bitsPerSample / 8); + + // Fmt is not of guaranteed size, so use the size header to skip to the end. + reader.BaseStream.Position = afterFmtPos; + + while (true) + { + _readFourCC(reader, format); + if (!format.SequenceEqual("data"u8)) + { + _skipChunk(reader); + continue; + } + + break; + } + + // We are in the data chunk. + size = reader.ReadInt32(); + var data = reader.ReadBytes(size); + + return new WavData(audioType, channels, sampleRate, byteRate, blockAlign, bitsPerSample, data); + } + + /// + /// See http://soundfile.sapp.org/doc/WaveFormat/ for reference. + /// + [PublicAPI] + private readonly struct WavData + { + public readonly WavAudioFormatType AudioType; + public readonly short NumChannels; + public readonly int SampleRate; + public readonly int ByteRate; + public readonly short BlockAlign; + public readonly short BitsPerSample; + public readonly ReadOnlyMemory Data; + + public WavData(WavAudioFormatType audioType, short numChannels, int sampleRate, int byteRate, + short blockAlign, short bitsPerSample, ReadOnlyMemory data) + { + AudioType = audioType; + NumChannels = numChannels; + SampleRate = sampleRate; + ByteRate = byteRate; + BlockAlign = blockAlign; + BitsPerSample = bitsPerSample; + Data = data; + } + } + + private enum WavAudioFormatType : short + { + Unknown = 0, + PCM = 1, + // There's a bunch of other types, those are all unsupported. + } + } +} diff --git a/Robust.Client/Graphics/Audio/ClydeAudio.IoC.cs b/Robust.Client/Graphics/Audio/ClydeAudio.IoC.cs new file mode 100644 index 00000000000..69fb7041afd --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudio.IoC.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using OpenTK.Audio.OpenAL; +using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; +using OpenTK.Mathematics; +using Robust.Client.Audio; +using Robust.Shared; +using Robust.Shared.Configuration; +using Robust.Shared.IoC; +using Robust.Shared.Audio; +using Robust.Shared.Log; +using Robust.Shared.Timing; +using Vector2 = System.Numerics.Vector2; + +namespace Robust.Client.Graphics.Audio +{ + internal partial class ClydeAudio + { + [Robust.Shared.IoC.Dependency] private readonly IConfigurationManager _cfg = default!; + [Robust.Shared.IoC.Dependency] private readonly IEyeManager _eyeManager = default!; + [Robust.Shared.IoC.Dependency] private readonly ILogManager _logMan = default!; + + private Thread? _gameThread; + + public bool InitializePostWindowing() + { + _gameThread = Thread.CurrentThread; + return _initializeAudio(); + } + + public void FrameProcess(FrameEventArgs eventArgs) + { + _updateAudio(); + } + + public void Shutdown() + { + _shutdownAudio(); + } + + private bool IsMainThread() + { + return Thread.CurrentThread == _gameThread; + } + } +} diff --git a/Robust.Client/Graphics/Audio/ClydeAudio.cs b/Robust.Client/Graphics/Audio/ClydeAudio.cs new file mode 100644 index 00000000000..cdbbca2d443 --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudio.cs @@ -0,0 +1,432 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using OpenTK.Audio.OpenAL; +using OpenTK.Audio.OpenAL.Extensions.Creative.EFX; +using OpenTK.Mathematics; +using Robust.Client.Audio; +using Robust.Shared; +using Robust.Shared.Audio; +using Robust.Shared.Log; + +namespace Robust.Client.Graphics.Audio +{ + internal sealed partial class ClydeAudio : IClydeAudio, IClydeAudioInternal + { + private ALDevice _openALDevice; + private ALContext _openALContext; + + private readonly List _audioSampleBuffers = new(); + + private readonly Dictionary> _audioSources = + new(); + + private readonly Dictionary> _bufferedAudioSources = + new(); + + private readonly HashSet _alcDeviceExtensions = new(); + private readonly HashSet _alContextExtensions = new(); + + // The base gain value for a listener, used to boost the default volume. + private const float _baseGain = 2f; + + public bool HasAlDeviceExtension(string extension) => _alcDeviceExtensions.Contains(extension); + public bool HasAlContextExtension(string extension) => _alContextExtensions.Contains(extension); + + internal bool IsEfxSupported; + + internal ISawmill OpenALSawmill = default!; + + private bool _initializeAudio() + { + OpenALSawmill = _logMan.GetSawmill("clyde.oal"); + + if (!_audioOpenDevice()) + return false; + + // Create OpenAL context. + _audioCreateContext(); + + IsEfxSupported = HasAlDeviceExtension("ALC_EXT_EFX"); + + _cfg.OnValueChanged(CVars.AudioMasterVolume, SetMasterVolume, true); + _cfg.OnValueChanged(CVars.AudioAttenuation, SetAudioAttenuation, true); + return true; + } + + private void _audioCreateContext() + { + unsafe + { + _openALContext = ALC.CreateContext(_openALDevice, (int*) 0); + } + + ALC.MakeContextCurrent(_openALContext); + _checkAlcError(_openALDevice); + _checkAlError(); + + // Load up AL context extensions. + var s = ALC.GetString(ALDevice.Null, AlcGetString.Extensions) ?? ""; + foreach (var extension in s.Split(' ')) + { + _alContextExtensions.Add(extension); + } + + OpenALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor)); + OpenALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer)); + OpenALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version)); + } + + private bool _audioOpenDevice() + { + var preferredDevice = _cfg.GetCVar(CVars.AudioDevice); + + // Open device. + if (!string.IsNullOrEmpty(preferredDevice)) + { + _openALDevice = ALC.OpenDevice(preferredDevice); + if (_openALDevice == IntPtr.Zero) + { + OpenALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.", + preferredDevice, ALC.GetError(ALDevice.Null)); + + _openALDevice = ALC.OpenDevice(null); + } + } + else + { + _openALDevice = ALC.OpenDevice(null); + } + + _checkAlcError(_openALDevice); + + if (_openALDevice == IntPtr.Zero) + { + OpenALSawmill.Error("Unable to open OpenAL device! {1}", ALC.GetError(ALDevice.Null)); + return false; + } + + // Load up ALC extensions. + var s = ALC.GetString(_openALDevice, AlcGetString.Extensions) ?? ""; + foreach (var extension in s.Split(' ')) + { + _alcDeviceExtensions.Add(extension); + } + return true; + } + + public void StopAllAudio() + { + foreach (var (key, source) in _audioSources) + { + if (source.TryGetTarget(out var target)) + { + target.StopPlaying(); + } + } + + foreach (var (key, source) in _bufferedAudioSources) + { + if (source.TryGetTarget(out var target)) + { + target.StopPlaying(); + } + } + } + + public void DisposeAllAudio() + { + foreach (var (key, source) in _audioSources) + { + if (source.TryGetTarget(out var target)) + { + target.Dispose(); + } + } + _audioSources.Clear(); + + foreach (var (key, source) in _bufferedAudioSources) + { + if (source.TryGetTarget(out var target)) + { + target.StopPlaying(); + target.Dispose(); + } + } + _bufferedAudioSources.Clear(); + } + + private void _shutdownAudio() + { + DisposeAllAudio(); + + if (_openALContext != ALContext.Null) + { + ALC.MakeContextCurrent(ALContext.Null); + + ALC.DestroyContext(_openALContext); + } + + if (_openALDevice != IntPtr.Zero) + { + ALC.CloseDevice(_openALDevice); + } + } + + private void _updateAudio() + { + var eye = _eyeManager.CurrentEye; + var vec = eye.Position.Position; + AL.Listener(ALListener3f.Position, vec.X, vec.Y, -5); + var rot2d = eye.Rotation.ToVec(); + AL.Listener(ALListenerfv.Orientation, new []{0, 0, -1, rot2d.X, rot2d.Y, 0}); + + // Default orientation: at: (0, 0, -1) up: (0, 1, 0) + var rot = eye.Rotation.ToVec(); + var at = new Vector3(0f, 0f, -1f); + var up = new Vector3(rot.Y, rot.X, 0f); + AL.Listener(ALListenerfv.Orientation, ref at, ref up); + + _flushALDisposeQueues(); + } + + private static void RemoveEfx((int sourceHandle, int filterHandle) handles) + { + if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle); + } + + public void SetMasterVolume(float newVolume) + { + AL.Listener(ALListenerf.Gain, _baseGain * newVolume); + } + + public void SetAudioAttenuation(int value) + { + var attenuation = (Attenuation) value; + + switch (attenuation) + { + case Attenuation.NoAttenuation: + AL.DistanceModel(ALDistanceModel.None); + break; + case Attenuation.InverseDistance: + AL.DistanceModel(ALDistanceModel.InverseDistance); + break; + case Attenuation.Default: + case Attenuation.InverseDistanceClamped: + AL.DistanceModel(ALDistanceModel.InverseDistanceClamped); + break; + case Attenuation.LinearDistance: + AL.DistanceModel(ALDistanceModel.LinearDistance); + break; + case Attenuation.LinearDistanceClamped: + AL.DistanceModel(ALDistanceModel.LinearDistanceClamped); + break; + case Attenuation.ExponentDistance: + AL.DistanceModel(ALDistanceModel.ExponentDistance); + break; + case Attenuation.ExponentDistanceClamped: + AL.DistanceModel(ALDistanceModel.ExponentDistanceClamped); + break; + default: + throw new ArgumentOutOfRangeException($"No implementation to set {attenuation.ToString()} for DistanceModel!"); + } + + var attToString = attenuation == Attenuation.Default ? Attenuation.InverseDistanceClamped : attenuation; + + OpenALSawmill.Info($"Set audio attenuation to {attToString.ToString()}"); + } + + public IClydeAudioSource? CreateAudioSource(AudioStream stream) + { + var source = AL.GenSource(); + + if (!AL.IsSource(source)) + { + OpenALSawmill.Error("Failed to generate source. Too many simultaneous audio streams? {0}", Environment.StackTrace); + return null; + } + + // ReSharper disable once PossibleInvalidOperationException + // TODO: This really shouldn't be indexing based on the ClydeHandle... + AL.Source(source, ALSourcei.Buffer, _audioSampleBuffers[(int) stream.ClydeHandle!.Value.Value].BufferHandle); + + var audioSource = new AudioSource(this, source, stream); + _audioSources.Add(source, new WeakReference(audioSource)); + return audioSource; + } + + public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false) + { + var source = AL.GenSource(); + + if (!AL.IsSource(source)) + throw new Exception("Failed to generate source. Too many simultaneous audio streams?"); + + // ReSharper disable once PossibleInvalidOperationException + + var audioSource = new BufferedAudioSource(this, source, AL.GenBuffers(buffers), floatAudio); + _bufferedAudioSources.Add(source, new WeakReference(audioSource)); + return audioSource; + } + + private void _checkAlcError(ALDevice device, + [CallerMemberName] string callerMember = "", + [CallerLineNumber] int callerLineNumber = -1) + { + var error = ALC.GetError(device); + if (error != AlcError.NoError) + { + OpenALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error); + } + } + + private void _checkAlError([CallerMemberName] string callerMember = "", + [CallerLineNumber] int callerLineNumber = -1) + { + var error = AL.GetError(); + if (error != ALError.NoError) + { + OpenALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error); + } + } + + public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null) + { + var vorbis = _readOggVorbis(stream); + + var buffer = AL.GenBuffer(); + + ALFormat format; + // NVorbis only supports loading into floats. + // If this becomes a problem due to missing extension support (doubt it but ok), + // check the git history, I originally used libvorbisfile which worked and loaded 16 bit LPCM. + if (vorbis.Channels == 1) + { + format = ALFormat.MonoFloat32Ext; + } + else if (vorbis.Channels == 2) + { + format = ALFormat.StereoFloat32Ext; + } + else + { + throw new InvalidOperationException("Unable to load audio with more than 2 channels."); + } + + unsafe + { + fixed (float* ptr = vorbis.Data.Span) + { + AL.BufferData(buffer, format, (IntPtr) ptr, vorbis.Data.Length * sizeof(float), + (int) vorbis.SampleRate); + } + } + + _checkAlError(); + + var handle = new ClydeHandle(_audioSampleBuffers.Count); + _audioSampleBuffers.Add(new LoadedAudioSample(buffer)); + var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate); + return new AudioStream(handle, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist); + } + + public AudioStream LoadAudioWav(Stream stream, string? name = null) + { + var wav = _readWav(stream); + + var buffer = AL.GenBuffer(); + + ALFormat format; + if (wav.BitsPerSample == 16) + { + if (wav.NumChannels == 1) + { + format = ALFormat.Mono16; + } + else if (wav.NumChannels == 2) + { + format = ALFormat.Stereo16; + } + else + { + throw new InvalidOperationException("Unable to load audio with more than 2 channels."); + } + } + else if (wav.BitsPerSample == 8) + { + if (wav.NumChannels == 1) + { + format = ALFormat.Mono8; + } + else if (wav.NumChannels == 2) + { + format = ALFormat.Stereo8; + } + else + { + throw new InvalidOperationException("Unable to load audio with more than 2 channels."); + } + } + else + { + throw new InvalidOperationException("Unable to load wav with bits per sample different from 8 or 16"); + } + + unsafe + { + fixed (byte* ptr = wav.Data.Span) + { + AL.BufferData(buffer, format, (IntPtr) ptr, wav.Data.Length, wav.SampleRate); + } + } + + _checkAlError(); + + var handle = new ClydeHandle(_audioSampleBuffers.Count); + _audioSampleBuffers.Add(new LoadedAudioSample(buffer)); + var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate); + return new AudioStream(handle, length, wav.NumChannels, name); + } + + public AudioStream LoadAudioRaw(ReadOnlySpan samples, int channels, int sampleRate, string? name = null) + { + var fmt = channels switch + { + 1 => ALFormat.Mono16, + 2 => ALFormat.Stereo16, + _ => throw new ArgumentOutOfRangeException( + nameof(channels), "Only stereo and mono is currently supported") + }; + + var buffer = AL.GenBuffer(); + _checkAlError(); + + unsafe + { + fixed (short* ptr = samples) + { + AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate); + } + } + + _checkAlError(); + + var handle = new ClydeHandle(_audioSampleBuffers.Count); + var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate); + _audioSampleBuffers.Add(new LoadedAudioSample(buffer)); + return new AudioStream(handle, length, channels, name); + } + + private sealed class LoadedAudioSample + { + public readonly int BufferHandle; + + public LoadedAudioSample(int bufferHandle) + { + BufferHandle = bufferHandle; + } + } + } +} diff --git a/Robust.Client/Graphics/Audio/ClydeAudioHeadless.cs b/Robust.Client/Graphics/Audio/ClydeAudioHeadless.cs new file mode 100644 index 00000000000..c1a2d29522c --- /dev/null +++ b/Robust.Client/Graphics/Audio/ClydeAudioHeadless.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Robust.Client.Audio; +using Robust.Client.Input; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using Color = Robust.Shared.Maths.Color; + +namespace Robust.Client.Graphics.Audio +{ + /// + /// Hey look, it's ClydeAudio's evil twin brother! + /// + [UsedImplicitly] + internal sealed class ClydeAudioHeadless : IClydeAudio, IClydeAudioInternal + { + public bool InitializePostWindowing() + { + return true; + } + + public void FrameProcess(FrameEventArgs eventArgs) + { + } + + public void Shutdown() + { + } + + public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null) + { + // TODO: Might wanna actually load this so the length gets reported correctly. + return new(default, default, 1, name); + } + + public AudioStream LoadAudioWav(Stream stream, string? name = null) + { + // TODO: Might wanna actually load this so the length gets reported correctly. + return new(default, default, 1, name); + } + + public AudioStream LoadAudioRaw(ReadOnlySpan samples, int channels, int sampleRate, string? name = null) + { + // TODO: Might wanna actually load this so the length gets reported correctly. + return new(default, default, channels, name); + } + + public IClydeAudioSource CreateAudioSource(AudioStream stream) + { + return DummyAudioSource.Instance; + } + + public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false) + { + return DummyBufferedAudioSource.Instance; + } + + public void SetMasterVolume(float newVolume) + { + // Nada. + } + + public void DisposeAllAudio() + { + // Nada. + } + + public void StopAllAudio() + { + // Nada. + } + } +} diff --git a/Robust.Client/Graphics/Audio/DummyAudioSource.cs b/Robust.Client/Graphics/Audio/DummyAudioSource.cs new file mode 100644 index 00000000000..aa706f21149 --- /dev/null +++ b/Robust.Client/Graphics/Audio/DummyAudioSource.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Robust.Client.Audio; +using Robust.Client.Input; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using Color = Robust.Shared.Maths.Color; + +namespace Robust.Client.Graphics.Audio +{ + /// + /// Hey look, it's ClydeAudio.AudioSource's evil twin brother! + /// + [Virtual] + internal class DummyAudioSource : IClydeAudioSource + { + public static DummyAudioSource Instance { get; } = new(); + + public bool IsPlaying => default; + public bool IsLooping { get; set; } + + public void Dispose() + { + // Nada. + } + + public void StartPlaying() + { + // Nada. + } + + public void StopPlaying() + { + // Nada. + } + + public bool IsGlobal { get; } + + public bool SetPosition(Vector2 position) + { + return true; + } + + public void SetPitch(float pitch) + { + // Nada. + } + + public void SetGlobal() + { + // Nada. + } + + public void SetVolume(float decibels) + { + // Nada. + } + + public void SetVolumeDirect(float gain) + { + // Nada. + } + + public void SetMaxDistance(float maxDistance) + { + // Nada. + } + + public void SetRolloffFactor(float rolloffFactor) + { + // Nada. + } + + public void SetReferenceDistance(float refDistance) + { + // Nada. + } + + public void SetOcclusion(float blocks) + { + // Nada. + } + + public void SetPlaybackPosition(float seconds) + { + // Nada. + } + + public void SetVelocity(Vector2 velocity) + { + // Nada. + } + } +} diff --git a/Robust.Shared/Audio/Sources/DummyBufferedAudioSource.cs b/Robust.Client/Graphics/Audio/DummyBufferedAudioSource.cs similarity index 60% rename from Robust.Shared/Audio/Sources/DummyBufferedAudioSource.cs rename to Robust.Client/Graphics/Audio/DummyBufferedAudioSource.cs index 122a261a83f..640168b0ba5 100644 --- a/Robust.Shared/Audio/Sources/DummyBufferedAudioSource.cs +++ b/Robust.Client/Graphics/Audio/DummyBufferedAudioSource.cs @@ -1,11 +1,24 @@ using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Robust.Client.Audio; +using Robust.Client.Input; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using Color = Robust.Shared.Maths.Color; -namespace Robust.Shared.Audio.Sources +namespace Robust.Client.Graphics.Audio { /// - /// Hey look, it's Audio.BufferedAudioSource's evil twin brother! + /// Hey look, it's ClydeAudio.BufferedAudioSource's evil twin brother! /// - internal sealed class DummyBufferedAudioSource : DummyAudioSource, IBufferedAudioSource + internal sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource { public new static DummyBufferedAudioSource Instance { get; } = new(); public int SampleRate { get; set; } = 0; diff --git a/Robust.Client/Graphics/Audio/FallbackProxyClydeAudio.cs b/Robust.Client/Graphics/Audio/FallbackProxyClydeAudio.cs new file mode 100644 index 00000000000..98673451b5d --- /dev/null +++ b/Robust.Client/Graphics/Audio/FallbackProxyClydeAudio.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Robust.Client.Audio; +using Robust.Client.Input; +using Robust.Shared.Timing; +using Robust.Shared.IoC; + +namespace Robust.Client.Graphics.Audio +{ + /// + /// For "start ss14 with no audio devices" Smugleaf + /// + [UsedImplicitly] + internal sealed class FallbackProxyClydeAudio : ProxyClydeAudio + { + [Dependency] private readonly IDependencyCollection _deps = default!; + + public override bool InitializePostWindowing() + { + // Deliberate lack of base call here (see base implementation for comments as to why there even is a base) + + ActualImplementation = new ClydeAudio(); + _deps.InjectDependencies(ActualImplementation, true); + if (ActualImplementation.InitializePostWindowing()) + return true; + + // If we get here, that failed, so use the fallback + ActualImplementation = new ClydeAudioHeadless(); + _deps.InjectDependencies(ActualImplementation, true); + return ActualImplementation.InitializePostWindowing(); + } + } +} diff --git a/Robust.Client/Graphics/Audio/ProxyClydeAudio.cs b/Robust.Client/Graphics/Audio/ProxyClydeAudio.cs new file mode 100644 index 00000000000..79b5eeaf7fc --- /dev/null +++ b/Robust.Client/Graphics/Audio/ProxyClydeAudio.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using JetBrains.Annotations; +using Robust.Client.Audio; +using Robust.Client.Input; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.PixelFormats; +using Color = Robust.Shared.Maths.Color; + +namespace Robust.Client.Graphics.Audio +{ + /// + /// For "start ss14 with no audio devices" Smugleaf + /// + [UsedImplicitly] + internal abstract class ProxyClydeAudio : IClydeAudio, IClydeAudioInternal + { + protected IClydeAudioInternal ActualImplementation = default!; + + public virtual bool InitializePostWindowing() + { + // This particular implementation exists to be overridden because removing this method causes C# to complain + return ActualImplementation.InitializePostWindowing(); + } + + public void FrameProcess(FrameEventArgs eventArgs) + { + ActualImplementation.FrameProcess(eventArgs); + } + + public void Shutdown() + { + ActualImplementation.Shutdown(); + } + + public AudioStream LoadAudioOggVorbis(Stream stream, string? name = null) + { + return ActualImplementation.LoadAudioOggVorbis(stream, name); + } + + public AudioStream LoadAudioWav(Stream stream, string? name = null) + { + return ActualImplementation.LoadAudioWav(stream, name); + } + + public AudioStream LoadAudioRaw(ReadOnlySpan samples, int channels, int sampleRate, string? name = null) + { + return ActualImplementation.LoadAudioRaw(samples, channels, sampleRate, name); + } + + public IClydeAudioSource? CreateAudioSource(AudioStream stream) + { + return ActualImplementation.CreateAudioSource(stream); + } + + public IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false) + { + return ActualImplementation.CreateBufferedAudioSource(buffers, floatAudio); + } + + public void SetMasterVolume(float newVolume) + { + ActualImplementation.SetMasterVolume(newVolume); + } + + public void DisposeAllAudio() + { + ActualImplementation.DisposeAllAudio(); + } + + public void StopAllAudio() + { + ActualImplementation.StopAllAudio(); + } + } +} diff --git a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs index 36b7a015418..1da177f6af7 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.HLR.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.HLR.cs @@ -512,7 +512,7 @@ private void RenderViewport(Viewport viewport) RenderOverlays(viewport, OverlaySpace.WorldSpaceBelowFOV, worldAABB, worldBounds); } - if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawFov) + if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawLight && eye.DrawFov) { ApplyFovToBuffer(viewport, eye); } diff --git a/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs b/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs index f1cfdfdce19..1aeb2872b09 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.LightRendering.cs @@ -332,7 +332,7 @@ private void FinalizeDepthDraw() private void DrawLightsAndFov(Viewport viewport, Box2Rotated worldBounds, Box2 worldAABB, IEye eye) { - if (!_lightManager.Enabled) + if (!_lightManager.Enabled || !eye.DrawLight) { return; } diff --git a/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs b/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs index ebda3975e1f..98ca8b9d946 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.Windowing.cs @@ -260,14 +260,14 @@ private IEnumerable> LoadWindowIcons() yield break; } - foreach (var file in _resManager.ContentFindFiles(_windowIconPath)) + foreach (var file in _resourceCache.ContentFindFiles(_windowIconPath)) { if (file.Extension != "png") { continue; } - using var stream = _resManager.ContentFileRead(file); + using var stream = _resourceCache.ContentFileRead(file); yield return Image.Load(stream); } } diff --git a/Robust.Client/Graphics/Clyde/Clyde.cs b/Robust.Client/Graphics/Clyde/Clyde.cs index 088faaf05c0..b85f33b3c54 100644 --- a/Robust.Client/Graphics/Clyde/Clyde.cs +++ b/Robust.Client/Graphics/Clyde/Clyde.cs @@ -11,7 +11,6 @@ using Robust.Client.UserInterface; using Robust.Shared; using Robust.Shared.Configuration; -using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; using Robust.Shared.Graphics; using Robust.Shared.IoC; @@ -36,8 +35,7 @@ internal sealed partial class Clyde : IClydeInternal, IPostInjectInit, IEntityEv [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IOverlayManager _overlayManager = default!; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; - [Dependency] private readonly IResourceManager _resManager = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IUserInterfaceManagerInternal _userInterfaceManager = default!; [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; diff --git a/Robust.Client/Graphics/Clyde/ClydeHeadless.cs b/Robust.Client/Graphics/Clyde/ClydeHeadless.cs index 972d2c9d1a0..a75ff8de7a8 100644 --- a/Robust.Client/Graphics/Clyde/ClydeHeadless.cs +++ b/Robust.Client/Graphics/Clyde/ClydeHeadless.cs @@ -292,6 +292,123 @@ public void Dispose() } } + [Virtual] + private class DummyAudioSource : IClydeAudioSource + { + public static DummyAudioSource Instance { get; } = new(); + + public bool IsPlaying => default; + public bool IsLooping { get; set; } + + public void Dispose() + { + // Nada. + } + + public void StartPlaying() + { + // Nada. + } + + public void StopPlaying() + { + // Nada. + } + + public bool IsGlobal { get; } + + public bool SetPosition(Vector2 position) + { + return true; + } + + public void SetPitch(float pitch) + { + // Nada. + } + + public void SetGlobal() + { + // Nada. + } + + public void SetVolume(float decibels) + { + // Nada. + } + + public void SetVolumeDirect(float gain) + { + // Nada. + } + + public void SetMaxDistance(float maxDistance) + { + // Nada. + } + + public void SetRolloffFactor(float rolloffFactor) + { + // Nada. + } + + public void SetReferenceDistance(float refDistance) + { + // Nada. + } + + public void SetOcclusion(float blocks) + { + // Nada. + } + + public void SetPlaybackPosition(float seconds) + { + // Nada. + } + + public void SetVelocity(Vector2 velocity) + { + // Nada. + } + } + + private sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource + { + public new static DummyBufferedAudioSource Instance { get; } = new(); + public int SampleRate { get; set; } = 0; + + public void WriteBuffer(int handle, ReadOnlySpan data) + { + // Nada. + } + + public void WriteBuffer(int handle, ReadOnlySpan data) + { + // Nada. + } + + public void QueueBuffers(ReadOnlySpan handles) + { + // Nada. + } + + public void EmptyBuffers() + { + // Nada. + } + + public void GetBuffersProcessed(Span handles) + { + // Nada. + } + + public int GetNumberOfBuffersProcessed() + { + return 0; + } + } + private sealed class DummyTexture : OwnedTexture { public DummyTexture(Vector2i size) : base(size) diff --git a/Robust.Client/Graphics/ClydeHandle.cs b/Robust.Client/Graphics/ClydeHandle.cs index 5dadc3f6247..3e7319e8220 100644 --- a/Robust.Client/Graphics/ClydeHandle.cs +++ b/Robust.Client/Graphics/ClydeHandle.cs @@ -1,54 +1,54 @@ using System; -using Robust.Shared.Graphics; -namespace Robust.Client.Graphics; - -internal readonly struct ClydeHandle : IEquatable, IClydeHandle +namespace Robust.Client.Graphics { - public ClydeHandle(long value) - { - Value = value; - } - - public long Value { get; } - - public static explicit operator ClydeHandle(long x) - { - return new(x); - } - - public static explicit operator long(ClydeHandle h) - { - return h.Value; - } - - public bool Equals(ClydeHandle other) - { - return Value == other.Value; - } - - public override bool Equals(object? obj) - { - return obj is ClydeHandle other && Equals(other); - } - - public override int GetHashCode() - { - return Value.GetHashCode(); - } - - public static bool operator ==(ClydeHandle left, ClydeHandle right) - { - return left.Value == right.Value; - } - - public static bool operator !=(ClydeHandle left, ClydeHandle right) - { - return left.Value != right.Value; - } - - public override string ToString() - { - return $"ClydeHandle {Value}"; + internal struct ClydeHandle : IEquatable + { + public ClydeHandle(long value) + { + Value = value; + } + + public readonly long Value; + + public static explicit operator ClydeHandle(long x) + { + return new(x); + } + + public static explicit operator long(ClydeHandle h) + { + return h.Value; + } + + public bool Equals(ClydeHandle other) + { + return Value == other.Value; + } + + public override bool Equals(object? obj) + { + return obj is ClydeHandle other && Equals(other); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + + public static bool operator ==(ClydeHandle left, ClydeHandle right) + { + return left.Value == right.Value; + } + + public static bool operator !=(ClydeHandle left, ClydeHandle right) + { + return left.Value != right.Value; + } + + public override string ToString() + { + return $"ClydeHandle {Value}"; + } } } diff --git a/Robust.Client/Graphics/Drawing/DrawingHandleScreen.cs b/Robust.Client/Graphics/Drawing/DrawingHandleScreen.cs index e6192d7af44..5decd158515 100644 --- a/Robust.Client/Graphics/Drawing/DrawingHandleScreen.cs +++ b/Robust.Client/Graphics/Drawing/DrawingHandleScreen.cs @@ -114,10 +114,9 @@ public Vector2 GetDimensions(Font font, ReadOnlySpan str, float scale) { if (rune == new Rune('\n')) { + baseLine.X = 0f; baseLine.Y += lineHeight; advanceTotal.Y += lineHeight; - advanceTotal.X = Math.Max(advanceTotal.X, baseLine.X); - baseLine.X = 0f; continue; } @@ -127,6 +126,7 @@ public Vector2 GetDimensions(Font font, ReadOnlySpan str, float scale) continue; var advance = metrics.Value.Advance; + advanceTotal.X += advance; baseLine += new Vector2(advance, 0); } diff --git a/Robust.Client/Graphics/IClydeAudio.cs b/Robust.Client/Graphics/IClydeAudio.cs new file mode 100644 index 00000000000..277c6ac182b --- /dev/null +++ b/Robust.Client/Graphics/IClydeAudio.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using Robust.Client.Audio; + +namespace Robust.Client.Graphics +{ + public interface IClydeAudio + { + // AUDIO SYSTEM DOWN BELOW. + AudioStream LoadAudioOggVorbis(Stream stream, string? name = null); + AudioStream LoadAudioWav(Stream stream, string? name = null); + AudioStream LoadAudioRaw(ReadOnlySpan samples, int channels, int sampleRate, string? name = null); + + void SetMasterVolume(float newVolume); + + void DisposeAllAudio(); + + void StopAllAudio(); + + IClydeAudioSource? CreateAudioSource(AudioStream stream); + IClydeBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false); + } +} diff --git a/Robust.Client/Graphics/IClydeAudioInternal.cs b/Robust.Client/Graphics/IClydeAudioInternal.cs new file mode 100644 index 00000000000..df19590a8ac --- /dev/null +++ b/Robust.Client/Graphics/IClydeAudioInternal.cs @@ -0,0 +1,16 @@ +using System; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Timing; + +namespace Robust.Client.Graphics +{ + internal interface IClydeAudioInternal : IClydeAudio + { + bool InitializePostWindowing(); + void FrameProcess(FrameEventArgs eventArgs); + void Shutdown(); + } +} diff --git a/Robust.Client/Graphics/IClydeAudioSource.cs b/Robust.Client/Graphics/IClydeAudioSource.cs new file mode 100644 index 00000000000..c03b3dd33ca --- /dev/null +++ b/Robust.Client/Graphics/IClydeAudioSource.cs @@ -0,0 +1,31 @@ +using System; +using System.Numerics; +using JetBrains.Annotations; +using Robust.Shared.Maths; + +namespace Robust.Client.Graphics +{ + public interface IClydeAudioSource : IDisposable + { + void StartPlaying(); + void StopPlaying(); + + bool IsPlaying { get; } + + bool IsLooping { get; set; } + bool IsGlobal { get; } + + [MustUseReturnValue] + bool SetPosition(Vector2 position); + void SetPitch(float pitch); + void SetGlobal(); + void SetVolume(float decibels); + void SetVolumeDirect(float gain); + void SetMaxDistance(float maxDistance); + void SetRolloffFactor(float rolloffFactor); + void SetReferenceDistance(float refDistance); + void SetOcclusion(float blocks); + void SetPlaybackPosition(float seconds); + void SetVelocity(Vector2 velocity); + } +} diff --git a/Robust.Client/Graphics/IClydeBufferedAudioSource.cs b/Robust.Client/Graphics/IClydeBufferedAudioSource.cs new file mode 100644 index 00000000000..eccda3dc7c6 --- /dev/null +++ b/Robust.Client/Graphics/IClydeBufferedAudioSource.cs @@ -0,0 +1,15 @@ +using System; + +namespace Robust.Client.Graphics +{ + public interface IClydeBufferedAudioSource : IClydeAudioSource + { + int SampleRate { get; set; } + int GetNumberOfBuffersProcessed(); + void GetBuffersProcessed(Span handles); + void WriteBuffer(int handle, ReadOnlySpan data); + void WriteBuffer(int handle, ReadOnlySpan data); + void QueueBuffers(ReadOnlySpan handles); + void EmptyBuffers(); + } +} diff --git a/Robust.Client/Graphics/Overlays/IOverlayManager.cs b/Robust.Client/Graphics/Overlays/IOverlayManager.cs index 1c94894e004..611bae5e195 100644 --- a/Robust.Client/Graphics/Overlays/IOverlayManager.cs +++ b/Robust.Client/Graphics/Overlays/IOverlayManager.cs @@ -4,29 +4,32 @@ using JetBrains.Annotations; using Robust.Shared.Timing; -namespace Robust.Client.Graphics; - -[PublicAPI] -public interface IOverlayManager +namespace Robust.Client.Graphics { - bool AddOverlay(Overlay overlay); - bool RemoveOverlay(Overlay overlay); - bool RemoveOverlay(Type overlayClass); - bool RemoveOverlay() where T : Overlay; - bool TryGetOverlay(Type overlayClass, [NotNullWhen(true)] out Overlay? overlay); - bool TryGetOverlay([NotNullWhen(true)] out T? overlay) where T : Overlay; + [PublicAPI] + public interface IOverlayManager + { + bool AddOverlay(Overlay overlay); - Overlay GetOverlay(Type overlayClass); - T GetOverlay() where T : Overlay; + bool RemoveOverlay(Overlay overlay); + bool RemoveOverlay(Type overlayClass); + bool RemoveOverlay() where T : Overlay; - bool HasOverlay(Type overlayClass); - bool HasOverlay() where T : Overlay; + bool TryGetOverlay(Type overlayClass, [NotNullWhen(true)] out Overlay? overlay); + bool TryGetOverlay([NotNullWhen(true)] out T? overlay) where T : Overlay; - IEnumerable AllOverlays { get; } -} + Overlay GetOverlay(Type overlayClass); + T GetOverlay() where T : Overlay; -internal interface IOverlayManagerInternal : IOverlayManager -{ - void FrameUpdate(FrameEventArgs args); + bool HasOverlay(Type overlayClass); + bool HasOverlay() where T : Overlay; + + IEnumerable AllOverlays { get; } + } + + internal interface IOverlayManagerInternal : IOverlayManager + { + void FrameUpdate(FrameEventArgs args); + } } diff --git a/Robust.Client/Graphics/Overlays/OverlayManager.cs b/Robust.Client/Graphics/Overlays/OverlayManager.cs index e0022802bb7..5bb32c7109a 100644 --- a/Robust.Client/Graphics/Overlays/OverlayManager.cs +++ b/Robust.Client/Graphics/Overlays/OverlayManager.cs @@ -6,106 +6,107 @@ using Robust.Shared.Timing; using Robust.Shared.ViewVariables; -namespace Robust.Client.Graphics; - -internal sealed class OverlayManager : IOverlayManagerInternal, IPostInjectInit +namespace Robust.Client.Graphics { - [Dependency] private readonly ILogManager _logMan = default!; + internal sealed class OverlayManager : IOverlayManagerInternal, IPostInjectInit + { + [Dependency] private readonly ILogManager _logMan = default!; - [ViewVariables] - private readonly Dictionary _overlays = new Dictionary(); - private ISawmill _logger = default!; + [ViewVariables] + private readonly Dictionary _overlays = new Dictionary(); + private ISawmill _logger = default!; - public IEnumerable AllOverlays => _overlays.Values; + public IEnumerable AllOverlays => _overlays.Values; - public void FrameUpdate(FrameEventArgs args) - { - foreach (var overlay in _overlays.Values) + public void FrameUpdate(FrameEventArgs args) { - overlay.FrameUpdate(args); + foreach (var overlay in _overlays.Values) + { + overlay.FrameUpdate(args); + } } - } - - public bool AddOverlay(Overlay overlay) - { - if (_overlays.ContainsKey(overlay.GetType())) - return false; - _overlays.Add(overlay.GetType(), overlay); - return true; - } - public bool RemoveOverlay(Type overlayClass) - { - if (!overlayClass.IsSubclassOf(typeof(Overlay))) + public bool AddOverlay(Overlay overlay) { - _logger.Error($"RemoveOverlay was called with arg: {overlayClass}, which is not a subclass of Overlay!"); - return false; + if (_overlays.ContainsKey(overlay.GetType())) + return false; + _overlays.Add(overlay.GetType(), overlay); + return true; } - return _overlays.Remove(overlayClass); - } - - public bool RemoveOverlay() where T : Overlay - { - return RemoveOverlay(typeof(T)); - } + public bool RemoveOverlay(Type overlayClass) + { + if (!overlayClass.IsSubclassOf(typeof(Overlay))) + { + _logger.Error($"RemoveOverlay was called with arg: {overlayClass}, which is not a subclass of Overlay!"); + return false; + } - public bool RemoveOverlay(Overlay overlay) - { - return _overlays.Remove(overlay.GetType()); - } + return _overlays.Remove(overlayClass); + } - public bool TryGetOverlay(Type overlayClass, [NotNullWhen(true)] out Overlay? overlay) - { - overlay = null; - if (!overlayClass.IsSubclassOf(typeof(Overlay))) + public bool RemoveOverlay() where T : Overlay { - _logger.Error($"TryGetOverlay was called with arg: {overlayClass}, which is not a subclass of Overlay!"); - return false; + return RemoveOverlay(typeof(T)); } - return _overlays.TryGetValue(overlayClass, out overlay); - } + public bool RemoveOverlay(Overlay overlay) + { + return _overlays.Remove(overlay.GetType()); + } - public bool TryGetOverlay([NotNullWhen(true)] out T? overlay) where T : Overlay - { - overlay = null; - if (_overlays.TryGetValue(typeof(T), out Overlay? toReturn)) + public bool TryGetOverlay(Type overlayClass, [NotNullWhen(true)] out Overlay? overlay) { - overlay = (T)toReturn; - return true; + overlay = null; + if (!overlayClass.IsSubclassOf(typeof(Overlay))) + { + _logger.Error($"TryGetOverlay was called with arg: {overlayClass}, which is not a subclass of Overlay!"); + return false; + } + + return _overlays.TryGetValue(overlayClass, out overlay); } - return false; - } + public bool TryGetOverlay([NotNullWhen(true)] out T? overlay) where T : Overlay + { + overlay = null; + if (_overlays.TryGetValue(typeof(T), out Overlay? toReturn)) + { + overlay = (T)toReturn; + return true; + } - public Overlay GetOverlay(Type overlayClass) - { - return _overlays[overlayClass]; - } + return false; + } - public T GetOverlay() where T : Overlay - { - return (T)_overlays[typeof(T)]; - } + public Overlay GetOverlay(Type overlayClass) + { + return _overlays[overlayClass]; + } - public bool HasOverlay(Type overlayClass) - { - if (!overlayClass.IsSubclassOf(typeof(Overlay))) + public T GetOverlay() where T : Overlay { - _logger.Error($"HasOverlay was called with arg: {overlayClass}, which is not a subclass of Overlay!"); + return (T)_overlays[typeof(T)]; } - return _overlays.ContainsKey(overlayClass); - } + public bool HasOverlay(Type overlayClass) + { + if (!overlayClass.IsSubclassOf(typeof(Overlay))) + { + _logger.Error($"HasOverlay was called with arg: {overlayClass}, which is not a subclass of Overlay!"); + } - public bool HasOverlay() where T : Overlay - { - return _overlays.ContainsKey(typeof(T)); - } + return _overlays.ContainsKey(overlayClass); + } - void IPostInjectInit.PostInject() - { - _logger = _logMan.GetSawmill("overlay"); + public bool HasOverlay() where T : Overlay + { + return _overlays.ContainsKey(typeof(T)); + } + + void IPostInjectInit.PostInject() + { + _logger = _logMan.GetSawmill("overlay"); + } } } diff --git a/Robust.Client/Graphics/Shaders/ShaderPrototype.cs b/Robust.Client/Graphics/Shaders/ShaderPrototype.cs index 144f745795d..2ab6b323e1c 100644 --- a/Robust.Client/Graphics/Shaders/ShaderPrototype.cs +++ b/Robust.Client/Graphics/Shaders/ShaderPrototype.cs @@ -120,7 +120,7 @@ void ISerializationHooks.AfterDeserialization() if (_path == null) throw new InvalidOperationException("Source shaders must specify a source file."); - _source = IoCManager.Resolve().GetResource(_path.Value); + _source = IoCManager.Resolve().GetResource(_path.Value); if (_paramMapping != null) { @@ -142,7 +142,7 @@ void ISerializationHooks.AfterDeserialization() case "canvas": Kind = ShaderKind.Canvas; - _source = IoCManager.Resolve().GetResource("/Shaders/Internal/default-sprite.swsl"); + _source = IoCManager.Resolve().GetResource("/Shaders/Internal/default-sprite.swsl"); break; default: diff --git a/Robust.Client/Map/ClydeTileDefinitionManager.cs b/Robust.Client/Map/ClydeTileDefinitionManager.cs index d0dd55655aa..ce8081a88bd 100644 --- a/Robust.Client/Map/ClydeTileDefinitionManager.cs +++ b/Robust.Client/Map/ClydeTileDefinitionManager.cs @@ -7,7 +7,6 @@ using Robust.Client.ResourceManagement; using Robust.Client.Utility; using Robust.Shared.Console; -using Robust.Shared.ContentPack; using Robust.Shared.GameObjects; using Robust.Shared.Graphics; using Robust.Shared.IoC; @@ -23,7 +22,7 @@ namespace Robust.Client.Map { internal sealed class ClydeTileDefinitionManager : TileDefinitionManager, IClydeTileDefinitionManager { - [Dependency] private readonly IResourceManager _manager = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; private Texture? _tileTextureAtlas; @@ -87,7 +86,7 @@ internal void _genTextureAtlas() 0, (h - EyeManager.PixelsPerMeter) / h, tileSize / w, tileSize / h); Image image; - using (var stream = _manager.ContentFileRead("/Textures/noTile.png")) + using (var stream = _resourceCache.ContentFileRead("/Textures/noTile.png")) { image = Image.Load(stream); } @@ -111,7 +110,7 @@ internal void _genTextureAtlas() // Already know it's not null above var path = def.Sprite!.Value; - using (var stream = _manager.ContentFileRead(path)) + using (var stream = _resourceCache.ContentFileRead(path)) { image = Image.Load(stream); } diff --git a/Robust.Client/Physics/PhysicsSystem.Predict.cs b/Robust.Client/Physics/PhysicsSystem.Predict.cs index c47c15b4863..5a05bc3da97 100644 --- a/Robust.Client/Physics/PhysicsSystem.Predict.cs +++ b/Robust.Client/Physics/PhysicsSystem.Predict.cs @@ -1,6 +1,5 @@ using System.Buffers; using System.Collections.Generic; -using Robust.Client.Player; using Robust.Shared.GameObjects; using Robust.Shared.Map.Components; using Robust.Shared.Physics; @@ -8,6 +7,7 @@ using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Dynamics.Contacts; using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; using Robust.Shared.Utility; namespace Robust.Client.Physics; diff --git a/Robust.Client/Placement/PlacementManager.cs b/Robust.Client/Placement/PlacementManager.cs index f68adc236de..c83576138e1 100644 --- a/Robust.Client/Placement/PlacementManager.cs +++ b/Robust.Client/Placement/PlacementManager.cs @@ -28,7 +28,7 @@ public sealed partial class PlacementManager : IPlacementManager, IDisposable, I { [Dependency] private readonly IClientNetManager _networkManager = default!; [Dependency] internal readonly IPlayerManager PlayerManager = default!; - [Dependency] internal readonly IClientResourceCache ResourceCache = default!; + [Dependency] internal readonly IResourceCache ResourceCache = default!; [Dependency] private readonly IReflectionManager _reflectionManager = default!; [Dependency] internal readonly IMapManager MapManager = default!; [Dependency] private readonly IGameTiming _time = default!; diff --git a/Robust.Client/Player/IPlayerManager.cs b/Robust.Client/Player/IPlayerManager.cs index 20478543a74..cb3b2cc0052 100644 --- a/Robust.Client/Player/IPlayerManager.cs +++ b/Robust.Client/Player/IPlayerManager.cs @@ -15,15 +15,22 @@ public interface IPlayerManager : ISharedPlayerManager event Action? PlayerListUpdated; /// - /// Invoked when gets attached to a new entity. See also + /// Invoked when gets attached to a new entity, or when the local + /// session gets updated. See also /// event Action? LocalPlayerAttached; /// - /// Invoked when gets detached from new entity. See also + /// Invoked when gets detached from an entity, or when the local + /// session gets updated. See also /// event Action? LocalPlayerDetached; + /// + /// Invoked whenever changes. + /// + event Action<(ICommonSession? Old, ICommonSession? New)>? LocalSessionChanged; + void ApplyPlayerStates(IReadOnlyCollection list); /// @@ -38,34 +45,8 @@ public interface IPlayerManager : ISharedPlayerManager /// void SetupMultiplayer(INetChannel channel); + void SetLocalSession(ICommonSession session); + [Obsolete("Use LocalSession instead")] LocalPlayer? LocalPlayer { get;} } - -/// -/// ECS event that gets raised when the local player gets attached to a new entity. The event is both broadcast and -/// raised directed at the new entity. -/// -public sealed class LocalPlayerAttachedEvent : EntityEventArgs -{ - public LocalPlayerAttachedEvent(EntityUid entity) - { - Entity = entity; - } - - public EntityUid Entity { get; } -} - -/// -/// ECS event that gets raised when the local player gets detached from an entity. The event is both broadcast and -/// raised directed at the new entity. -/// -public sealed class LocalPlayerDetachedEvent : EntityEventArgs -{ - public LocalPlayerDetachedEvent(EntityUid entity) - { - Entity = entity; - } - - public EntityUid Entity { get; } -} \ No newline at end of file diff --git a/Robust.Client/Player/PlayerManager.cs b/Robust.Client/Player/PlayerManager.cs index 7c5557dcdd0..682b433ce06 100644 --- a/Robust.Client/Player/PlayerManager.cs +++ b/Robust.Client/Player/PlayerManager.cs @@ -42,12 +42,13 @@ public override ICommonSession[] NetworkedSessions /// public override int MaxPlayers => _client.GameInfo?.ServerMaxPlayers ?? -1; - public LocalPlayer? LocalPlayer { get; set; } + public LocalPlayer? LocalPlayer { get; private set; } public event Action? LocalStatusChanged; public event Action? PlayerListUpdated; public event Action? LocalPlayerDetached; public event Action? LocalPlayerAttached; + public event Action<(ICommonSession? Old, ICommonSession? New)>? LocalSessionChanged; /// public override void Initialize(int maxPlayers) @@ -64,22 +65,14 @@ private void StatusChanged(object? sender, SessionStatusEventArgs e) LocalStatusChanged?.Invoke(e); } - /// - public override void Startup() - { - if (LocalSession == null) - throw new InvalidOperationException("LocalSession cannot be null"); - - LocalPlayer = new LocalPlayer(LocalSession); - base.Startup(); - } - public void SetupSinglePlayer(string name) { if (LocalSession != null) throw new InvalidOperationException($"Player manager already running?"); - LocalSession = CreateAndAddSession(default, name); + var session = CreateAndAddSession(default, name); + session.ClientSide = true; + SetLocalSession(session); Startup(); PlayerListUpdated?.Invoke(); } @@ -89,18 +82,44 @@ public void SetupMultiplayer(INetChannel channel) if (LocalSession != null) throw new InvalidOperationException($"Player manager already running?"); - var session = CreateAndAddSession(channel.UserId, channel.UserName); - session.Channel = channel; - LocalSession = session; + SetLocalSession(CreateAndAddSession(channel)); Startup(); _network.ClientSendMessage(new MsgPlayerListReq()); } + public void SetLocalSession(ICommonSession? session) + { + if (session == LocalSession) + return; + + var old = LocalSession; + + if (old?.AttachedEntity is {} oldUid) + { + LocalSession = null; + LocalPlayer = null; + Sawmill.Info($"Detaching local player from {EntManager.ToPrettyString(oldUid)}."); + EntManager.EventBus.RaiseLocalEvent(oldUid, new LocalPlayerDetachedEvent(oldUid), true); + LocalPlayerDetached?.Invoke(oldUid); + } + + LocalSession = session; + LocalPlayer = session == null ? null : new LocalPlayer(session); + Sawmill.Info($"Changing local session from {old?.ToString() ?? "null"} to {session?.ToString() ?? "null"}."); + LocalSessionChanged?.Invoke((old, LocalSession)); + + if (session?.AttachedEntity is {} newUid) + { + Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(newUid)}."); + EntManager.EventBus.RaiseLocalEvent(newUid, new LocalPlayerAttachedEvent(newUid), true); + LocalPlayerAttached?.Invoke(newUid); + } + } + /// public override void Shutdown() { - if (LocalSession != null) - SetAttachedEntity(LocalSession, null); + SetAttachedEntity(LocalSession, null, out _); LocalPlayer = null; LocalSession = null; _pendingStates.Clear(); @@ -108,16 +127,21 @@ public override void Shutdown() PlayerListUpdated?.Invoke(); } - public override void SetAttachedEntity(ICommonSession session, EntityUid? uid) + public override bool SetAttachedEntity(ICommonSession? session, EntityUid? uid, out ICommonSession? kicked, bool force = false) { + kicked = null; + if (session == null) + return false; + if (session.AttachedEntity == uid) - return; + return true; var old = session.AttachedEntity; - base.SetAttachedEntity(session, uid); + if (!base.SetAttachedEntity(session, uid, out kicked, force)) + return false; if (session != LocalSession) - return; + return true; if (old.HasValue) { @@ -129,13 +153,13 @@ public override void SetAttachedEntity(ICommonSession session, EntityUid? uid) if (uid == null) { Sawmill.Info($"Local player is no longer attached to any entity."); - return; + return true; } if (!EntManager.EntityExists(uid)) { Sawmill.Error($"Attempted to attach player to non-existent entity {uid}!"); - return; + return true; } if (!EntManager.EnsureComponent(uid.Value, out EyeComponent eye)) @@ -148,6 +172,7 @@ public override void SetAttachedEntity(ICommonSession session, EntityUid? uid) Sawmill.Info($"Attaching local player to {EntManager.ToPrettyString(uid)}."); EntManager.EventBus.RaiseLocalEvent(uid.Value, new LocalPlayerAttachedEvent(uid.Value), true); LocalPlayerAttached?.Invoke(uid.Value); + return true; } public void ApplyPlayerStates(IReadOnlyCollection list) @@ -193,7 +218,7 @@ private bool ApplyStates(IReadOnlyCollection list, bool fullList) _pendingStates.Remove(state.UserId); } - SetAttachedEntity(LocalSession, uid); + SetAttachedEntity(LocalSession, uid, out _, true); SetStatus(LocalSession, state.Status); } @@ -233,11 +258,10 @@ private bool UpdatePlayerList(IEnumerable remotePlayers, bool full { // This is a new userid, so we create a new session. DebugTools.Assert(state.UserId != LocalPlayer?.UserId); - var newSession = CreateAndAddSession(state.UserId, state.Name); + var newSession = (CommonSession) CreateAndAddSession(state.UserId, state.Name); newSession.Ping = state.Ping; - newSession.Name = state.Name; SetStatus(newSession, state.Status); - SetAttachedEntity(newSession, controlled); + SetAttachedEntity(newSession, controlled, out _, true); dirty = true; continue; } @@ -256,7 +280,7 @@ private bool UpdatePlayerList(IEnumerable remotePlayers, bool full local.Name = state.Name; local.Ping = state.Ping; SetStatus(local, state.Status); - SetAttachedEntity(local, controlled); + SetAttachedEntity(local, controlled, out _, true); } // Remove old users. This only works if the provided state is a list of all players @@ -264,10 +288,12 @@ private bool UpdatePlayerList(IEnumerable remotePlayers, bool full { foreach (var oldUser in InternalSessions.Keys.ToArray()) { - // clear slot, player left if (users.Contains(oldUser)) continue; + if (InternalSessions[oldUser].ClientSide) + continue; + DebugTools.Assert(oldUser != LocalUser || LocalUser == null || LocalUser == default(NetUserId), diff --git a/Robust.Client/Profiling/LiveProfileViewControl.cs b/Robust.Client/Profiling/LiveProfileViewControl.cs index 5154b543400..01e1a67d076 100644 --- a/Robust.Client/Profiling/LiveProfileViewControl.cs +++ b/Robust.Client/Profiling/LiveProfileViewControl.cs @@ -13,7 +13,7 @@ namespace Robust.Client.Profiling; public sealed class LiveProfileViewControl : Control { [Dependency] private readonly ProfManager _profManager = default!; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; public int MaxDepth { get; set; } = 2; diff --git a/Robust.Client/Replays/Playback/IReplayPlaybackManager.cs b/Robust.Client/Replays/Playback/IReplayPlaybackManager.cs index 3073bc754a7..3793f81403b 100644 --- a/Robust.Client/Replays/Playback/IReplayPlaybackManager.cs +++ b/Robust.Client/Replays/Playback/IReplayPlaybackManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.Network; using Robust.Shared.Replays; using Robust.Shared.Serialization.Markdown.Mapping; @@ -128,6 +129,11 @@ public interface IReplayPlaybackManager /// event Action? ReplayUnpaused; + /// + /// Invoked just before a replay applies a game state. + /// + event Action<(GameState Current, GameState? Next)>? BeforeApplyState; + /// /// If currently replaying a client-side recording, this is the user that recorded the replay. /// Useful for setting default observer spawn positions. @@ -137,5 +143,5 @@ public interface IReplayPlaybackManager /// /// Fetches the entity that the is currently attached to. /// - public bool TryGetRecorderEntity([NotNullWhen(true)] out EntityUid? uid); + bool TryGetRecorderEntity([NotNullWhen(true)] out EntityUid? uid); } diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs index cfd9f77e0a8..adc8ed1248f 100644 --- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs +++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs @@ -66,6 +66,7 @@ private void ApplyCheckpointState(CheckpointState checkpoint, ReplayData replay) _gameState.ClearDetachQueue(); EnsureDetachedExist(checkpoint); _gameState.DetachImmediate(checkpoint.Detached); + BeforeApplyState?.Invoke((checkpoint.State, next)); _gameState.ApplyGameState(checkpoint.State, next); } diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Time.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Time.cs index f19d66a7328..9643ae1ac1a 100644 --- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Time.cs +++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Time.cs @@ -1,4 +1,5 @@ using System; +using Robust.Client.GameObjects; using Robust.Client.GameStates; using Robust.Shared.Utility; @@ -58,7 +59,13 @@ public void SetIndex(int value, bool pausePlayback = true) _timing.LastRealTick = _timing.LastProcessedTick = _timing.CurTick = Replay.CurTick; _gameState.UpdateFullRep(state, cloneDelta: true); - _gameState.ApplyGameState(state, Replay.NextState); + + // Clear existing lerps + _entMan.EntitySysManager.GetEntitySystem().Reset(); + + var next = Replay.NextState; + BeforeApplyState?.Invoke((state, next)); + _gameState.ApplyGameState(state, next); ProcessMessages(Replay.CurMessages, skipEffectEvents); // TODO REPLAYS block audio diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs index b14a36b3669..6fb60f36e54 100644 --- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs +++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs @@ -42,7 +42,9 @@ private void TickUpdateOverride(FrameEventArgs args) { var state = Replay.CurState; _gameState.UpdateFullRep(state, cloneDelta: true); - _gameState.ApplyGameState(state, Replay.NextState); + var next = Replay.NextState; + BeforeApplyState?.Invoke((state, next)); + _gameState.ApplyGameState(state, next); DebugTools.Assert(Replay.LastApplied >= state.FromSequence); DebugTools.Assert(Replay.LastApplied + 1 <= state.ToSequence); Replay.LastApplied = state.ToSequence; diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs index 0b3c7d1d54e..a8df7fe91b9 100644 --- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs +++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Robust.Client.Audio; using Robust.Client.Audio.Midi; using Robust.Client.Configuration; using Robust.Client.GameObjects; @@ -11,9 +10,9 @@ using Robust.Client.Timing; using Robust.Client.Upload; using Robust.Shared; -using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Network; @@ -29,7 +28,7 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager [Dependency] private readonly IBaseClient _client = default!; [Dependency] private readonly IMidiManager _midi = default!; [Dependency] private readonly IPlayerManager _player = default!; - [Dependency] private readonly IAudioInternal _clydeAudio = default!; + [Dependency] private readonly IClydeAudio _clydeAudio = default!; [Dependency] private readonly IClientGameTiming _timing = default!; [Dependency] private readonly IClientNetManager _netMan = default!; [Dependency] private readonly IComponentFactory _factory = default!; @@ -46,6 +45,7 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager public event Action? ReplayPlaybackStopped; public event Action? ReplayPaused; public event Action? ReplayUnpaused; + public event Action<(GameState Current, GameState? Next)>? BeforeApplyState; public ReplayData? Replay { get; private set; } public NetUserId? Recorder => Replay?.Recorder; diff --git a/Robust.Client/ResourceManagement/BaseResource.cs b/Robust.Client/ResourceManagement/BaseResource.cs new file mode 100644 index 00000000000..0381c708a15 --- /dev/null +++ b/Robust.Client/ResourceManagement/BaseResource.cs @@ -0,0 +1,36 @@ +using System; +using System.Threading; +using Robust.Shared.Utility; + +namespace Robust.Client.ResourceManagement +{ + /// + /// Base resource for the cache. + /// + public abstract class BaseResource : IDisposable + { + /// + /// Fallback resource path if this one does not exist. + /// + public virtual ResPath? Fallback => null; + + /// + /// Disposes this resource. + /// + public virtual void Dispose() + { + } + + /// + /// Deserializes the resource from the VFS. + /// + /// ResourceCache this resource is being loaded into. + /// Path of the resource requested on the VFS. + public abstract void Load(IResourceCache cache, ResPath path); + + public virtual void Reload(IResourceCache cache, ResPath path, CancellationToken ct = default) + { + + } + } +} diff --git a/Robust.Client/ResourceManagement/IClientResourceCache.cs b/Robust.Client/ResourceManagement/IClientResourceCache.cs deleted file mode 100644 index ba14ee5806d..00000000000 --- a/Robust.Client/ResourceManagement/IClientResourceCache.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Robust.Client.Graphics; -using Robust.Shared.Audio; -using Robust.Shared.GameObjects; - -namespace Robust.Client.ResourceManagement; - -/// -public interface IClientResourceCache : IResourceCache -{ - // Resource load callbacks so content can hook stuff like click maps. - event Action OnRawTextureLoaded; - event Action OnRsiLoaded; - - IClyde Clyde { get; } - IFontManager FontManager { get; } -} diff --git a/Robust.Client/ResourceManagement/IClientResourceCacheInternal.cs b/Robust.Client/ResourceManagement/IClientResourceCacheInternal.cs deleted file mode 100644 index a9e5c8e0510..00000000000 --- a/Robust.Client/ResourceManagement/IClientResourceCacheInternal.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.LoaderApi; -using Robust.Shared.ContentPack; -using Robust.Shared.Utility; - -namespace Robust.Client.ResourceManagement; - -/// -internal interface IClientResourceCacheInternal : IClientResourceCache -{ - void TextureLoaded(TextureLoadedEventArgs eventArgs); - void RsiLoaded(RsiLoadedEventArgs eventArgs); - void PreloadTextures(); - - void MountLoaderApi(IResourceManager manager, IFileApi api, string apiPrefix, ResPath? prefix = null); -} diff --git a/Robust.Client/ResourceManagement/IResourceCache.cs b/Robust.Client/ResourceManagement/IResourceCache.cs new file mode 100644 index 00000000000..7b9b3a98a35 --- /dev/null +++ b/Robust.Client/ResourceManagement/IResourceCache.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Robust.Client.Graphics; +using Robust.Shared.ContentPack; +using Robust.Shared.Utility; + +namespace Robust.Client.ResourceManagement +{ + public interface IResourceCache : IResourceManager + { + T GetResource(string path, bool useFallback = true) + where T : BaseResource, new(); + + T GetResource(ResPath path, bool useFallback = true) + where T : BaseResource, new(); + + bool TryGetResource(string path, [NotNullWhen(true)] out T? resource) + where T : BaseResource, new(); + + bool TryGetResource(ResPath path, [NotNullWhen(true)] out T? resource) + where T : BaseResource, new(); + + void ReloadResource(string path) + where T : BaseResource, new(); + + void ReloadResource(ResPath path) + where T : BaseResource, new(); + + void CacheResource(string path, T resource) + where T : BaseResource, new(); + + void CacheResource(ResPath path, T resource) + where T : BaseResource, new(); + + T GetFallback() + where T : BaseResource, new(); + + IEnumerable> GetAllResources() where T : BaseResource, new(); + + // Resource load callbacks so content can hook stuff like click maps. + event Action OnRawTextureLoaded; + event Action OnRsiLoaded; + + IClyde Clyde { get; } + IClydeAudio ClydeAudio { get; } + IFontManager FontManager { get; } + } +} diff --git a/Robust.Client/ResourceManagement/IResourceCacheInternal.cs b/Robust.Client/ResourceManagement/IResourceCacheInternal.cs new file mode 100644 index 00000000000..ed78d53d4e6 --- /dev/null +++ b/Robust.Client/ResourceManagement/IResourceCacheInternal.cs @@ -0,0 +1,15 @@ +using Robust.LoaderApi; +using Robust.Shared.ContentPack; +using Robust.Shared.Utility; + +namespace Robust.Client.ResourceManagement +{ + internal interface IResourceCacheInternal : IResourceCache, IResourceManagerInternal + { + void TextureLoaded(TextureLoadedEventArgs eventArgs); + void RsiLoaded(RsiLoadedEventArgs eventArgs); + + void MountLoaderApi(IFileApi api, string apiPrefix, ResPath? prefix=null); + void PreloadTextures(); + } +} diff --git a/Robust.Client/ResourceManagement/ResourceCache.LoaderApi.cs b/Robust.Client/ResourceManagement/ResourceCache.LoaderApi.cs index ca60ad6132d..6bfbf6af933 100644 --- a/Robust.Client/ResourceManagement/ResourceCache.LoaderApi.cs +++ b/Robust.Client/ResourceManagement/ResourceCache.LoaderApi.cs @@ -9,13 +9,6 @@ namespace Robust.Client.ResourceManagement { internal partial class ResourceCache { - public void MountLoaderApi(IResourceManager manager, IFileApi api, string apiPrefix, ResPath? prefix = null) - { - prefix ??= ResPath.Root; - var root = new LoaderApiLoader(api, apiPrefix); - manager.AddRoot(prefix.Value, root); - } - private sealed class LoaderApiLoader : IContentRoot { private readonly IFileApi _api; diff --git a/Robust.Client/ResourceManagement/ResourceCache.Preload.cs b/Robust.Client/ResourceManagement/ResourceCache.Preload.cs index f698171adbf..90a526a1a57 100644 --- a/Robust.Client/ResourceManagement/ResourceCache.Preload.cs +++ b/Robust.Client/ResourceManagement/ResourceCache.Preload.cs @@ -3,13 +3,10 @@ using System.Linq; using System.Threading.Tasks; using OpenToolkit.Graphics.OpenGL4; -using Robust.Client.Audio; using Robust.Client.Graphics; using Robust.Client.Utility; using Robust.Shared; -using Robust.Shared.Audio; using Robust.Shared.Configuration; -using Robust.Shared.ContentPack; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Maths; @@ -22,8 +19,7 @@ namespace Robust.Client.ResourceManagement internal partial class ResourceCache { [field: Dependency] public IClyde Clyde { get; } = default!; - [field: Dependency] public IAudioInternal ClydeAudio { get; } = default!; - [Dependency] private readonly IResourceManager _manager = default!; + [field: Dependency] public IClydeAudio ClydeAudio { get; } = default!; [field: Dependency] public IFontManager FontManager { get; } = default!; [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!; @@ -48,7 +44,7 @@ private void PreloadTextures(ISawmill sawmill) var sw = Stopwatch.StartNew(); var resList = GetTypeDict(); - var texList = _manager.ContentFindFiles("/Textures/") + var texList = ContentFindFiles("/Textures/") // Skip PNG files inside RSIs. .Where(p => p.Extension == "png" && !p.ToString().Contains(".rsi/") && !resList.ContainsKey(p)) .Select(p => new TextureResource.LoadStepData {Path = p}) @@ -58,7 +54,7 @@ private void PreloadTextures(ISawmill sawmill) { try { - TextureResource.LoadPreTexture(_manager, data); + TextureResource.LoadPreTexture(this, data); } catch (Exception e) { @@ -120,7 +116,7 @@ private void PreloadRsis(ISawmill sawmill) var sw = Stopwatch.StartNew(); var resList = GetTypeDict(); - var rsiList = _manager.ContentFindFiles("/Textures/") + var rsiList = ContentFindFiles("/Textures/") .Where(p => p.ToString().EndsWith(".rsi/meta.json")) .Select(c => c.Directory) .Where(p => !resList.ContainsKey(p)) @@ -131,7 +127,7 @@ private void PreloadRsis(ISawmill sawmill) { try { - RSIResource.LoadPreTexture(_manager, data); + RSIResource.LoadPreTexture(this, data); } catch (Exception e) { diff --git a/Robust.Client/ResourceManagement/ResourceCache.cs b/Robust.Client/ResourceManagement/ResourceCache.cs index 94f0fe9f482..8e3de707549 100644 --- a/Robust.Client/ResourceManagement/ResourceCache.cs +++ b/Robust.Client/ResourceManagement/ResourceCache.cs @@ -1,23 +1,221 @@ -using System; -using Robust.Shared.ResourceManagement; +using Robust.Shared.ContentPack; +using Robust.Shared.Log; +using Robust.Shared.Utility; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; +using Robust.LoaderApi; -namespace Robust.Client.ResourceManagement; - -/// -/// Handles caching of -/// -internal sealed partial class ResourceCache : SharedResourceCache, IClientResourceCacheInternal, IDisposable +namespace Robust.Client.ResourceManagement { - public event Action? OnRawTextureLoaded; - public event Action? OnRsiLoaded; - - public void TextureLoaded(TextureLoadedEventArgs eventArgs) + internal sealed partial class ResourceCache : ResourceManager, IResourceCacheInternal, IDisposable { - OnRawTextureLoaded?.Invoke(eventArgs); - } + private readonly Dictionary> CachedResources = + new(); - public void RsiLoaded(RsiLoadedEventArgs eventArgs) - { - OnRsiLoaded?.Invoke(eventArgs); + private readonly Dictionary _fallbacks = new(); + + public T GetResource(string path, bool useFallback = true) where T : BaseResource, new() + { + return GetResource(new ResPath(path), useFallback); + } + + public T GetResource(ResPath path, bool useFallback = true) where T : BaseResource, new() + { + var cache = GetTypeDict(); + if (cache.TryGetValue(path, out var cached)) + { + return (T) cached; + } + + var _resource = new T(); + try + { + _resource.Load(this, path); + cache[path] = _resource; + return _resource; + } + catch (Exception e) + { + if (useFallback && _resource.Fallback != null) + { + Logger.Error( + $"Exception while loading resource {typeof(T)} at '{path}', resorting to fallback.\n{Environment.StackTrace}\n{e}"); + return GetResource(_resource.Fallback.Value, false); + } + else + { + Logger.Error( + $"Exception while loading resource {typeof(T)} at '{path}', no fallback available\n{Environment.StackTrace}\n{e}"); + throw; + } + } + } + + public bool TryGetResource(string path, [NotNullWhen(true)] out T? resource) where T : BaseResource, new() + { + return TryGetResource(new ResPath(path), out resource); + } + + public bool TryGetResource(ResPath path, [NotNullWhen(true)] out T? resource) where T : BaseResource, new() + { + var cache = GetTypeDict(); + if (cache.TryGetValue(path, out var cached)) + { + resource = (T) cached; + return true; + } + + var _resource = new T(); + try + { + _resource.Load(this, path); + resource = _resource; + cache[path] = resource; + return true; + } + catch + { + resource = null; + return false; + } + } + + public void ReloadResource(string path) where T : BaseResource, new() + { + ReloadResource(new ResPath(path)); + } + + public void ReloadResource(ResPath path) where T : BaseResource, new() + { + var cache = GetTypeDict(); + + if (!cache.TryGetValue(path, out var res)) + { + return; + } + + try + { + res.Reload(this, path); + } + catch (Exception e) + { + Logger.Error($"Exception while reloading resource {typeof(T)} at '{path}'\n{e}"); + throw; + } + } + + public bool HasResource(string path) where T : BaseResource, new() + { + return HasResource(new ResPath(path)); + } + + public bool HasResource(ResPath path) where T : BaseResource, new() + { + return TryGetResource(path, out var _); + } + + public void CacheResource(string path, T resource) where T : BaseResource, new() + { + CacheResource(new ResPath(path), resource); + } + + public void CacheResource(ResPath path, T resource) where T : BaseResource, new() + { + GetTypeDict()[path] = resource; + } + + public T GetFallback() where T : BaseResource, new() + { + if (_fallbacks.TryGetValue(typeof(T), out var fallback)) + { + return (T) fallback; + } + + var res = new T(); + if (res.Fallback == null) + { + throw new InvalidOperationException($"Resource of type '{typeof(T)}' has no fallback."); + } + + fallback = GetResource(res.Fallback.Value, useFallback: false); + _fallbacks.Add(typeof(T), fallback); + return (T) fallback; + } + + public IEnumerable> GetAllResources() where T : BaseResource, new() + { + return GetTypeDict().Select(p => new KeyValuePair(p.Key, (T) p.Value)); + } + + public event Action? OnRawTextureLoaded; + public event Action? OnRsiLoaded; + + #region IDisposable Members + + private bool disposed = false; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + if (disposing) + { + foreach (var res in CachedResources.Values.SelectMany(dict => dict.Values)) + { + res.Dispose(); + } + } + + disposed = true; + } + + ~ResourceCache() + { + Dispose(false); + } + + #endregion IDisposable Members + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Dictionary GetTypeDict() + { + if (!CachedResources.TryGetValue(typeof(T), out var ret)) + { + ret = new Dictionary(); + CachedResources.Add(typeof(T), ret); + } + + return ret; + } + + public void TextureLoaded(TextureLoadedEventArgs eventArgs) + { + OnRawTextureLoaded?.Invoke(eventArgs); + } + + public void RsiLoaded(RsiLoadedEventArgs eventArgs) + { + OnRsiLoaded?.Invoke(eventArgs); + } + + public void MountLoaderApi(IFileApi api, string apiPrefix, ResPath? prefix=null) + { + prefix ??= ResPath.Root; + var root = new LoaderApiLoader(api, apiPrefix); + AddRoot(prefix.Value, root); + } } } diff --git a/Robust.Client/ResourceManagement/ResourceTypes/AudioResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/AudioResource.cs new file mode 100644 index 00000000000..110a7c5250e --- /dev/null +++ b/Robust.Client/ResourceManagement/ResourceTypes/AudioResource.cs @@ -0,0 +1,43 @@ +using System; +using Robust.Client.Audio; +using Robust.Shared.Utility; +using System.IO; +using Robust.Client.Graphics; +using Robust.Shared.IoC; + +namespace Robust.Client.ResourceManagement +{ + public sealed class AudioResource : BaseResource + { + public AudioStream AudioStream { get; private set; } = default!; + + public override void Load(IResourceCache cache, ResPath path) + { + if (!cache.ContentFileExists(path)) + { + throw new FileNotFoundException("Content file does not exist for audio sample."); + } + + using (var fileStream = cache.ContentFileRead(path)) + { + if (path.Extension == "ogg") + { + AudioStream = cache.ClydeAudio.LoadAudioOggVorbis(fileStream, path.ToString()); + } + else if (path.Extension == "wav") + { + AudioStream = cache.ClydeAudio.LoadAudioWav(fileStream, path.ToString()); + } + else + { + throw new NotSupportedException("Unable to load audio files outside of ogg Vorbis or PCM wav"); + } + } + } + + public static implicit operator AudioStream(AudioResource res) + { + return res.AudioStream; + } + } +} diff --git a/Robust.Client/ResourceManagement/ResourceTypes/FontResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/FontResource.cs index 8c7f3994948..8f570542151 100644 --- a/Robust.Client/ResourceManagement/ResourceTypes/FontResource.cs +++ b/Robust.Client/ResourceManagement/ResourceTypes/FontResource.cs @@ -1,8 +1,6 @@ using System.IO; using Robust.Client.Graphics; -using Robust.Shared.ContentPack; using Robust.Shared.IoC; -using Robust.Shared.ResourceManagement; using Robust.Shared.Utility; namespace Robust.Client.ResourceManagement @@ -11,17 +9,16 @@ public sealed class FontResource : BaseResource { internal IFontFaceHandle FontFaceHandle { get; private set; } = default!; - public override void Load(IDependencyCollection dependencies, ResPath path) + public override void Load(IResourceCache cache, ResPath path) { - - if (!dependencies.Resolve().TryContentFileRead(path, out var stream)) + if (!cache.TryContentFileRead(path, out var stream)) { throw new FileNotFoundException("Content file does not exist for font"); } using (stream) { - FontFaceHandle = dependencies.Resolve().Load(stream); + FontFaceHandle = ((IFontManagerInternal)cache.FontManager).Load(stream); } } diff --git a/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs index 24e213dc527..c7cb919f115 100644 --- a/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs +++ b/Robust.Client/ResourceManagement/ResourceTypes/RSIResource.cs @@ -3,12 +3,10 @@ using System.Linq; using Robust.Client.Graphics; using Robust.Client.Utility; -using Robust.Shared.ContentPack; using Robust.Shared.Graphics; using Robust.Shared.Graphics.RSI; using Robust.Shared.IoC; using Robust.Shared.Maths; -using Robust.Shared.ResourceManagement; using Robust.Shared.Resources; using Robust.Shared.Utility; using SixLabors.ImageSharp; @@ -36,27 +34,26 @@ public sealed class RSIResource : BaseResource /// public const uint MAXIMUM_RSI_VERSION = RsiLoading.MAXIMUM_RSI_VERSION; - public override void Load(IDependencyCollection dependencies, ResPath path) + public override void Load(IResourceCache cache, ResPath path) { var loadStepData = new LoadStepData {Path = path}; - var manager = dependencies.Resolve(); - LoadPreTexture(manager, loadStepData); + LoadPreTexture(cache, loadStepData); - loadStepData.AtlasTexture = dependencies.Resolve().LoadTextureFromImage( + loadStepData.AtlasTexture = cache.Clyde.LoadTextureFromImage( loadStepData.AtlasSheet, loadStepData.Path.ToString()); LoadPostTexture(loadStepData); - LoadFinish(dependencies.Resolve(), loadStepData); + LoadFinish(cache, loadStepData); loadStepData.AtlasSheet.Dispose(); } - internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) + internal static void LoadPreTexture(IResourceCache cache, LoadStepData data) { var manifestPath = data.Path / "meta.json"; RsiLoading.RsiMetadata metadata; - using (var manifestFile = manager.ContentFileRead(manifestPath)) + using (var manifestFile = cache.ContentFileRead(manifestPath)) { metadata = RsiLoading.LoadRsiMetadata(manifestFile); } @@ -89,7 +86,7 @@ internal static void LoadPreTexture(IResourceManager manager, LoadStepData data) var stateObject = metadata.States[index]; // Load image from disk. var texPath = data.Path / (stateObject.StateId + ".png"); - using (var stream = manager.ContentFileRead(texPath)) + using (var stream = cache.ContentFileRead(texPath)) { reg.Src = Image.Load(stream); } @@ -215,10 +212,14 @@ internal static void LoadPostTexture(LoadStepData data) } } - internal void LoadFinish(IClientResourceCacheInternal cache, LoadStepData data) + internal void LoadFinish(IResourceCache cache, LoadStepData data) { RSI = data.Rsi; - cache.RsiLoaded(new RsiLoadedEventArgs(data.Path, this, data.AtlasSheet, data.CallbackOffsets)); + + if (cache is IResourceCacheInternal cacheInternal) + { + cacheInternal.RsiLoaded(new RsiLoadedEventArgs(data.Path, this, data.AtlasSheet, data.CallbackOffsets)); + } } /// diff --git a/Robust.Client/ResourceManagement/ResourceTypes/ShaderSourceResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/ShaderSourceResource.cs index deae43661c6..5d5df20b111 100644 --- a/Robust.Client/ResourceManagement/ResourceTypes/ShaderSourceResource.cs +++ b/Robust.Client/ResourceManagement/ResourceTypes/ShaderSourceResource.cs @@ -4,7 +4,6 @@ using Robust.Client.Graphics.Clyde; using Robust.Shared.ContentPack; using Robust.Shared.IoC; -using Robust.Shared.ResourceManagement; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; @@ -21,31 +20,28 @@ internal sealed class ShaderSourceResource : BaseResource [ViewVariables] internal ParsedShader ParsedShader { get; private set; } = default!; - public override void Load(IDependencyCollection dependencies, ResPath path) + public override void Load(IResourceCache cache, ResPath path) { - var manager = dependencies.Resolve(); - - using (var stream = manager.ContentFileRead(path)) + using (var stream = cache.ContentFileRead(path)) using (var reader = new StreamReader(stream, EncodingHelpers.UTF8)) { - ParsedShader = ShaderParser.Parse(reader, manager); + ParsedShader = ShaderParser.Parse(reader, cache); } - ClydeHandle = dependencies.Resolve().LoadShader(ParsedShader, path.ToString()); + ClydeHandle = ((IClydeInternal)cache.Clyde).LoadShader(ParsedShader, path.ToString()); } - public override void Reload(IDependencyCollection dependencies, ResPath path, CancellationToken ct = default) + public override void Reload(IResourceCache cache, ResPath path, CancellationToken ct = default) { - var manager = dependencies.Resolve(); ct = ct != default ? ct : new CancellationTokenSource(30000).Token; for (;;) { try { - using var stream = manager.ContentFileRead(path); + using var stream = cache.ContentFileRead(path); using var reader = new StreamReader(stream, EncodingHelpers.UTF8); - ParsedShader = ShaderParser.Parse(reader, manager); + ParsedShader = ShaderParser.Parse(reader, cache); break; } catch (IOException ioe) @@ -61,7 +57,7 @@ public override void Reload(IDependencyCollection dependencies, ResPath path, Ca } } - dependencies.Resolve().ReloadShader(ClydeHandle, ParsedShader); + ((IClydeInternal)cache.Clyde).ReloadShader(ClydeHandle, ParsedShader); } } } diff --git a/Robust.Client/ResourceManagement/ResourceTypes/TextureResource.cs b/Robust.Client/ResourceManagement/ResourceTypes/TextureResource.cs index c375e2d5f08..24db71b9207 100644 --- a/Robust.Client/ResourceManagement/ResourceTypes/TextureResource.cs +++ b/Robust.Client/ResourceManagement/ResourceTypes/TextureResource.cs @@ -1,12 +1,10 @@ using System.IO; using System.Threading; using Robust.Client.Graphics; -using Robust.Shared.ContentPack; using Robust.Shared.Graphics; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Maths; -using Robust.Shared.ResourceManagement; using Robust.Shared.Utility; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; @@ -21,7 +19,7 @@ public sealed class TextureResource : BaseResource public Texture Texture => _texture; - public override void Load(IDependencyCollection dependencies, ResPath path) + public override void Load(IResourceCache cache, ResPath path) { if (path.Directory.Filename.EndsWith(".rsi")) { @@ -33,12 +31,12 @@ public override void Load(IDependencyCollection dependencies, ResPath path) var data = new LoadStepData {Path = path}; - LoadPreTexture(dependencies.Resolve(), data); - LoadTexture(dependencies.Resolve(), data); - LoadFinish(dependencies.Resolve(), data); + LoadPreTexture(cache, data); + LoadTexture(cache.Clyde, data); + LoadFinish(cache, data); } - internal static void LoadPreTexture(IResourceManager cache, LoadStepData data) + internal static void LoadPreTexture(IResourceCache cache, LoadStepData data) { using (var stream = cache.ContentFileRead(data.Path)) { @@ -53,11 +51,11 @@ internal static void LoadTexture(IClyde clyde, LoadStepData data) data.Texture = clyde.LoadTextureFromImage(data.Image, data.Path.ToString(), data.LoadParameters); } - internal void LoadFinish(IClientResourceCache cache, LoadStepData data) + internal void LoadFinish(IResourceCache cache, LoadStepData data) { _texture = data.Texture; - if (cache is IClientResourceCacheInternal cacheInternal) + if (cache is IResourceCacheInternal cacheInternal) { cacheInternal.TextureLoaded(new TextureLoadedEventArgs(data.Path, data.Image, this)); } @@ -65,7 +63,7 @@ internal void LoadFinish(IClientResourceCache cache, LoadStepData data) data.Image.Dispose(); } - private static TextureLoadParameters? TryLoadTextureParameters(IResourceManager cache, ResPath path) + private static TextureLoadParameters? TryLoadTextureParameters(IResourceCache cache, ResPath path) { var metaPath = path.WithName(path.Filename + ".yml"); if (cache.TryContentFileRead(metaPath, out var stream)) @@ -92,11 +90,12 @@ internal void LoadFinish(IClientResourceCache cache, LoadStepData data) return null; } - public override void Reload(IDependencyCollection dependencies, ResPath path, CancellationToken ct = default) + public override void Reload(IResourceCache cache, ResPath path, CancellationToken ct = default) { - var data = new LoadStepData {Path = path}; + var clyde = IoCManager.Resolve(); - LoadPreTexture(dependencies.Resolve(), data); + var data = new LoadStepData {Path = path}; + LoadPreTexture(cache, data); if (data.Image.Width == Texture.Width && data.Image.Height == Texture.Height) { @@ -107,7 +106,7 @@ public override void Reload(IDependencyCollection dependencies, ResPath path, Ca { // Dimensions do not match, make new texture. _texture.Dispose(); - LoadTexture(dependencies.Resolve(), data); + LoadTexture(clyde, data); _texture = data.Texture; } diff --git a/Robust.Client/Robust.Client.csproj b/Robust.Client/Robust.Client.csproj index 4654b0cc8a9..b1eb05fc071 100644 --- a/Robust.Client/Robust.Client.csproj +++ b/Robust.Client/Robust.Client.csproj @@ -16,12 +16,12 @@ + - diff --git a/Robust.Client/Serialization/ClientSpriteSpecifierSerializer.cs b/Robust.Client/Serialization/ClientSpriteSpecifierSerializer.cs index 08ee5d3b882..aea3cc09bde 100644 --- a/Robust.Client/Serialization/ClientSpriteSpecifierSerializer.cs +++ b/Robust.Client/Serialization/ClientSpriteSpecifierSerializer.cs @@ -27,7 +27,7 @@ public override ValidationNode ValidateRsi(ISerializationManager serializationMa return new ErrorNode(node, "Sprite specifier has missing/invalid state node"); } - var res = dependencies.Resolve(); + var res = dependencies.Resolve(); var rsiPath = TextureRoot / valuePathNode.Value; if (!res.TryGetResource(rsiPath, out RSIResource? resource)) { diff --git a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs index b687e94dc4b..dda465d5369 100644 --- a/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs +++ b/Robust.Client/UserInterface/Controllers/Implementations/EntitySpawningUIController.cs @@ -21,7 +21,7 @@ public sealed class EntitySpawningUIController : UIController { [Dependency] private readonly IPlacementManager _placement = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!; - [Dependency] private readonly IClientResourceCache _resources = default!; + [Dependency] private readonly IResourceCache _resources = default!; private EntitySpawnWindow? _window; private readonly List _shownEntities = new(); diff --git a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs index 445344d6042..104f20df466 100644 --- a/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs +++ b/Robust.Client/UserInterface/Controllers/Implementations/TileSpawningUIController.cs @@ -19,7 +19,7 @@ namespace Robust.Client.UserInterface.Controllers.Implementations; public sealed class TileSpawningUIController : UIController { [Dependency] private readonly IPlacementManager _placement = default!; - [Dependency] private readonly IClientResourceCache _resources = default!; + [Dependency] private readonly IResourceCache _resources = default!; [Dependency] private readonly ITileDefinitionManager _tiles = default!; private TileSpawnWindow? _window; diff --git a/Robust.Client/UserInterface/Controls/RichTextLabel.cs b/Robust.Client/UserInterface/Controls/RichTextLabel.cs index 8d406382272..8836b42e3c0 100644 --- a/Robust.Client/UserInterface/Controls/RichTextLabel.cs +++ b/Robust.Client/UserInterface/Controls/RichTextLabel.cs @@ -20,6 +20,7 @@ public class RichTextLabel : Control public RichTextLabel() { IoCManager.InjectDependencies(this); + VerticalAlignment = VAlignment.Center; } public void SetMessage(FormattedMessage message, Type[]? tagsAllowed = null, Color? defaultColor = null) diff --git a/Robust.Client/UserInterface/Controls/TextureRect.cs b/Robust.Client/UserInterface/Controls/TextureRect.cs index f982a16b898..34e21137a3e 100644 --- a/Robust.Client/UserInterface/Controls/TextureRect.cs +++ b/Robust.Client/UserInterface/Controls/TextureRect.cs @@ -48,7 +48,7 @@ public string TexturePath { set { - Texture = IoCManager.Resolve().GetResource(value); + Texture = IoCManager.Resolve().GetResource(value); _texturePath = value; } diff --git a/Robust.Client/UserInterface/CustomControls/DebugMonitorControls/DebugCoordsPanel.cs b/Robust.Client/UserInterface/CustomControls/DebugMonitorControls/DebugCoordsPanel.cs index 5e1783ccc98..a8415381918 100644 --- a/Robust.Client/UserInterface/CustomControls/DebugMonitorControls/DebugCoordsPanel.cs +++ b/Robust.Client/UserInterface/CustomControls/DebugMonitorControls/DebugCoordsPanel.cs @@ -70,6 +70,7 @@ protected override void FrameUpdate(FrameEventArgs args) return; var mapSystem = _entityManager.System(); + var xformSystem = _entityManager.System(); if (_mapManager.TryFindGridAt(mouseWorldMap, out var mouseGridUid, out var mouseGrid)) { @@ -80,7 +81,7 @@ protected override void FrameUpdate(FrameEventArgs args) { mouseGridPos = new EntityCoordinates(_mapManager.GetMapEntityId(mouseWorldMap.MapId), mouseWorldMap.Position); - tile = new TileRef(EntityUid.Invalid, mouseGridPos.ToVector2i(_entityManager, _mapManager), Tile.Empty); + tile = new TileRef(EntityUid.Invalid, mouseGridPos.ToVector2i(_entityManager, _mapManager, xformSystem), Tile.Empty); } var controlHovered = UserInterfaceManager.CurrentlyHovered; @@ -90,35 +91,35 @@ protected override void FrameUpdate(FrameEventArgs args) Mouse Pos: Screen: {mouseScreenPos} {mouseWorldMap} - {mouseGridPos} + {_entityManager.GetNetCoordinates(mouseGridPos)} {tile} GUI: {controlHovered}"); - _textBuilder.AppendLine("\nAttached Entity:"); - var controlledEntity = _playerManager?.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid; + _textBuilder.AppendLine("\nAttached NetEntity:"); + var controlledEntity = _playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid; + if (controlledEntity == EntityUid.Invalid) { - _textBuilder.AppendLine("No attached entity."); + _textBuilder.AppendLine("No attached netentity."); } else { var entityTransform = _entityManager.GetComponent(controlledEntity); - var playerWorldOffset = entityTransform.MapPosition; + var playerWorldOffset = xformSystem.GetMapCoordinates(entityTransform); var playerScreen = _eyeManager.WorldToScreen(playerWorldOffset.Position); var playerCoordinates = entityTransform.Coordinates; - var playerRotation = entityTransform.WorldRotation; + var playerRotation = xformSystem.GetWorldRotation(entityTransform); var gridRotation = entityTransform.GridUid != null - ? _entityManager.GetComponent(entityTransform.GridUid.Value) - .WorldRotation + ? xformSystem.GetWorldRotation(entityTransform.GridUid.Value) : Angle.Zero; _textBuilder.Append($@" Screen: {playerScreen} {playerWorldOffset} - {playerCoordinates} + {_entityManager.GetNetCoordinates(playerCoordinates)} Rotation: {playerRotation.Degrees:F2}° - EntId: {controlledEntity} - GridUid: {entityTransform.GridUid} + NEntId: {_entityManager.GetNetEntity(controlledEntity)} + Grid NEntId: {_entityManager.GetNetEntity(entityTransform.GridUid)} Grid Rotation: {gridRotation.Degrees:F2}°"); } diff --git a/Robust.Client/UserInterface/DevWindow/DevWindow.xaml.cs b/Robust.Client/UserInterface/DevWindow/DevWindow.xaml.cs index b641777f892..2502d3b0e65 100644 --- a/Robust.Client/UserInterface/DevWindow/DevWindow.xaml.cs +++ b/Robust.Client/UserInterface/DevWindow/DevWindow.xaml.cs @@ -28,7 +28,7 @@ private void InitializeComponent() TabContainer.SetTabTitle(Perf, "Profiling"); Stylesheet = - new DefaultStylesheet(IoCManager.Resolve(), IoCManager.Resolve()).Stylesheet; + new DefaultStylesheet(IoCManager.Resolve(), IoCManager.Resolve()).Stylesheet; } } diff --git a/Robust.Client/UserInterface/RichText/BoldItalicTag.cs b/Robust.Client/UserInterface/RichText/BoldItalicTag.cs index c1a0b00af2e..eda24e5c190 100644 --- a/Robust.Client/UserInterface/RichText/BoldItalicTag.cs +++ b/Robust.Client/UserInterface/RichText/BoldItalicTag.cs @@ -9,7 +9,7 @@ public sealed class BoldItalicTag : IMarkupTag { public const string BoldItalicFont = "DefaultBoldItalic"; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public string Name => "bolditalic"; diff --git a/Robust.Client/UserInterface/RichText/BoldTag.cs b/Robust.Client/UserInterface/RichText/BoldTag.cs index 8fb2f39158a..c1e832e563c 100644 --- a/Robust.Client/UserInterface/RichText/BoldTag.cs +++ b/Robust.Client/UserInterface/RichText/BoldTag.cs @@ -10,7 +10,7 @@ public sealed class BoldTag : IMarkupTag { public const string BoldFont = "DefaultBold"; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public string Name => "bold"; diff --git a/Robust.Client/UserInterface/RichText/FontTag.cs b/Robust.Client/UserInterface/RichText/FontTag.cs index 222b9691768..f9d88fb2f74 100644 --- a/Robust.Client/UserInterface/RichText/FontTag.cs +++ b/Robust.Client/UserInterface/RichText/FontTag.cs @@ -16,7 +16,7 @@ public sealed class FontTag : IMarkupTag public const string DefaultFont = "Default"; public const int DefaultSize = 12; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public string Name => "font"; @@ -43,7 +43,7 @@ public void PopDrawContext(MarkupNode node, MarkupDrawingContext context) public static Font CreateFont( Stack contextFontStack, MarkupNode node, - IClientResourceCache cache, + IResourceCache cache, IPrototypeManager prototypeManager, string fontId) { diff --git a/Robust.Client/UserInterface/RichText/HeadingTag.cs b/Robust.Client/UserInterface/RichText/HeadingTag.cs index acd189783c2..4d8c4e784e6 100644 --- a/Robust.Client/UserInterface/RichText/HeadingTag.cs +++ b/Robust.Client/UserInterface/RichText/HeadingTag.cs @@ -8,7 +8,7 @@ namespace Robust.Client.UserInterface.RichText; public sealed class HeadingTag : IMarkupTag { - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public string Name => "head"; diff --git a/Robust.Client/UserInterface/RichText/ItalicTag.cs b/Robust.Client/UserInterface/RichText/ItalicTag.cs index 37eedb6505d..b70cc11fb0f 100644 --- a/Robust.Client/UserInterface/RichText/ItalicTag.cs +++ b/Robust.Client/UserInterface/RichText/ItalicTag.cs @@ -10,7 +10,7 @@ public sealed class ItalicTag : IMarkupTag { public const string ItalicFont = "DefaultItalic"; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public string Name => "italic"; diff --git a/Robust.Client/UserInterface/Stylesheets/DefaultStylesheet.cs b/Robust.Client/UserInterface/Stylesheets/DefaultStylesheet.cs index a1c39f08195..72575887cec 100644 --- a/Robust.Client/UserInterface/Stylesheets/DefaultStylesheet.cs +++ b/Robust.Client/UserInterface/Stylesheets/DefaultStylesheet.cs @@ -12,7 +12,7 @@ public sealed class DefaultStylesheet { public Stylesheet Stylesheet { get; private set; } = default!; - public DefaultStylesheet(IClientResourceCache res, IUserInterfaceManager userInterfaceManager) + public DefaultStylesheet(IResourceCache res, IUserInterfaceManager userInterfaceManager) { var notoSansFont = res.GetResource("/EngineFonts/NotoSans/NotoSans-Regular.ttf"); var notoSansFont12 = new VectorFont(notoSansFont, 12); diff --git a/Robust.Client/UserInterface/Themes/UiTheme.cs b/Robust.Client/UserInterface/Themes/UiTheme.cs index ab97260db99..872f4309ef0 100644 --- a/Robust.Client/UserInterface/Themes/UiTheme.cs +++ b/Robust.Client/UserInterface/Themes/UiTheme.cs @@ -4,7 +4,6 @@ using System.Linq; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; -using Robust.Shared.ContentPack; using Robust.Shared.Graphics; using Robust.Shared.IoC; using Robust.Shared.Log; @@ -19,7 +18,7 @@ namespace Robust.Client.UserInterface.Themes; [Prototype("uiTheme")] public sealed class UITheme : IPrototype { - private IClientResourceCache? _cache; + private IResourceCache? _cache; private IUserInterfaceManager? _uiMan; //this is used for ease of access @@ -38,11 +37,10 @@ public sealed class UITheme : IPrototype public Dictionary? Colors { get; } public ResPath Path => _path == default ? new ResPath(DefaultPath+"/"+ID) : _path; - private void ValidateFilePath(IResourceManager manager) + private void ValidateFilePath(IResourceCache resourceCache) { - var foundFolders = manager.ContentFindFiles(Path.ToRootedPath()); - if (!foundFolders.Any()) - throw new Exception("UITheme: "+ID+" not found in resources!"); + var foundFolders = resourceCache.ContentFindFiles(Path.ToRootedPath()); + if (!foundFolders.Any()) throw new Exception("UITheme: "+ID+" not found in resources!"); } public Texture ResolveTexture(string texturePath) diff --git a/Robust.Client/UserInterface/UserInterfaceManager.cs b/Robust.Client/UserInterface/UserInterfaceManager.cs index 28a3921c146..8c1be1b0612 100644 --- a/Robust.Client/UserInterface/UserInterfaceManager.cs +++ b/Robust.Client/UserInterface/UserInterfaceManager.cs @@ -38,7 +38,7 @@ internal sealed partial class UserInterfaceManager : IUserInterfaceManagerIntern [Dependency] private readonly IFontManager _fontManager = default!; [Dependency] private readonly IClydeInternal _clyde = default!; [Dependency] private readonly IClientGameTiming _gameTiming = default!; - [Dependency] private readonly IClientResourceCache _resourceCache = default!; + [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; diff --git a/Robust.Client/Utility/SpriteSpecifierExt.cs b/Robust.Client/Utility/SpriteSpecifierExt.cs index 178f426ca79..412857a41ef 100644 --- a/Robust.Client/Utility/SpriteSpecifierExt.cs +++ b/Robust.Client/Utility/SpriteSpecifierExt.cs @@ -17,7 +17,7 @@ namespace Robust.Client.Utility /// public static class SpriteSpecifierExt { - public static Texture GetTexture(this SpriteSpecifier.Texture texSpecifier, IClientResourceCache cache) + public static Texture GetTexture(this SpriteSpecifier.Texture texSpecifier, IResourceCache cache) { return cache .GetResource(SpriteSpecifierSerializer.TextureRoot / texSpecifier.TexturePath) @@ -25,7 +25,7 @@ public static Texture GetTexture(this SpriteSpecifier.Texture texSpecifier, ICli } [Obsolete("Use SpriteSystem")] - public static RSI.State GetState(this SpriteSpecifier.Rsi rsiSpecifier, IClientResourceCache cache) + public static RSI.State GetState(this SpriteSpecifier.Rsi rsiSpecifier, IResourceCache cache) { if (!cache.TryGetResource(SpriteSpecifierSerializer.TextureRoot / rsiSpecifier.RsiPath, out var theRsi)) { @@ -56,7 +56,7 @@ public static IDirectionalTextureProvider DirFrame0(this SpriteSpecifier specifi [Obsolete("Use SpriteSystem")] public static IRsiStateLike RsiStateLike(this SpriteSpecifier specifier) { - var resC = IoCManager.Resolve(); + var resC = IoCManager.Resolve(); switch (specifier) { case SpriteSpecifier.Texture tex: diff --git a/Robust.Packaging/AssetProcessing/Passes/AssetPassPackRsis.cs b/Robust.Packaging/AssetProcessing/Passes/AssetPassPackRsis.cs index ffc99c744ec..5b0f04e177c 100644 --- a/Robust.Packaging/AssetProcessing/Passes/AssetPassPackRsis.cs +++ b/Robust.Packaging/AssetProcessing/Passes/AssetPassPackRsis.cs @@ -61,7 +61,7 @@ protected override void AcceptFinished() foreach (var (key, dat) in _foundRsis) { if (dat.MetaJson == null) - return; + continue; RunJob(() => { diff --git a/Robust.Packaging/RobustClientAssetGraph.cs b/Robust.Packaging/RobustClientAssetGraph.cs index 485111c4b64..85a2eca9030 100644 --- a/Robust.Packaging/RobustClientAssetGraph.cs +++ b/Robust.Packaging/RobustClientAssetGraph.cs @@ -21,11 +21,11 @@ public sealed class RobustClientAssetGraph /// public IReadOnlyCollection AllPasses { get; } - public RobustClientAssetGraph() + public RobustClientAssetGraph(bool parallel = true) { // The code injecting the list of source files is assumed to be pretty single-threaded. // We use a parallelizing input to break out all the work on files coming in onto multiple threads. - Input = new AssetPassPipe { Name = "RobustClientAssetGraphInput", Parallelize = true }; + Input = new AssetPassPipe { Name = "RobustClientAssetGraphInput", Parallelize = parallel }; PresetPasses = new AssetPassPipe { Name = "RobustClientAssetGraphPresetPasses" }; Output = new AssetPassPipe { Name = "RobustClientAssetGraphOutput", CheckDuplicates = true }; NormalizeText = new AssetPassNormalizeText { Name = "RobustClientAssetGraphNormalizeText" }; diff --git a/Robust.Packaging/RobustClientPackaging.cs b/Robust.Packaging/RobustClientPackaging.cs index 49a94534474..c23fe4479bb 100644 --- a/Robust.Packaging/RobustClientPackaging.cs +++ b/Robust.Packaging/RobustClientPackaging.cs @@ -4,9 +4,10 @@ namespace Robust.Packaging; public sealed class RobustClientPackaging { - public static IReadOnlySet ClientIgnoresResources { get; } = new HashSet + public static IReadOnlySet ClientIgnoredResources { get; } = new HashSet { "Maps", + "ConfigPresets", // Leaving this here for future archaeologists to ponder at. "emotes.xml", "Groups", @@ -18,48 +19,8 @@ public static async Task WriteClientResources( AssetPass pass, CancellationToken cancel = default) { - var ignoreSet = ClientIgnoresResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet(); + var ignoreSet = ClientIgnoredResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet(); - await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"), pass, ignoreSet, cancel); - } - - public static async Task WriteContentAssemblies( - AssetPass pass, - string contentDir, - string binDir, - IEnumerable contentAssemblies, - CancellationToken cancel = default) - { - await WriteContentAssemblies("Assemblies", pass, contentDir, binDir, contentAssemblies, cancel); - } - - public static Task WriteContentAssemblies( - string target, - AssetPass pass, - string contentDir, - string binDir, - IEnumerable contentAssemblies, - CancellationToken cancel = default) - { - var files = new List(); - - var sourceDir = Path.Combine(contentDir, "bin", binDir); - - foreach (var asm in contentAssemblies) - { - files.Add($"{asm}.dll"); - - var pdbPath = $"{asm}.pdb"; - if (File.Exists(Path.Combine(sourceDir, pdbPath))) - files.Add(pdbPath); - } - - foreach (var f in files) - { - cancel.ThrowIfCancellationRequested(); - pass.InjectFileFromDisk($"{target}/{f}", Path.Combine(sourceDir, f)); - } - - return Task.CompletedTask; + await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"), pass, ignoreSet, cancel: cancel); } } diff --git a/Robust.Packaging/RobustServerPackaging.cs b/Robust.Packaging/RobustServerPackaging.cs new file mode 100644 index 00000000000..a53bad809b2 --- /dev/null +++ b/Robust.Packaging/RobustServerPackaging.cs @@ -0,0 +1,33 @@ +using Robust.Packaging.AssetProcessing; + +namespace Robust.Packaging; + +public sealed class RobustServerPackaging +{ + public static IReadOnlySet ServerIgnoresResources { get; } = new HashSet + { + "Audio", + "Textures", + "Fonts", + "Shaders", + }; + + public static async Task WriteServerResources( + string contentDir, + AssetPass pass, + CancellationToken cancel = default) + { + var ignoreSet = ServerIgnoresResources.Union(RobustSharedPackaging.SharedIgnoredResources).ToHashSet(); + + await RobustSharedPackaging.DoResourceCopy(Path.Combine(contentDir, "Resources"), + pass, + ignoreSet, + "Resources", + cancel); + await RobustSharedPackaging.DoResourceCopy(Path.Combine("RobustToolbox", "Resources"), + pass, + ignoreSet, + "Resources", + cancel); + } +} diff --git a/Robust.Packaging/RobustSharedPackaging.cs b/Robust.Packaging/RobustSharedPackaging.cs index 6644ad5c7c1..a2a23bf7820 100644 --- a/Robust.Packaging/RobustSharedPackaging.cs +++ b/Robust.Packaging/RobustSharedPackaging.cs @@ -15,10 +15,53 @@ public sealed class RobustSharedPackaging ".DS_Store" }; + // IDK what these are supposed to correspond to but targetDir is the target directory. + public static async Task WriteContentAssemblies( + AssetPass pass, + string contentDir, + string binDir, + IEnumerable contentAssemblies, + string targetDir = "Assemblies", + CancellationToken cancel = default) + { + await WriteContentAssemblies(targetDir, pass, contentDir, binDir, contentAssemblies, cancel); + } + + public static Task WriteContentAssemblies( + string target, + AssetPass pass, + string contentDir, + string binDir, + IEnumerable contentAssemblies, + CancellationToken cancel = default) + { + var files = new List(); + + var sourceDir = Path.Combine(contentDir, "bin", binDir); + + foreach (var asm in contentAssemblies) + { + files.Add($"{asm}.dll"); + + var pdbPath = $"{asm}.pdb"; + if (File.Exists(Path.Combine(sourceDir, pdbPath))) + files.Add(pdbPath); + } + + foreach (var f in files) + { + cancel.ThrowIfCancellationRequested(); + pass.InjectFileFromDisk($"{target}/{f}", Path.Combine(sourceDir, f)); + } + + return Task.CompletedTask; + } + public static Task DoResourceCopy( string diskSource, AssetPass pass, IReadOnlySet ignoreSet, + string targetDir = "", CancellationToken cancel = default) { foreach (var path in Directory.EnumerateFileSystemEntries(diskSource)) @@ -29,7 +72,7 @@ public static Task DoResourceCopy( if (ignoreSet.Contains(filename)) continue; - var targetPath = filename; + var targetPath = Path.Combine(targetDir, filename); if (Directory.Exists(path)) CopyDirIntoZip(path, targetPath, pass); else @@ -44,11 +87,11 @@ private static void CopyDirIntoZip(string directory, string basePath, AssetPass foreach (var file in Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)) { var relPath = Path.GetRelativePath(directory, file); - if (Path.DirectorySeparatorChar != '/') - relPath = relPath.Replace(Path.DirectorySeparatorChar, '/'); - var zipPath = $"{basePath}/{relPath}"; + if (Path.DirectorySeparatorChar != '/') + zipPath = zipPath.Replace(Path.DirectorySeparatorChar, '/'); + // Console.WriteLine($"{directory}/{zipPath} -> /{zipPath}"); pass.InjectFileFromDisk(zipPath, file); } diff --git a/Robust.Server/Audio/AudioSystem.Effects.cs b/Robust.Server/Audio/AudioSystem.Effects.cs deleted file mode 100644 index ea14d71164c..00000000000 --- a/Robust.Server/Audio/AudioSystem.Effects.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Shared.Audio.Components; -using Robust.Shared.Audio.Effects; -using Robust.Shared.Collections; -using Robust.Shared.GameObjects; - -namespace Robust.Server.Audio; - -public sealed partial class AudioSystem -{ - protected override void InitializeEffect() - { - base.InitializeEffect(); - SubscribeLocalEvent(OnEffectAdd); - SubscribeLocalEvent(OnAuxiliaryAdd); - } - - private void ShutdownEffect() - { - } - - /// - /// Reloads all entities. - /// - public void ReloadPresets() - { - var query = AllEntityQuery(); - var toDelete = new ValueList(); - - while (query.MoveNext(out var uid, out _)) - { - toDelete.Add(uid); - } - - foreach (var ent in toDelete) - { - Del(ent); - } - - foreach (var proto in ProtoMan.EnumeratePrototypes()) - { - if (!proto.CreateAuxiliary) - continue; - - var effect = CreateEffect(); - var aux = CreateAuxiliary(); - SetEffectPreset(effect.Entity, effect.Component, proto); - SetEffect(aux.Entity, aux.Component, effect.Entity); - var preset = AddComp(aux.Entity); - _auxiliaries.Remove(preset.Preset); - preset.Preset = proto.ID; - _auxiliaries[preset.Preset] = aux.Entity; - } - } - - private void OnEffectAdd(EntityUid uid, AudioEffectComponent component, ComponentAdd args) - { - component.Effect = new DummyAudioEffect(); - } - - private void OnAuxiliaryAdd(EntityUid uid, AudioAuxiliaryComponent component, ComponentAdd args) - { - component.Auxiliary = new DummyAuxiliaryAudio(); - } - - public override (EntityUid Entity, AudioAuxiliaryComponent Component) CreateAuxiliary() - { - var (ent, comp) = base.CreateAuxiliary(); - _pvs.AddGlobalOverride(GetNetEntity(ent)); - return (ent, comp); - } - - public override (EntityUid Entity, AudioEffectComponent Component) CreateEffect() - { - var (ent, comp) = base.CreateEffect(); - _pvs.AddGlobalOverride(GetNetEntity(ent)); - return (ent, comp); - } -} diff --git a/Robust.Server/Audio/AudioSystem.cs b/Robust.Server/Audio/AudioSystem.cs deleted file mode 100644 index b7604667d89..00000000000 --- a/Robust.Server/Audio/AudioSystem.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Collections.Generic; -using System.Numerics; -using Robust.Server.GameObjects; -using Robust.Server.GameStates; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Components; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Player; -using Robust.Shared.Utility; - -namespace Robust.Server.Audio; - -public sealed partial class AudioSystem : SharedAudioSystem -{ - [Dependency] private readonly PvsOverrideSystem _pvs = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnAudioStartup); - } - - public override void Shutdown() - { - base.Shutdown(); - ShutdownEffect(); - } - - private void OnAudioStartup(EntityUid uid, AudioComponent component, ComponentStartup args) - { - component.Source = new DummyAudioSource(); - } - - private void AddAudioFilter(EntityUid uid, AudioComponent component, Filter filter) - { - var count = filter.Count; - - if (count == 0) - return; - - var nent = GetNetEntity(uid); - _pvs.AddSessionOverrides(nent, filter); - - var ents = new HashSet(count); - - foreach (var session in filter.Recipients) - { - var ent = session.AttachedEntity; - - if (ent == null) - continue; - - ents.Add(ent.Value); - } - - DebugTools.Assert(component.IncludedEntities == null); - component.IncludedEntities = ents; - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) - { - var entity = Spawn("Audio", MapCoordinates.Nullspace); - var audio = SetupAudio(entity, filename, audioParams); - AddAudioFilter(entity, audio, playerFilter); - - return (entity, audio); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null) - { - if (!Exists(uid)) - return null; - - var entity = Spawn("Audio", new EntityCoordinates(uid, Vector2.Zero)); - var audio = SetupAudio(entity, filename, audioParams); - AddAudioFilter(entity, audio, playerFilter); - - return (entity, audio); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityUid uid, AudioParams? audioParams = null) - { - if (!Exists(uid)) - return null; - - var entity = Spawn("Audio", new EntityCoordinates(uid, Vector2.Zero)); - var audio = SetupAudio(entity, filename, audioParams); - - return (entity, audio); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) - { - if (!coordinates.IsValid(EntityManager)) - return null; - - var entity = Spawn("Audio", coordinates); - var audio = SetupAudio(entity, filename, audioParams); - AddAudioFilter(entity, audio, playerFilter); - - return (entity, audio); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string filename, EntityCoordinates coordinates, - AudioParams? audioParams = null) - { - if (!coordinates.IsValid(EntityManager)) - return null; - - var entity = Spawn("Audio", coordinates); - var audio = SetupAudio(entity, filename, audioParams); - - return (entity, audio); - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(SoundSpecifier? sound, EntityUid source, EntityUid? user, AudioParams? audioParams = null) - { - if (sound == null) - return null; - - var audio = PlayPvs(GetSound(sound), source, audioParams ?? sound.Params); - - if (audio == null) - return null; - - audio.Value.Component.ExcludedEntity = user; - return audio; - } - - /// - public override (EntityUid Entity, AudioComponent Component)? PlayPredicted(SoundSpecifier? sound, EntityCoordinates coordinates, EntityUid? user, AudioParams? audioParams = null) - { - if (sound == null) - return null; - - var audio = PlayPvs(GetSound(sound), coordinates, audioParams ?? sound.Params); - - if (audio == null) - return null; - - audio.Value.Component.ExcludedEntity = user; - return audio; - } - - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null) - { - return PlayGlobal(filename, Filter.SinglePlayer(recipient), false, audioParams); - } - - public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null) - { - if (TryComp(recipient, out ActorComponent? actor)) - return PlayGlobal(filename, actor.PlayerSession, audioParams); - - return null; - } - - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) - { - return PlayEntity(filename, Filter.SinglePlayer(recipient), uid, false, audioParams); - } - - public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) - { - if (TryComp(recipient, out ActorComponent? actor)) - return PlayEntity(filename, actor.PlayerSession, uid, audioParams); - - return null; - } - - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - return PlayStatic(filename, Filter.SinglePlayer(recipient), coordinates, false, audioParams); - } - - public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - if (TryComp(recipient, out ActorComponent? actor)) - return PlayStatic(filename, actor.PlayerSession, coordinates, audioParams); - - return null; - } -} diff --git a/Robust.Server/BaseServer.cs b/Robust.Server/BaseServer.cs index aa2268f0389..e4d4a0f5462 100644 --- a/Robust.Server/BaseServer.cs +++ b/Robust.Server/BaseServer.cs @@ -660,10 +660,14 @@ public void Cleanup() { // Write down exception log var logPath = _config.GetCVar(CVars.LogPath); - var relPath = PathHelpers.ExecutableRelativeFile(logPath); - Directory.CreateDirectory(relPath); - var pathToWrite = Path.Combine(relPath, + if (!Path.IsPathRooted(logPath)) + { + logPath = PathHelpers.ExecutableRelativeFile(logPath); + } + + var pathToWrite = Path.Combine(logPath, "Runtime-" + DateTime.Now.ToString("yyyy-MM-dd-THH-mm-ss") + ".txt"); + Directory.CreateDirectory(logPath); File.WriteAllText(pathToWrite, _runtimeLog.Display(), EncodingHelpers.UTF8); } diff --git a/Robust.Server/GameObjects/EntitySystems/ActorSystem.cs b/Robust.Server/GameObjects/EntitySystems/ActorSystem.cs deleted file mode 100644 index 4b5584d8098..00000000000 --- a/Robust.Server/GameObjects/EntitySystems/ActorSystem.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using JetBrains.Annotations; -using Robust.Server.Player; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Network; -using Robust.Shared.Player; -using Robust.Shared.Utility; - -namespace Robust.Server.GameObjects -{ - /// - /// System that handles players being attached/detached from entities. - /// - [UsedImplicitly] - public sealed class ActorSystem : EntitySystem - { - [Dependency] private readonly IPlayerManager _playerManager = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnActorShutdown); - } - - /// - /// Attaches a player session to an entity, optionally kicking any sessions already attached to it. - /// - /// The entity to attach the player to - /// The player to attach to the entity - /// Whether to kick any existing players from the entity - /// Whether the attach succeeded, or not. - public bool Attach(EntityUid? uid, ICommonSession player, bool force = false) - { - return Attach(uid, player, false, out _); - } - - /// - /// Attaches a player session to an entity, optionally kicking any sessions already attached to it. - /// - /// The entity to attach the player to - /// The player to attach to the entity - /// Whether to kick any existing players from the entity - /// The player that was forcefully kicked, or null. - /// Whether the attach succeeded, or not. - public bool Attach(EntityUid? entity, ICommonSession player, bool force, out ICommonSession? forceKicked) - { - // Null by default. - forceKicked = null; - - if (player.AttachedEntity == entity) - { - DebugTools.Assert(entity == null || HasComp(entity)); - return true; - } - - if (entity is not { } uid) - return Detach(player); - - // Cannot attach to a deleted, nonexisting or terminating entity. - if (TerminatingOrDeleted(uid)) - return false; - - // Check if there was a player attached to the entity already... - if (TryComp(uid, out ActorComponent? actor)) - { - // If we're not forcing the attach, this fails. - if (!force) - return false; - - // Set the event's force-kicked session before detaching it. - forceKicked = actor.PlayerSession; - RemComp(uid, actor); - DebugTools.AssertNull(forceKicked.AttachedEntity); - } - - // Detach from the currently attached entity. - Detach(player); - - // We add the actor component. - actor = EntityManager.AddComponent(uid); - EntityManager.EnsureComponent(uid); - actor.PlayerSession = player; - _playerManager.SetAttachedEntity(player, uid); - DebugTools.Assert(player.AttachedEntity == entity); - - // The player is fully attached now, raise an event! - RaiseLocalEvent(uid, new PlayerAttachedEvent(uid, player, forceKicked), true); - return true; - } - - /// - /// Detaches an attached session from the entity, if any. - /// - /// The entity player sessions will be detached from. - /// Whether any player session was detached. - public bool Detach(EntityUid uid, ActorComponent? actor = null) - { - if (!Resolve(uid, ref actor, false)) - return false; - - RemComp(uid, actor); - return true; - } - - /// - /// Detaches this player from its attached entity, if any. - /// - /// The player session that will be detached from any attached entities. - /// Whether the player is now detached from any entities. - /// This returns true if the player wasn't attached to any entity. - public bool Detach(ICommonSession player, ActorComponent? actor = null) - { - var uid = player.AttachedEntity; - if (uid == null) - return true; - - if (!Resolve(uid.Value, ref actor, false)) - { - Log.Error($"Player {player} was attached to a deleted entity?"); - ((CommonSession) player).AttachedEntity = null; - return true; - } - - RemComp(uid.Value, actor); - DebugTools.AssertNull(player.AttachedEntity); - return false; - } - - private void OnActorShutdown(EntityUid entity, ActorComponent component, ComponentShutdown args) - { - _playerManager.SetAttachedEntity(component.PlayerSession, null); - - // The player is fully detached now that the component has shut down. - RaiseLocalEvent(entity, new PlayerDetachedEvent(entity, component.PlayerSession), true); - } - - public bool TryGetActorFromUserId(NetUserId? userId, [NotNullWhen(true)] out ICommonSession? actor, out EntityUid? actorEntity) - { - actor = null; - actorEntity = null; - if (userId != null) - { - if (!_playerManager.TryGetSessionById(userId.Value, out actor)) - return false; - actorEntity = actor.AttachedEntity; - } - - return actor != null; - } - } - - /// - /// Event for when a player has been attached to an entity. - /// - public sealed class PlayerAttachedEvent : EntityEventArgs - { - public EntityUid Entity { get; } - public ICommonSession Player { get; } - - /// - /// The player session that was forcefully kicked from the entity, if any. - /// - public ICommonSession? Kicked { get; } - - public PlayerAttachedEvent(EntityUid entity, ICommonSession player, ICommonSession? kicked = null) - { - Entity = entity; - Player = player; - Kicked = kicked; - } - } - - /// - /// Event for when a player has been detached from an entity. - /// - public sealed class PlayerDetachedEvent : EntityEventArgs - { - public EntityUid Entity { get; } - public ICommonSession Player { get; } - - public PlayerDetachedEvent(EntityUid entity, ICommonSession player) - { - Entity = entity; - Player = player; - } - } -} diff --git a/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs b/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs new file mode 100644 index 00000000000..c917fc1e569 --- /dev/null +++ b/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs @@ -0,0 +1,176 @@ +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Player; + +namespace Robust.Server.GameObjects; +[UsedImplicitly] +public sealed class AudioSystem : SharedAudioSystem +{ + [Dependency] private readonly TransformSystem _transform = default!; + + + private uint _streamIndex; + + private sealed class AudioSourceServer : IPlayingAudioStream + { + private readonly uint _id; + private readonly AudioSystem _audioSystem; + private readonly IEnumerable? _sessions; + + internal AudioSourceServer(AudioSystem parent, uint identifier, IEnumerable? sessions = null) + { + _audioSystem = parent; + _id = identifier; + _sessions = sessions; + } + public void Stop() + { + _audioSystem.InternalStop(_id, _sessions); + } + } + + private void InternalStop(uint id, IEnumerable? 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++); + } + + /// + public override IPlayingAudioStream? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) + { + var id = CacheIdentifier(); + var msg = new PlayAudioGlobalMessage + { + FileName = filename, + AudioParams = audioParams ?? AudioParams.Default, + Identifier = id + }; + + RaiseNetworkEvent(msg, playerFilter, recordReplay); + + return new AudioSourceServer(this, id, playerFilter.Recipients.ToArray()); + } + + public override IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null) + { + if(!EntityManager.TryGetComponent(uid, out var transform)) + return null; + + var id = CacheIdentifier(); + + var fallbackCoordinates = GetFallbackCoordinates(transform.MapPosition); + + var msg = new PlayAudioEntityMessage + { + FileName = filename, + Coordinates = GetNetCoordinates(transform.Coordinates), + FallbackCoordinates = GetNetCoordinates(fallbackCoordinates), + NetEntity = GetNetEntity(uid), + AudioParams = audioParams ?? AudioParams.Default, + Identifier = id, + }; + + RaiseNetworkEvent(msg, playerFilter, recordReplay); + + return new AudioSourceServer(this, id, playerFilter.Recipients.ToArray()); + } + + /// + public override IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) + { + var id = CacheIdentifier(); + + var fallbackCoordinates = GetFallbackCoordinates(coordinates.ToMap(EntityManager, _transform)); + + var msg = new PlayAudioPositionalMessage + { + FileName = filename, + Coordinates = GetNetCoordinates(coordinates), + FallbackCoordinates = GetNetCoordinates(fallbackCoordinates), + AudioParams = audioParams ?? AudioParams.Default, + Identifier = id + }; + + RaiseNetworkEvent(msg, playerFilter, recordReplay); + + return new AudioSourceServer(this, id, playerFilter.Recipients.ToArray()); + } + + /// + public override IPlayingAudioStream? PlayPredicted(SoundSpecifier? sound, EntityUid source, EntityUid? user, AudioParams? audioParams = null) + { + if (sound == null) + return null; + + var filter = Filter.Pvs(source, entityManager: EntityManager, playerManager: PlayerManager, cfgManager: CfgManager).RemoveWhereAttachedEntity(e => e == user); + return Play(sound, filter, source, true, audioParams); + } + + public override IPlayingAudioStream? PlayPredicted(SoundSpecifier? sound, EntityCoordinates coordinates, EntityUid? user, + AudioParams? audioParams = null) + { + if (sound == null) + return null; + + var filter = Filter.Pvs(coordinates, entityMan: EntityManager, playerMan: PlayerManager).RemoveWhereAttachedEntity(e => e == user); + return Play(sound, filter, coordinates, true, audioParams); + } + + public override IPlayingAudioStream? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null) + { + return PlayGlobal(filename, Filter.SinglePlayer(recipient), false, audioParams); + } + + public override IPlayingAudioStream? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null) + { + if (TryComp(recipient, out ActorComponent? actor)) + return PlayGlobal(filename, actor.PlayerSession, audioParams); + return null; + } + + public override IPlayingAudioStream? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) + { + return Play(filename, Filter.SinglePlayer(recipient), uid, false, audioParams); + } + + public override IPlayingAudioStream? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) + { + if (TryComp(recipient, out ActorComponent? actor)) + return PlayEntity(filename, actor.PlayerSession, uid, audioParams); + return null; + } + + public override IPlayingAudioStream? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + return Play(filename, Filter.SinglePlayer(recipient), coordinates, false, audioParams); + } + + public override IPlayingAudioStream? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + if (TryComp(recipient, out ActorComponent? actor)) + return PlayStatic(filename, actor.PlayerSession, coordinates, audioParams); + return null; + } +} diff --git a/Robust.Server/GameObjects/EntitySystems/EyeSystem.cs b/Robust.Server/GameObjects/EntitySystems/EyeSystem.cs index f9d19c48910..4f9e01d659d 100644 --- a/Robust.Server/GameObjects/EntitySystems/EyeSystem.cs +++ b/Robust.Server/GameObjects/EntitySystems/EyeSystem.cs @@ -1,5 +1,4 @@ using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; namespace Robust.Server.GameObjects; diff --git a/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs b/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs index a17af19a693..6c3f40dd694 100644 --- a/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs +++ b/Robust.Server/GameObjects/EntitySystems/MapLoaderSystem.cs @@ -296,6 +296,9 @@ private bool Deserialize(MapData data) ReadGrids(data); + // grids prior to engine v175 might've been serialized with empty chunks which now throw debug asserts. + RemoveEmptyChunks(data); + // Then, go hierarchically in order and do the entity things. StartupEntities(data); @@ -311,6 +314,25 @@ private bool Deserialize(MapData data) return true; } + private void RemoveEmptyChunks(MapData data) + { + var gridQuery = _serverEntityManager.GetEntityQuery(); + foreach (var uid in data.EntitiesToDeserialize.Keys) + { + if (!gridQuery.TryGetComponent(uid, out var gridComp)) + continue; + + foreach (var (index, chunk) in gridComp.Chunks) + { + if (chunk.FilledTiles > 0) + continue; + + Log.Warning($"Encountered empty chunk while deserializing map. Grid: {ToPrettyString(uid)}. Chunk index: {index}"); + gridComp.Chunks.Remove(index); + } + } + } + private bool VerifyEntitiesExist(MapData data, BeforeEntityReadEvent ev) { _stopwatch.Restart(); diff --git a/Robust.Server/GameObjects/EntitySystems/ServerMetaDataSystem.cs b/Robust.Server/GameObjects/EntitySystems/ServerMetaDataSystem.cs index 9eea4f661ac..91b7eb995b0 100644 --- a/Robust.Server/GameObjects/EntitySystems/ServerMetaDataSystem.cs +++ b/Robust.Server/GameObjects/EntitySystems/ServerMetaDataSystem.cs @@ -1,6 +1,7 @@ using Robust.Server.GameStates; using Robust.Shared.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Player; namespace Robust.Server.GameObjects; diff --git a/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs b/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs index 6d96e6df350..2861d7301ff 100644 --- a/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs +++ b/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using JetBrains.Annotations; using Robust.Server.Player; @@ -21,8 +20,6 @@ public sealed class UserInterfaceSystem : SharedUserInterfaceSystem private readonly List _sessionCache = new(); - private readonly Dictionary> _openInterfaces = new(); - /// public override void Initialize() { @@ -46,7 +43,7 @@ private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs args) if (args.NewStatus != SessionStatus.Disconnected) return; - if (!_openInterfaces.TryGetValue(args.Session, out var buis)) + if (!OpenInterfaces.TryGetValue(args.Session, out var buis)) return; foreach (var bui in buis.ToArray()) @@ -92,7 +89,7 @@ public override void Update(float frameTime) /// /// Verify that the subscribed clients are still in range of the interface. /// - private void CheckRange(EntityUid uid, Shared.GameObjects.ActiveUserInterfaceComponent activeUis, PlayerBoundUserInterface ui, TransformComponent transform, EntityQuery query) + private void CheckRange(EntityUid uid, ActiveUserInterfaceComponent activeUis, PlayerBoundUserInterface ui, TransformComponent transform, EntityQuery query) { if (ui.InteractionRange <= 0) return; @@ -139,11 +136,6 @@ private void CheckRange(EntityUid uid, Shared.GameObjects.ActiveUserInterfaceCom } } - private void ActivateInterface(PlayerBoundUserInterface ui) - { - EnsureComp(ui.Owner).Interfaces.Add(ui); - } - #region Get BUI public bool HasUi(EntityUid uid, Enum uiKey, UserInterfaceComponent? ui = null) @@ -168,20 +160,14 @@ public PlayerBoundUserInterface GetUi(EntityUid uid, Enum uiKey, UserInterfaceCo ? bui : null; } - public bool TryGetUi(EntityUid uid, Enum uiKey, [NotNullWhen(true)] out PlayerBoundUserInterface? bui, UserInterfaceComponent? ui = null) - { - bui = null; - - return Resolve(uid, ref ui, false) && ui.Interfaces.TryGetValue(uiKey, out bui); - } /// /// Return UIs a session has open. /// Null if empty. - /// + /// public List? GetAllUIsForSession(ICommonSession session) { - _openInterfaces.TryGetValue(session, out var value); + OpenInterfaces.TryGetValue(session, out var value); return value; } #endregion @@ -259,94 +245,14 @@ public void SetUiState(PlayerBoundUserInterface bui, BoundUserInterfaceState sta bui.StateDirty = true; } - /// - /// Switches between closed and open for a specific client. - /// - public bool TryToggleUi(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null) - { - if (!TryGetUi(uid, uiKey, out var bui, ui)) - return false; - - ToggleUi(bui, session); - return true; - } - - /// - /// Switches between closed and open for a specific client. - /// - public void ToggleUi(PlayerBoundUserInterface bui, ICommonSession session) - { - if (bui._subscribedSessions.Contains(session)) - CloseUi(bui, session); - else - OpenUi(bui, session); - } - - #region Open - - public bool TryOpen(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null) - { - if (!TryGetUi(uid, uiKey, out var bui, ui)) - return false; - - return OpenUi(bui, session); - } - - /// - /// Opens this interface for a specific client. - /// - public bool OpenUi(PlayerBoundUserInterface bui, ICommonSession session) - { - if (session.Status == SessionStatus.Connecting || session.Status == SessionStatus.Disconnected) - return false; - - if (!bui._subscribedSessions.Add(session)) - return false; - - _openInterfaces.GetOrNew(session).Add(bui); - RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session)); - - RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient); - - // Fun fact, clients needs to have BUIs open before they can receive the state..... - if (bui.LastStateMsg != null) - RaiseNetworkEvent(bui.LastStateMsg, session.ConnectedClient); - - ActivateInterface(bui); - return true; - } - - #endregion - #region Close - public bool TryClose(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null) - { - if (!TryGetUi(uid, uiKey, out var bui, ui)) - return false; - - return CloseUi(bui, session); - } - - /// - /// Close this interface for a specific client. - /// - public bool CloseUi(PlayerBoundUserInterface bui, ICommonSession session, ActiveUserInterfaceComponent? activeUis = null) - { - if (!bui._subscribedSessions.Remove(session)) - return false; - - RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient); - CloseShared(bui, session, activeUis); - return true; - } - protected override void CloseShared(PlayerBoundUserInterface bui, ICommonSession session, ActiveUserInterfaceComponent? activeUis = null) { var owner = bui.Owner; bui._subscribedSessions.Remove(session); bui.PlayerStateOverrides.Remove(session); - if (_openInterfaces.TryGetValue(session, out var buis)) + if (OpenInterfaces.TryGetValue(session, out var buis)) buis.Remove(bui); RaiseLocalEvent(owner, new BoundUIClosedEvent(bui.UiKey, owner, session)); diff --git a/Robust.Server/GameStates/PvsOverrideSystem.cs b/Robust.Server/GameStates/PvsOverrideSystem.cs index ddef2110ce0..bd022a688a3 100644 --- a/Robust.Server/GameStates/PvsOverrideSystem.cs +++ b/Robust.Server/GameStates/PvsOverrideSystem.cs @@ -16,38 +16,30 @@ public sealed class PvsOverrideSystem : EntitySystem /// /// Whether or not to supersede existing overrides. /// If true, this will also recursively send any children of the given index. - public void AddGlobalOverride(NetEntity entity, bool removeExistingOverride = true, bool recursive = false) + public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false) { - _pvs.EntityPVSCollection.AddGlobalOverride(entity, removeExistingOverride, recursive); + _pvs.EntityPVSCollection.AddGlobalOverride(GetNetEntity(uid), removeExistingOverride, recursive); } /// - /// Used to ensure that an entity is always sent to a specific client. Overrides any global or pre-existing - /// client-specific overrides. + /// Used to ensure that an entity is always sent to a specific client. By default this overrides any global or pre-existing + /// client-specific overrides. Unlike global overrides, this is always recursive. /// /// Whether or not to supersede existing overrides. - public void AddSessionOverride(NetEntity entity, ICommonSession session, bool removeExistingOverride = true) - { - _pvs.EntityPVSCollection.AddSessionOverride(entity, session, removeExistingOverride); - } - - // 'placeholder' - public void AddSessionOverrides(NetEntity entity, Filter filter, bool removeExistingOverride = true) + /// If true, this will also recursively send any children of the given index. + public void AddSessionOverride(EntityUid uid, ICommonSession session, bool removeExistingOverride = true) { - foreach (var player in filter.Recipients) - { - AddSessionOverride(entity, player, removeExistingOverride); - } + _pvs.EntityPVSCollection.AddSessionOverride(GetNetEntity(uid), session, removeExistingOverride); } /// /// Removes any global or client-specific overrides. /// - public void ClearOverride(NetEntity entity, TransformComponent? xform = null) + public void ClearOverride(EntityUid uid, TransformComponent? xform = null) { - if (!TryGetEntity(entity, out var uid) || !Resolve(uid.Value, ref xform)) + if (!Resolve(uid, ref xform)) return; - _pvs.EntityPVSCollection.UpdateIndex(entity, xform.Coordinates, true); + _pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), xform.Coordinates, true); } } diff --git a/Robust.Server/GameStates/PvsSystem.cs b/Robust.Server/GameStates/PvsSystem.cs index 186f792d73c..a70d164588f 100644 --- a/Robust.Server/GameStates/PvsSystem.cs +++ b/Robust.Server/GameStates/PvsSystem.cs @@ -888,7 +888,7 @@ private void AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, return null; var tick = _gameTiming.CurTick; - var minSize = Math.Max(0, lastSent.Count - lastSent.Count); + var minSize = Math.Max(0, lastSent.Count - visibleEnts.Count); var leftView = new List(minSize); foreach (var netEntity in lastSent.Keys) diff --git a/Robust.Server/GameStates/ServerGameStateManager.cs b/Robust.Server/GameStates/ServerGameStateManager.cs index 2c67cb5dce3..1f1db6a3ce3 100644 --- a/Robust.Server/GameStates/ServerGameStateManager.cs +++ b/Robust.Server/GameStates/ServerGameStateManager.cs @@ -378,15 +378,10 @@ private void SendStateUpdate(int i, if (_gameTiming.CurTick.Value > lastAck.Value + _pvs.ForceAckThreshold) { stateUpdateMessage.ForceSendReliably = true; - - // Aside from the time shortly after connecting, this shouldn't be common. If it is happening. - // something is probably wrong (or we have a malicious client). Hence we log an error. - // If it is more frequent than I think, this can be downgraded to a warning. - #if FULL_RELEASE var connectedTime = (DateTime.UtcNow - session.ConnectedTime).TotalMinutes; if (lastAck > GameTick.Zero && connectedTime > 1) - _logger.Error($"Client {session} exceeded ack-tick threshold. Last ack: {lastAck}. Cur tick: {_gameTiming.CurTick}. Connect time: {connectedTime} minutes"); + _logger.Warning($"Client {session} exceeded ack-tick threshold. Last ack: {lastAck}. Cur tick: {_gameTiming.CurTick}. Connect time: {connectedTime} minutes"); #endif } diff --git a/Robust.Server/Graphics/ClydeHandle.cs b/Robust.Server/Graphics/ClydeHandle.cs deleted file mode 100644 index 313867fa34d..00000000000 --- a/Robust.Server/Graphics/ClydeHandle.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Robust.Shared.Graphics; - -namespace Robust.Server.Graphics; - -public struct ClydeHandle : IClydeHandle -{ - public long Value => -1; -} diff --git a/Robust.Server/Maps/MapChunkSerializer.cs b/Robust.Server/Maps/MapChunkSerializer.cs index dff28e56130..d06e45679b5 100644 --- a/Robust.Server/Maps/MapChunkSerializer.cs +++ b/Robust.Server/Maps/MapChunkSerializer.cs @@ -12,6 +12,7 @@ using Robust.Shared.Serialization.Markdown.Validation; using Robust.Shared.Serialization.Markdown.Value; using Robust.Shared.Serialization.TypeSerializers.Interfaces; +using Robust.Shared.Utility; namespace Robust.Server.Maps; @@ -93,6 +94,7 @@ public DataNode Write(ISerializationManager serializationManager, MapChunk value IDependencyCollection dependencies, bool alwaysWrite = false, ISerializationContext? context = null) { + DebugTools.Assert(value.FilledTiles > 0, "Attempting to write an empty chunk"); var root = new MappingDataNode(); var ind = new ValueDataNode($"{value.X},{value.Y}"); root.Add("ind", ind); diff --git a/Robust.Server/Placement/PlacementManager.cs b/Robust.Server/Placement/PlacementManager.cs index b9a0fc4fb4e..ec68bc9dd99 100644 --- a/Robust.Server/Placement/PlacementManager.cs +++ b/Robust.Server/Placement/PlacementManager.cs @@ -31,6 +31,10 @@ public sealed class PlacementManager : IPlacementManager [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ILogManager _logManager = default!; + private EntityLookupSystem _lookup => _entityManager.System(); + private SharedMapSystem _maps => _entityManager.System(); + private SharedTransformSystem _xformSystem => _entityManager.System(); + //TO-DO: Expand for multiple permission per mob? // Add support for multi-use placeables (tiles etc.). public List BuildPermissions { get; set; } = new(); @@ -45,6 +49,7 @@ public sealed class PlacementManager : IPlacementManager public void Initialize() { + // Someday PlacementManagerSystem my beloved. _sawmill = _logManager.GetSawmill("placement"); _networkManager.RegisterNetMessage(HandleNetMessage); @@ -143,7 +148,7 @@ public void HandlePlacementRequest(MsgPlacement msg) if (_entityManager.TryGetComponent(gridUid, out var grid)) { var replacementQuery = _entityManager.GetEntityQuery(); - var anc = grid.GetAnchoredEntitiesEnumerator(grid.LocalToTile(coordinates)); + var anc = _maps.GetAnchoredEntitiesEnumerator(gridUid.Value, grid, _maps.LocalToTile(gridUid.Value, grid, coordinates)); var toDelete = new ValueList(); while (anc.MoveNext(out var ent)) @@ -186,14 +191,11 @@ private void PlaceNewTile(int tileType, EntityCoordinates coordinates, NetUserId MapGridComponent? grid; - _mapManager.TryGetGrid(coordinates.EntityId, out grid); - - if (grid == null) - _mapManager.TryFindGridAt(coordinates.ToMap(_entityManager), out _, out grid); - - if (grid != null) // stick to existing grid + EntityUid gridId = coordinates.EntityId; + if (_entityManager.TryGetComponent(coordinates.EntityId, out grid) + || _mapManager.TryFindGridAt(coordinates.ToMap(_entityManager, _xformSystem), out gridId, out grid)) { - grid.SetTile(coordinates, new Tile(tileType)); + _maps.SetTile(gridId, grid, coordinates, new Tile(tileType)); var placementEraseEvent = new PlacementTileEvent(tileType, coordinates, placingUserId); _entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent); @@ -202,9 +204,9 @@ private void PlaceNewTile(int tileType, EntityCoordinates coordinates, NetUserId { var newGrid = _mapManager.CreateGridEntity(coordinates.GetMapId(_entityManager)); var newGridXform = _entityManager.GetComponent(newGrid); - newGridXform.WorldPosition = coordinates.Position - newGrid.Comp.TileSizeHalfVector; // assume bottom left tile origin + _xformSystem.SetWorldPosition(newGridXform, coordinates.Position - newGrid.Comp.TileSizeHalfVector); // assume bottom left tile origin var tilePos = newGrid.Comp.WorldToTile(coordinates.Position); - newGrid.Comp.SetTile(tilePos, new Tile(tileType)); + _maps.SetTile(newGrid.Owner, newGrid.Comp, tilePos, new Tile(tileType)); var placementEraseEvent = new PlacementTileEvent(tileType, coordinates, placingUserId); _entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent); @@ -228,11 +230,16 @@ private void HandleRectRemoveReq(MsgPlacement msg) { EntityCoordinates start = _entityManager.GetCoordinates(msg.NetCoordinates); Vector2 rectSize = msg.RectSize; - foreach (EntityUid entity in EntitySystem.Get().GetEntitiesIntersecting(start.GetMapId(_entityManager), + foreach (var entity in _lookup.GetEntitiesIntersecting(start.GetMapId(_entityManager), new Box2(start.Position, start.Position + rectSize))) { - if (_entityManager.Deleted(entity) || _entityManager.HasComponent(entity) || _entityManager.HasComponent(entity)) + if (_entityManager.Deleted(entity) || + _entityManager.HasComponent(entity) || + _entityManager.HasComponent(entity)) + { continue; + } + var placementEraseEvent = new PlacementEntityEvent(entity, _entityManager.GetComponent(entity).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId); _entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent); _entityManager.DeleteEntity(entity); @@ -247,16 +254,16 @@ public void SendPlacementBegin(EntityUid mob, int range, string objectType, stri if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor)) return; - var playerConnection = actor.PlayerSession.ConnectedClient; - if (playerConnection == null) - return; + var playerConnection = actor.PlayerSession.Channel; - var message = new MsgPlacement(); - message.PlaceType = PlacementManagerMessage.StartPlacement; - message.Range = range; - message.IsTile = false; - message.ObjType = objectType; - message.AlignOption = alignOption; + var message = new MsgPlacement + { + PlaceType = PlacementManagerMessage.StartPlacement, + Range = range, + IsTile = false, + ObjType = objectType, + AlignOption = alignOption + }; _networkManager.ServerSendMessage(message, playerConnection); } @@ -268,16 +275,16 @@ public void SendPlacementBeginTile(EntityUid mob, int range, string tileType, st if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor)) return; - var playerConnection = actor.PlayerSession.ConnectedClient; - if (playerConnection == null) - return; + var playerConnection = actor.PlayerSession.Channel; - var message = new MsgPlacement(); - message.PlaceType = PlacementManagerMessage.StartPlacement; - message.Range = range; - message.IsTile = true; - message.ObjType = tileType; - message.AlignOption = alignOption; + var message = new MsgPlacement + { + PlaceType = PlacementManagerMessage.StartPlacement, + Range = range, + IsTile = true, + ObjType = tileType, + AlignOption = alignOption + }; _networkManager.ServerSendMessage(message, playerConnection); } @@ -289,12 +296,12 @@ public void SendPlacementCancel(EntityUid mob) if (!_entityManager.TryGetComponent(mob, out ActorComponent? actor)) return; - var playerConnection = actor.PlayerSession.ConnectedClient; - if (playerConnection == null) - return; + var playerConnection = actor.PlayerSession.Channel; - var message = new MsgPlacement(); - message.PlaceType = PlacementManagerMessage.CancelPlacement; + var message = new MsgPlacement + { + PlaceType = PlacementManagerMessage.CancelPlacement + }; _networkManager.ServerSendMessage(message, playerConnection); } diff --git a/Robust.Server/Player/PlayerManager.cs b/Robust.Server/Player/PlayerManager.cs index 4407d364285..61f562691e5 100644 --- a/Robust.Server/Player/PlayerManager.cs +++ b/Robust.Server/Player/PlayerManager.cs @@ -80,9 +80,7 @@ private Task OnConnecting(NetConnectingArgs args) /// private void NewSession(object? sender, NetChannelArgs args) { - var session = CreateAndAddSession(args.Channel.UserId, args.Channel.UserName); - session.Channel = args.Channel; - + CreateAndAddSession(args.Channel); PlayerCountMetric.Set(PlayerCount); // Synchronize base time. var msgTimeBase = new MsgSyncTimeBase(); @@ -106,8 +104,7 @@ private void EndSession(object? sender, NetChannelArgs args) DebugTools.Assert(session.Channel == args.Channel); SetStatus(session, SessionStatus.Disconnected); - if (session.AttachedEntity != null) - EntManager.System().Detach(session.AttachedEntity.Value); + SetAttachedEntity(session, null, out _, true); var viewSys = EntManager.System(); foreach (var eye in session.ViewSubscriptions.ToArray()) diff --git a/Robust.Server/ServerIoC.cs b/Robust.Server/ServerIoC.cs index feffc05b46c..8d9ca24fc86 100644 --- a/Robust.Server/ServerIoC.cs +++ b/Robust.Server/ServerIoC.cs @@ -15,7 +15,6 @@ using Robust.Server.Upload; using Robust.Server.ViewVariables; using Robust.Shared; -using Robust.Shared.Audio; using Robust.Shared.Configuration; using Robust.Shared.Console; using Robust.Shared.ContentPack; @@ -27,7 +26,6 @@ using Robust.Shared.Prototypes; using Robust.Shared.Reflection; using Robust.Shared.Replays; -using Robust.Shared.ResourceManagement; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Upload; @@ -66,7 +64,6 @@ internal static void RegisterIoC(IDependencyCollection deps) deps.Register(); deps.Register(); deps.Register(); - deps.Register(); deps.Register(); deps.Register(); deps.Register(); @@ -95,7 +92,6 @@ internal static void RegisterIoC(IDependencyCollection deps) deps.Register(); deps.Register(); deps.Register(); - deps.Register(); } } } diff --git a/Robust.Server/ServerStatus/DefaultMagicAczProvider.cs b/Robust.Server/ServerStatus/DefaultMagicAczProvider.cs index 87b266aa8db..8d3e6545e78 100644 --- a/Robust.Server/ServerStatus/DefaultMagicAczProvider.cs +++ b/Robust.Server/ServerStatus/DefaultMagicAczProvider.cs @@ -34,12 +34,12 @@ public async Task Package( var inputPass = graph.Input; var contentDir = FindContentRootPath(_deps); - await RobustClientPackaging.WriteContentAssemblies( + await RobustSharedPackaging.WriteContentAssemblies( inputPass, contentDir, binFolderPath, assemblyNames, - cancel); + cancel: cancel); await RobustClientPackaging.WriteClientResources(contentDir, inputPass, cancel); diff --git a/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs b/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs index f51722c1d9a..71da4a70054 100644 --- a/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs +++ b/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs @@ -167,7 +167,7 @@ private static string GenerateSource(in GeneratorExecutionContext context, IName getStateInit.Append($@" {name} = GetNetEntitySet(component.{name}),"); handleStateSetters.Append($@" - component.{name} = EnsureEntitySet<{componentName}>(state.{name}, uid);"); + EnsureEntitySet<{componentName}>(state.{name}, uid, component.{name});"); break; case GlobalEntityUidListName: @@ -177,7 +177,7 @@ private static string GenerateSource(in GeneratorExecutionContext context, IName getStateInit.Append($@" {name} = GetNetEntityList(component.{name}),"); handleStateSetters.Append($@" - component.{name} = EnsureEntityList<{componentName}>(state.{name}, uid);"); + EnsureEntityList<{componentName}>(state.{name}, uid, component.{name});"); break; default: diff --git a/Robust.Shared.Maths/Box2.cs b/Robust.Shared.Maths/Box2.cs index 4083bf24e94..a5124a6041b 100644 --- a/Robust.Shared.Maths/Box2.cs +++ b/Robust.Shared.Maths/Box2.cs @@ -87,8 +87,6 @@ public readonly Vector2 Extents get => (TopRight - BottomLeft) * 0.5f; } - public static Box2 Empty = new Box2(); - /// /// A 1x1 unit box with the origin centered. /// diff --git a/Robust.Shared/Audio/AudioParams.cs b/Robust.Shared/Audio/AudioParams.cs index a8db8910cc3..47d9a541319 100644 --- a/Robust.Shared/Audio/AudioParams.cs +++ b/Robust.Shared/Audio/AudioParams.cs @@ -1,7 +1,6 @@ using Robust.Shared.Serialization; using System; using System.Diagnostics.Contracts; -using Robust.Shared.Audio.Systems; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.GameObjects; @@ -12,7 +11,10 @@ public enum Attenuation : int { // https://hackage.haskell.org/package/OpenAL-1.7.0.5/docs/Sound-OpenAL-AL-Attenuation.html - Invalid = 0, + /// + /// Default to the overall attenuation. If set project-wide will use InverseDistanceClamped. This is what you typically want for an audio source. + /// + Default = 0, NoAttenuation = 1 << 0, InverseDistance = 1 << 1, InverseDistanceClamped = 1 << 2, @@ -32,56 +34,54 @@ public partial struct AudioParams /// /// The DistanceModel to use for this specific source. /// - [DataField] - public Attenuation Attenuation { get; set; } = Attenuation.LinearDistanceClamped; + [DataField("attenuation")] + public Attenuation Attenuation { get; set; } = Default.Attenuation; /// /// Base volume to play the audio at, in dB. /// - [DataField] + [DataField("volume")] public float Volume { get; set; } = Default.Volume; /// /// Scale for the audio pitch. /// - [DataField] - public float Pitch { get; set; } = Default.Pitch; + [DataField("pitchscale")] + public float PitchScale { get; set; } = Default.PitchScale; /// /// Audio bus to play on. /// - [DataField] + [DataField("busname")] public string BusName { get; set; } = Default.BusName; /// /// Only applies to positional audio. /// The maximum distance from which the audio is hearable. /// - [DataField] + [DataField("maxdistance")] public float MaxDistance { get; set; } = Default.MaxDistance; /// /// Used for distance attenuation calculations. Set to 0f to make a sound exempt from distance attenuation. /// - [DataField] + [DataField("rolloffFactor")] public float RolloffFactor { get; set; } = Default.RolloffFactor; /// /// Equivalent of the minimum distance to use for an audio source. /// - [DataField] + [DataField("referenceDistance")] public float ReferenceDistance { get; set; } = Default.ReferenceDistance; - [DataField] - public bool Loop { get; set; } = Default.Loop; + [DataField("loop")] public bool Loop { get; set; } = Default.Loop; - [DataField] - public float PlayOffsetSeconds { get; set; } = Default.PlayOffsetSeconds; + [DataField("playoffset")] public float PlayOffsetSeconds { get; set; } = Default.PlayOffsetSeconds; /// /// If not null, this will randomly modify the pitch scale by adding a number drawn from a normal distribution with this deviation. /// - [DataField] + [DataField("variation")] public float? Variation { get; set; } = null; // For the max distance value: it's 2000 in Godot, but I assume that's PIXELS due to the 2D positioning, @@ -96,21 +96,21 @@ public AudioParams() { } public AudioParams( float volume, - float pitch, + float pitchScale, string busName, float maxDistance, float refDistance, bool loop, float playOffsetSeconds, float? variation = null) - : this(volume, pitch, busName, maxDistance, 1, refDistance, loop, playOffsetSeconds, variation) + : this(volume, pitchScale, busName, maxDistance, 1, refDistance, loop, playOffsetSeconds, variation) { } - public AudioParams(float volume, float pitch, string busName, float maxDistance,float rolloffFactor, float refDistance, bool loop, float playOffsetSeconds, float? variation = null) : this() + public AudioParams(float volume, float pitchScale, string busName, float maxDistance,float rolloffFactor, float refDistance, bool loop, float playOffsetSeconds, float? variation = null) : this() { Volume = volume; - Pitch = pitch; + PitchScale = pitchScale; BusName = busName; MaxDistance = maxDistance; RolloffFactor = rolloffFactor; @@ -163,7 +163,7 @@ public readonly AudioParams WithVariation(float? variation) public readonly AudioParams WithPitchScale(float pitch) { var me = this; - me.Pitch = pitch; + me.PitchScale = pitch; return me; } diff --git a/Robust.Shared/Audio/AudioPresetPrototype.cs b/Robust.Shared/Audio/AudioPresetPrototype.cs deleted file mode 100644 index 9e5fe916d3c..00000000000 --- a/Robust.Shared/Audio/AudioPresetPrototype.cs +++ /dev/null @@ -1,91 +0,0 @@ -using Robust.Shared.Maths; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Robust.Shared.Audio; - -/// -/// Contains audio defaults to set for sounds. -/// This can be used by to apply an audio preset. -/// -[Prototype("audioPreset")] -public sealed class AudioPresetPrototype : IPrototype -{ - [IdDataField] - public string ID { get; } = default!; - - /// - /// Should the engine automatically create an auxiliary audio effect slot for this. - /// - [DataField] - public bool CreateAuxiliary; - - [DataField] - public float Density; - - [DataField] - public float Diffusion; - - [DataField] - public float Gain; - - [DataField("gainHf")] - public float GainHF; - - [DataField("gainLf")] - public float GainLF; - - [DataField] - public float DecayTime; - - [DataField("decayHfRatio")] - public float DecayHFRatio; - - [DataField("decayLfRatio")] - public float DecayLFRatio; - - [DataField] - public float ReflectionsGain; - - [DataField] - public float ReflectionsDelay; - - [DataField] - public Vector3 ReflectionsPan; - - [DataField] - public float LateReverbGain; - - [DataField] - public float LateReverbDelay; - - [DataField] - public Vector3 LateReverbPan; - - [DataField] - public float EchoTime; - - [DataField] - public float EchoDepth; - - [DataField] - public float ModulationTime; - - [DataField] - public float ModulationDepth; - - [DataField("airAbsorptionGainHf")] - public float AirAbsorptionGainHF; - - [DataField("hfReference")] - public float HFReference; - - [DataField("lfReference")] - public float LFReference; - - [DataField] - public float RoomRolloffFactor; - - [DataField("decayHfLimit")] - public int DecayHFLimit; -} diff --git a/Robust.Shared/Audio/Components/AudioAuxiliaryComponent.cs b/Robust.Shared/Audio/Components/AudioAuxiliaryComponent.cs deleted file mode 100644 index a1648d655b5..00000000000 --- a/Robust.Shared/Audio/Components/AudioAuxiliaryComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Robust.Shared.Audio.Effects; -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Robust.Shared.Audio.Components; - -/// -/// Can have Audio passed to it to apply effects or filters. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(SharedAudioSystem))] -public sealed partial class AudioAuxiliaryComponent : Component -{ - /// - /// Audio effect to attach to this auxiliary audio slot. - /// - [DataField, AutoNetworkedField] - public EntityUid? Effect; - - [ViewVariables] - internal IAuxiliaryAudio Auxiliary = new DummyAuxiliaryAudio(); -} diff --git a/Robust.Shared/Audio/Components/AudioComponent.cs b/Robust.Shared/Audio/Components/AudioComponent.cs deleted file mode 100644 index 76bb5b28f8c..00000000000 --- a/Robust.Shared/Audio/Components/AudioComponent.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Numerics; -using Robust.Shared.Audio.Effects; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.ViewVariables; - -namespace Robust.Shared.Audio.Components; - -/// -/// Stores the audio data for an audio entity. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedAudioSystem))] -public sealed partial class AudioComponent : Component, IAudioSource -{ - #region Filter - - public override bool SessionSpecific => true; - - /// - /// Used for synchronising audio on client that comes into PVS range. - /// - [DataField(customTypeSerializer:typeof(TimeOffsetSerializer)), AutoNetworkedField] - public TimeSpan AudioStart; - - #region Filters - - // Don't need to network these as client doesn't care. - - /// - /// If this sound was predicted do we exclude it from a specific entity. - /// Useful for predicted audio. - /// - [DataField] - public EntityUid? ExcludedEntity; - - /// - /// If the sound was filtered what entities were included. - /// - [DataField] - public HashSet? IncludedEntities; - - #endregion - - #endregion - - // We can't just start playing on audio creation as we don't have the correct position yet. - // As such we'll wait for FrameUpdate before we start playing to avoid the position being cooked. - public bool Started = false; - - [AutoNetworkedField] - [DataField(required: true)] - public string FileName = string.Empty; - - /// - /// Audio params. Set this if you want to adjust default volume, max distance, etc. - /// - [AutoNetworkedField] - [DataField] - public AudioParams Params = AudioParams.Default; - - /// - /// Audio source that interacts with OpenAL. - /// - [ViewVariables(VVAccess.ReadOnly)] - internal IAudioSource Source = default!; - - /// - /// Auxiliary entity to pass audio to. - /// - [DataField, AutoNetworkedField] - public EntityUid? Auxiliary; - - /* - * Values for IAudioSource stored on the component and sent to IAudioSource as applicable. - * Most of these aren't networked as they double AudioParams data and these just interact with IAudioSource. - */ - - #region Source - - public void Pause() => Source.Pause(); - - /// - public void StartPlaying() => Source.StartPlaying(); - - /// - public void StopPlaying() => Source.StopPlaying(); - - /// - /// - /// - [ViewVariables] - public bool Playing - { - get => Source.Playing; - set => Source.Playing = value; - } - - /// - /// - /// - [ViewVariables] - public bool Looping - { - get => Source.Looping; - set => Source.Looping = value; - } - - /// - /// - /// - [AutoNetworkedField] - public bool Global { get; set; } - - /// - /// - /// - public float Pitch - { - get => Source.Pitch; - set => Source.Pitch = value; - } - - /// - /// - /// - public float MaxDistance - { - get => Source.MaxDistance; - set => Source.MaxDistance = value; - } - - /// - /// - /// - public float RolloffFactor - { - get => Source.RolloffFactor; - set => Source.RolloffFactor = value; - } - - /// - /// - /// - public float ReferenceDistance - { - get => Source.ReferenceDistance; - set => Source.ReferenceDistance = value; - } - - /// - /// - /// - /// - /// Not replicated as audio always tracks the entity's position. - /// - [ViewVariables] - public Vector2 Position - { - get => Source.Position; - set => Source.Position = value; - } - - /// - /// - /// - [ViewVariables] - [Access(Other = AccessPermissions.ReadWriteExecute)] - public float Volume - { - get => Source.Volume; - set => Source.Volume = value; - } - - /// - /// - /// - [ViewVariables] - [Access(Other = AccessPermissions.ReadWriteExecute)] - public float Gain - { - get => Source.Gain; - set => Source.Gain = value; - } - - /// - /// - /// - [ViewVariables] - [Access(Other = AccessPermissions.ReadWriteExecute)] - public float Occlusion - { - get => Source.Occlusion; - set => Source.Occlusion = value; - } - - /// - /// - /// - [ViewVariables] - public float PlaybackPosition - { - get => Source.PlaybackPosition; - set => Source.PlaybackPosition = value; - } - - /// - /// - /// - /// - /// Not replicated. - /// - [ViewVariables] - public Vector2 Velocity - { - get => Source.Velocity; - set => Source.Velocity = value; - } - - void IAudioSource.SetAuxiliary(IAuxiliaryAudio? audio) - { - Source.SetAuxiliary(audio); - } - - #endregion - - public void Dispose() - { - Source.Dispose(); - } -} diff --git a/Robust.Shared/Audio/Components/AudioEffectComponent.cs b/Robust.Shared/Audio/Components/AudioEffectComponent.cs deleted file mode 100644 index 4ce5bf716be..00000000000 --- a/Robust.Shared/Audio/Components/AudioEffectComponent.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System; -using Robust.Shared.Audio.Effects; -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.Maths; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Robust.Shared.Audio.Components; - -/// -/// Stores OpenAL audio effect data that can be bound to an . -/// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedAudioSystem)), AutoGenerateComponentState] -public sealed partial class AudioEffectComponent : Component, IAudioEffect -{ - [ViewVariables] - internal IAudioEffect Effect = new DummyAudioEffect(); - - /// - [DataField, AutoNetworkedField] - public float Density - { - get => Effect.Density; - set => Effect.Density = value; - } - - /// - [DataField, AutoNetworkedField] - public float Diffusion - { - get => Effect.Diffusion; - set => Effect.Diffusion = value; - } - - /// - [DataField, AutoNetworkedField] - public float Gain - { - get => Effect.Gain; - set => Effect.Gain = value; - } - - /// - [DataField, AutoNetworkedField] - public float GainHF - { - get => Effect.GainHF; - set => Effect.GainHF = value; - } - - /// - [DataField, AutoNetworkedField] - public float GainLF - { - get => Effect.GainLF; - set => Effect.GainLF = value; - } - - /// - [DataField, AutoNetworkedField] - public float DecayTime - { - get => Effect.DecayTime; - set => Effect.DecayTime = value; - } - - /// - [DataField, AutoNetworkedField] - public float DecayHFRatio - { - get => Effect.DecayHFRatio; - set => Effect.DecayHFRatio = value; - } - - /// - [DataField, AutoNetworkedField] - public float DecayLFRatio - { - get => Effect.DecayLFRatio; - set => Effect.DecayLFRatio = value; - } - - /// - [DataField, AutoNetworkedField] - public float ReflectionsGain - { - get => Effect.ReflectionsGain; - set => Effect.ReflectionsGain = value; - } - - /// - [DataField, AutoNetworkedField] - public float ReflectionsDelay - { - get => Effect.ReflectionsDelay; - set => Effect.ReflectionsDelay = value; - } - - /// - [DataField, AutoNetworkedField] - public Vector3 ReflectionsPan - { - get => Effect.ReflectionsPan; - set => Effect.ReflectionsPan = value; - } - - /// - [DataField, AutoNetworkedField] - public float LateReverbGain - { - get => Effect.LateReverbGain; - set => Effect.LateReverbGain = value; - } - - /// - [DataField, AutoNetworkedField] - public float LateReverbDelay - { - get => Effect.LateReverbDelay; - set => Effect.LateReverbDelay = value; - } - - /// - [DataField, AutoNetworkedField] - public Vector3 LateReverbPan - { - get => Effect.LateReverbPan; - set => Effect.LateReverbPan = value; - } - - /// - [DataField, AutoNetworkedField] - public float EchoTime - { - get => Effect.EchoTime; - set => Effect.EchoTime = value; - } - - /// - [DataField, AutoNetworkedField] - public float EchoDepth - { - get => Effect.EchoDepth; - set => Effect.EchoDepth = value; - } - - /// - [DataField, AutoNetworkedField] - public float ModulationTime - { - get => Effect.ModulationTime; - set => Effect.ModulationTime = value; - } - - /// - [DataField, AutoNetworkedField] - public float ModulationDepth - { - get => Effect.ModulationDepth; - set => Effect.ModulationDepth = value; - } - - /// - [DataField, AutoNetworkedField] - public float AirAbsorptionGainHF - { - get => Effect.AirAbsorptionGainHF; - set => Effect.AirAbsorptionGainHF = value; - } - - /// - [DataField, AutoNetworkedField] - public float HFReference - { - get => Effect.HFReference; - set => Effect.HFReference = value; - } - - /// - [DataField, AutoNetworkedField] - public float LFReference - { - get => Effect.LFReference; - set => Effect.LFReference = value; - } - - /// - [DataField, AutoNetworkedField] - public float RoomRolloffFactor - { - get => Effect.RoomRolloffFactor; - set => Effect.RoomRolloffFactor = value; - } - - /// - [DataField, AutoNetworkedField] - public int DecayHFLimit - { - get => Effect.DecayHFLimit; - set => Effect.DecayHFLimit = value; - } -} diff --git a/Robust.Shared/Audio/Components/AudioPresetComponent.cs b/Robust.Shared/Audio/Components/AudioPresetComponent.cs deleted file mode 100644 index c9723ed6fdc..00000000000 --- a/Robust.Shared/Audio/Components/AudioPresetComponent.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.Shared.Audio.Systems; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; - -namespace Robust.Shared.Audio.Components; - -/// -/// Marks this entity as being spawned for audio presets in case we need to reload. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedAudioSystem))] -public sealed partial class AudioPresetComponent : Component -{ - [AutoNetworkedField] - public string Preset = string.Empty; -} diff --git a/Robust.Shared/Audio/Effects/DummyAudioEffect.cs b/Robust.Shared/Audio/Effects/DummyAudioEffect.cs deleted file mode 100644 index 1c90d3c3253..00000000000 --- a/Robust.Shared/Audio/Effects/DummyAudioEffect.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Robust.Shared.Audio.Components; -using Robust.Shared.Maths; - -namespace Robust.Shared.Audio.Effects; - -/// -internal sealed class DummyAudioEffect : IAudioEffect -{ - public void Dispose() - { - } - - /// - public float Density { get; set; } - - /// - public float Diffusion { get; set; } - - /// - public float Gain { get; set; } - - /// - public float GainHF { get; set; } - - /// - public float GainLF { get; set; } - - /// - public float DecayTime { get; set; } - - /// - public float DecayHFRatio { get; set; } - - /// - public float DecayLFRatio { get; set; } - - /// - public float ReflectionsGain { get; set; } - - /// - public float ReflectionsDelay { get; set; } - - /// - public Vector3 ReflectionsPan { get; set; } - - /// - public float LateReverbGain { get; set; } - - /// - public float LateReverbDelay { get; set; } - - /// - public Vector3 LateReverbPan { get; set; } - - /// - public float EchoTime { get; set; } - - /// - public float EchoDepth { get; set; } - - /// - public float ModulationTime { get; set; } - - /// - public float ModulationDepth { get; set; } - - /// - public float AirAbsorptionGainHF { get; set; } - - /// - public float HFReference { get; set; } - - /// - public float LFReference { get; set; } - - /// - public float RoomRolloffFactor { get; set; } - - /// - public int DecayHFLimit { get; set; } -} diff --git a/Robust.Shared/Audio/Effects/DummyAuxiliaryAudio.cs b/Robust.Shared/Audio/Effects/DummyAuxiliaryAudio.cs deleted file mode 100644 index b685288cda7..00000000000 --- a/Robust.Shared/Audio/Effects/DummyAuxiliaryAudio.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Robust.Shared.Audio.Effects; - -/// -internal sealed class DummyAuxiliaryAudio : IAuxiliaryAudio -{ - public void Dispose() - { - } - - /// - public void SetEffect(IAudioEffect? effect) - { - } -} diff --git a/Robust.Shared/Audio/Effects/IAudioEffect.cs b/Robust.Shared/Audio/Effects/IAudioEffect.cs deleted file mode 100644 index 59d8cc948f6..00000000000 --- a/Robust.Shared/Audio/Effects/IAudioEffect.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using Robust.Shared.Maths; - -namespace Robust.Shared.Audio.Effects; - -internal interface IAudioEffect -{ - /// - /// Gets the preset value for . - /// - public float Density { get; set; } - - /// - /// Gets the preset value for . - /// - public float Diffusion { get; set; } - - /// - /// Gets the preset value for . - /// - public float Gain { get; set; } - - /// - /// Gets the preset value for . - /// - public float GainHF { get; set; } - - /// - /// Gets the preset value for . - /// - public float GainLF { get; set; } - - /// - /// Gets the preset value for . - /// - public float DecayTime { get; set; } - - /// - /// Gets the preset value for . - /// - public float DecayHFRatio { get; set; } - - /// - /// Gets the preset value for . - /// - public float DecayLFRatio { get; set; } - - /// - /// Gets the preset value for . - /// - public float ReflectionsGain { get; set; } - - /// - /// Gets the preset value for . - /// - public float ReflectionsDelay { get; set; } - - /// - /// Gets the preset value for . - /// - public Vector3 ReflectionsPan { get; set; } - - /// - /// Gets the preset value for . - /// - public float LateReverbGain { get; set; } - - /// - /// Gets the preset value for . - /// - public float LateReverbDelay { get; set; } - - /// - /// Gets the preset value for . - /// - public Vector3 LateReverbPan { get; set; } - - /// - /// Gets the preset value for . - /// - public float EchoTime { get; set; } - - /// - /// Gets the preset value for . - /// - public float EchoDepth { get; set; } - - /// - /// Gets the preset value for . - /// - public float ModulationTime { get; set; } - - /// - /// Gets the preset value for . - /// - public float ModulationDepth { get; set; } - - /// - /// Gets the preset value for . - /// - public float AirAbsorptionGainHF { get; set; } - - /// - /// Gets the preset value for . - /// - public float HFReference { get; set; } - - /// - /// Gets the preset value for . - /// - public float LFReference { get; set; } - - /// - /// Gets the preset value for . - /// - public float RoomRolloffFactor { get; set; } - - /// - /// Gets the preset value for . - /// - public int DecayHFLimit { get; set; } -} diff --git a/Robust.Shared/Audio/Effects/IAuxiliaryAudio.cs b/Robust.Shared/Audio/Effects/IAuxiliaryAudio.cs deleted file mode 100644 index 43e72a6ae40..00000000000 --- a/Robust.Shared/Audio/Effects/IAuxiliaryAudio.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Robust.Shared.Audio.Effects; - -internal interface IAuxiliaryAudio : IDisposable -{ - /// - /// Sets the audio effect for this auxiliary audio slot. - /// - void SetEffect(IAudioEffect? effect); -} diff --git a/Robust.Shared/Audio/Effects/ReverbPresets.cs b/Robust.Shared/Audio/Effects/ReverbPresets.cs deleted file mode 100644 index c94eedb1f3b..00000000000 --- a/Robust.Shared/Audio/Effects/ReverbPresets.cs +++ /dev/null @@ -1,3435 +0,0 @@ -// -// ReverbPresets.cs -// -// Copyright (C) 2019 OpenTK -// -// This software may be modified and distributed under the terms -// of the MIT license. See the LICENSE file for details. -// - - -using Robust.Shared.Maths; - -namespace Robust.Shared.Audio.Effects; - -/// -/// A set of reverb presets that can be used with the extension. -/// -public static class ReverbPresets -{ - /// - /// A reverb preset (approximating a generic location). - /// - public static readonly ReverbProperties Generic = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.8913f, - 1.0000f, - 1.4900f, - 0.8300f, - 1.0000f, - 0.0500f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a padded cell). - /// - public static readonly ReverbProperties PaddedCell = new ReverbProperties - ( - 0.1715f, - 1.0000f, - 0.3162f, - 0.0010f, - 1.0000f, - 0.1700f, - 0.1000f, - 1.0000f, - 0.2500f, - 0.0010f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2691f, - 0.0020f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a room). - /// - public static readonly ReverbProperties Room = new ReverbProperties - ( - 0.4287f, - 1.0000f, - 0.3162f, - 0.5929f, - 1.0000f, - 0.4000f, - 0.8300f, - 1.0000f, - 0.1503f, - 0.0020f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.0629f, - 0.0030f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a bathroom). - /// - public static readonly ReverbProperties Bathroom = new ReverbProperties - ( - 0.1715f, - 1.0000f, - 0.3162f, - 0.2512f, - 1.0000f, - 1.4900f, - 0.5400f, - 1.0000f, - 0.6531f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 3.2734f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a living room). - /// - public static readonly ReverbProperties LivingRoom = new ReverbProperties - ( - 0.9766f, - 1.0000f, - 0.3162f, - 0.0010f, - 1.0000f, - 0.5000f, - 0.1000f, - 1.0000f, - 0.2051f, - 0.0030f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2805f, - 0.0040f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a stone room). - /// - public static readonly ReverbProperties StoneRoom = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.7079f, - 1.0000f, - 2.3100f, - 0.6400f, - 1.0000f, - 0.4411f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1003f, - 0.0170f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an auditorium). - /// - public static readonly ReverbProperties Auditorium = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.5781f, - 1.0000f, - 4.3200f, - 0.5900f, - 1.0000f, - 0.4032f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7170f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a concert hall). - /// - public static readonly ReverbProperties ConcertHall = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.5623f, - 1.0000f, - 3.9200f, - 0.7000f, - 1.0000f, - 0.2427f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.9977f, - 0.0290f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a cave). - /// - public static readonly ReverbProperties Cave = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 1.0000f, - 1.0000f, - 2.9100f, - 1.3000f, - 1.0000f, - 0.5000f, - 0.0150f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7063f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating an arena). - /// - public static readonly ReverbProperties Arena = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.4477f, - 1.0000f, - 7.2400f, - 0.3300f, - 1.0000f, - 0.2612f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.0186f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hangar). - /// - public static readonly ReverbProperties Hangar = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.3162f, - 1.0000f, - 10.0500f, - 0.2300f, - 1.0000f, - 0.5000f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2560f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a carpeted hallway). - /// - public static readonly ReverbProperties CarpetedHallway = new ReverbProperties - ( - 0.4287f, - 1.0000f, - 0.3162f, - 0.0100f, - 1.0000f, - 0.3000f, - 0.1000f, - 1.0000f, - 0.1215f, - 0.0020f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1531f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hallway). - /// - public static readonly ReverbProperties Hallway = new ReverbProperties - ( - 0.3645f, - 1.0000f, - 0.3162f, - 0.7079f, - 1.0000f, - 1.4900f, - 0.5900f, - 1.0000f, - 0.2458f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.6615f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a stone corridor). - /// - public static readonly ReverbProperties StoneCorridor = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.7612f, - 1.0000f, - 2.7000f, - 0.7900f, - 1.0000f, - 0.2472f, - 0.0130f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.5758f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an alley). - /// - public static readonly ReverbProperties Alley = new ReverbProperties - ( - 1.0000f, - 0.3000f, - 0.3162f, - 0.7328f, - 1.0000f, - 1.4900f, - 0.8600f, - 1.0000f, - 0.2500f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.9954f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1250f, - 0.9500f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a forest). - /// - public static readonly ReverbProperties Forest = new ReverbProperties - ( - 1.0000f, - 0.3000f, - 0.3162f, - 0.0224f, - 1.0000f, - 1.4900f, - 0.5400f, - 1.0000f, - 0.0525f, - 0.1620f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7682f, - 0.0880f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1250f, - 1.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a city). - /// - public static readonly ReverbProperties City = new ReverbProperties - ( - 1.0000f, - 0.5000f, - 0.3162f, - 0.3981f, - 1.0000f, - 1.4900f, - 0.6700f, - 1.0000f, - 0.0730f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1427f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a mountain). - /// - public static readonly ReverbProperties Mountains = new ReverbProperties - ( - 1.0000f, - 0.2700f, - 0.3162f, - 0.0562f, - 1.0000f, - 1.4900f, - 0.2100f, - 1.0000f, - 0.0407f, - 0.3000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1919f, - 0.1000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 1.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a quarry). - /// - public static readonly ReverbProperties Quarry = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.3162f, - 1.0000f, - 1.4900f, - 0.8300f, - 1.0000f, - 0.0000f, - 0.0610f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.7783f, - 0.0250f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1250f, - 0.7000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a plain). - /// - public static readonly ReverbProperties Plain = new ReverbProperties - ( - 1.0000f, - 0.2100f, - 0.3162f, - 0.1000f, - 1.0000f, - 1.4900f, - 0.5000f, - 1.0000f, - 0.0585f, - 0.1790f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1089f, - 0.1000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 1.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a parking lot). - /// - public static readonly ReverbProperties ParkingLot = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 1.0000f, - 1.0000f, - 1.6500f, - 1.5000f, - 1.0000f, - 0.2082f, - 0.0080f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2652f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a sewer pipe). - /// - public static readonly ReverbProperties Sewerpipe = new ReverbProperties - ( - 0.3071f, - 0.8000f, - 0.3162f, - 0.3162f, - 1.0000f, - 2.8100f, - 0.1400f, - 1.0000f, - 1.6387f, - 0.0140f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 3.2471f, - 0.0210f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an underwater location). - /// - public static readonly ReverbProperties Underwater = new ReverbProperties - ( - 0.3645f, - 1.0000f, - 0.3162f, - 0.0100f, - 1.0000f, - 1.4900f, - 0.1000f, - 1.0000f, - 0.5963f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 7.0795f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 1.1800f, - 0.3480f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a drugged state). - /// - public static readonly ReverbProperties Drugged = new ReverbProperties - ( - 0.4287f, - 0.5000f, - 0.3162f, - 1.0000f, - 1.0000f, - 8.3900f, - 1.3900f, - 1.0000f, - 0.8760f, - 0.0020f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 3.1081f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 1.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a dizzy state). - /// - public static readonly ReverbProperties Dizzy = new ReverbProperties - ( - 0.3645f, - 0.6000f, - 0.3162f, - 0.6310f, - 1.0000f, - 17.2300f, - 0.5600f, - 1.0000f, - 0.1392f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.4937f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 1.0000f, - 0.8100f, - 0.3100f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a psychotic state). - /// - public static readonly ReverbProperties Psychotic = new ReverbProperties - ( - 0.0625f, - 0.5000f, - 0.3162f, - 0.8404f, - 1.0000f, - 7.5600f, - 0.9100f, - 1.0000f, - 0.4864f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 2.4378f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 4.0000f, - 1.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /* Castle Presets */ - - /// - /// A reverb preset (approximating a small room in a castle). - /// - public static readonly ReverbProperties CastleSmallRoom = new ReverbProperties - ( - 1.0000f, - 0.8900f, - 0.3162f, - 0.3981f, - 0.1000f, - 1.2200f, - 0.8300f, - 0.3100f, - 0.8913f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.9953f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1380f, - 0.0800f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a short passage in a castle). - /// - public static readonly ReverbProperties CastleShortPassage = new ReverbProperties - ( - 1.0000f, - 0.8900f, - 0.3162f, - 0.3162f, - 0.1000f, - 2.3200f, - 0.8300f, - 0.3100f, - 0.8913f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0230f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1380f, - 0.0800f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a medium room in a castle). - /// - public static readonly ReverbProperties CastleMediumRoom = new ReverbProperties - ( - 1.0000f, - 0.9300f, - 0.3162f, - 0.2818f, - 0.1000f, - 2.0400f, - 0.8300f, - 0.4600f, - 0.6310f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.5849f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1550f, - 0.0300f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a large room in a castle). - /// - public static readonly ReverbProperties CastleLargeRoom = new ReverbProperties - ( - 1.0000f, - 0.8200f, - 0.3162f, - 0.2818f, - 0.1259f, - 2.5300f, - 0.8300f, - 0.5000f, - 0.4467f, - 0.0340f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0160f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1850f, - 0.0700f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a long passage in a castle). - /// - public static readonly ReverbProperties CastleLongPassage = new ReverbProperties - ( - 1.0000f, - 0.8900f, - 0.3162f, - 0.3981f, - 0.1000f, - 3.4200f, - 0.8300f, - 0.3100f, - 0.8913f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0230f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1380f, - 0.0800f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hall in a castle). - /// - public static readonly ReverbProperties CastleHall = new ReverbProperties - ( - 1.0000f, - 0.8100f, - 0.3162f, - 0.2818f, - 0.1778f, - 3.1400f, - 0.7900f, - 0.6200f, - 0.1778f, - 0.0560f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0240f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a cupboard in a castle). - /// - public static readonly ReverbProperties CastleCupboard = new ReverbProperties - ( - 1.0000f, - 0.8900f, - 0.3162f, - 0.2818f, - 0.1000f, - 0.6700f, - 0.8700f, - 0.3100f, - 1.4125f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 3.5481f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1380f, - 0.0800f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a castle courtyard). - /// - public static readonly ReverbProperties CastleCourtyard = new ReverbProperties - ( - 1.0000f, - 0.4200f, - 0.3162f, - 0.4467f, - 0.1995f, - 2.1300f, - 0.6100f, - 0.2300f, - 0.2239f, - 0.1600f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7079f, - 0.0360f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.3700f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating an alcove in a castle). - /// - public static readonly ReverbProperties CastleAlcove = new ReverbProperties - ( - 1.0000f, - 0.8900f, - 0.3162f, - 0.5012f, - 0.1000f, - 1.6400f, - 0.8700f, - 0.3100f, - 1.0000f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0340f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1380f, - 0.0800f, - 0.2500f, - 0.0000f, - 0.9943f, - 5168.6001f, - 139.5000f, - 0.0000f, - 0x1 - ); - - /* Factory Presets */ - - /// - /// A reverb preset (approximating a small room in a factory). - /// - public static readonly ReverbProperties FactorySmallRoom = new ReverbProperties - ( - 0.3645f, - 0.8200f, - 0.3162f, - 0.7943f, - 0.5012f, - 1.7200f, - 0.6500f, - 1.3100f, - 0.7079f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.7783f, - 0.0240f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1190f, - 0.0700f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a short passage in a factory). - /// - public static readonly ReverbProperties FactoryShortPassage = new ReverbProperties - ( - 0.3645f, - 0.6400f, - 0.2512f, - 0.7943f, - 0.5012f, - 2.5300f, - 0.6500f, - 1.3100f, - 1.0000f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0380f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1350f, - 0.2300f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a medium room in a factory). - /// - public static readonly ReverbProperties FactoryMediumRoom = new ReverbProperties - ( - 0.4287f, - 0.8200f, - 0.2512f, - 0.7943f, - 0.5012f, - 2.7600f, - 0.6500f, - 1.3100f, - 0.2818f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0230f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1740f, - 0.0700f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a large room in a factory). - /// - public static readonly ReverbProperties FactoryLargeRoom = new ReverbProperties - ( - 0.4287f, - 0.7500f, - 0.2512f, - 0.7079f, - 0.6310f, - 4.2400f, - 0.5100f, - 1.3100f, - 0.1778f, - 0.0390f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0230f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2310f, - 0.0700f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a long passage in a factory). - /// - public static readonly ReverbProperties FactoryLongPassage = new ReverbProperties - ( - 0.3645f, - 0.6400f, - 0.2512f, - 0.7943f, - 0.5012f, - 4.0600f, - 0.6500f, - 1.3100f, - 1.0000f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0370f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1350f, - 0.2300f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hall in a factory). - /// - public static readonly ReverbProperties FactoryHall = new ReverbProperties - ( - 0.4287f, - 0.7500f, - 0.3162f, - 0.7079f, - 0.6310f, - 7.4300f, - 0.5100f, - 1.3100f, - 0.0631f, - 0.0730f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.8913f, - 0.0270f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0700f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a cupboard in a factory). - /// - public static readonly ReverbProperties FactoryCupboard = new ReverbProperties - ( - 0.3071f, - 0.6300f, - 0.2512f, - 0.7943f, - 0.5012f, - 0.4900f, - 0.6500f, - 1.3100f, - 1.2589f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.9953f, - 0.0320f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1070f, - 0.0700f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a factory courtyard). - /// - public static readonly ReverbProperties FactoryCourtyard = new ReverbProperties - ( - 0.3071f, - 0.5700f, - 0.3162f, - 0.3162f, - 0.6310f, - 2.3200f, - 0.2900f, - 0.5600f, - 0.2239f, - 0.1400f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.3981f, - 0.0390f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.2900f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an alcove in a factory). - /// - public static readonly ReverbProperties FactoryAlcove = new ReverbProperties - ( - 0.3645f, - 0.5900f, - 0.2512f, - 0.7943f, - 0.5012f, - 3.1400f, - 0.6500f, - 1.3100f, - 1.4125f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.0000f, - 0.0380f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1140f, - 0.1000f, - 0.2500f, - 0.0000f, - 0.9943f, - 3762.6001f, - 362.5000f, - 0.0000f, - 0x1 - ); - - /* Ice Palace Presets */ - - /// - /// A reverb preset (approximating a small room in an ice palace). - /// - public static readonly ReverbProperties IcePalaceSmallRoom = new ReverbProperties - ( - 1.0000f, - 0.8400f, - 0.3162f, - 0.5623f, - 0.2818f, - 1.5100f, - 1.5300f, - 0.2700f, - 0.8913f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1640f, - 0.1400f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a short passage in an ice palace). - /// - public static readonly ReverbProperties IcePalaceShortPassage = new ReverbProperties - ( - 1.0000f, - 0.7500f, - 0.3162f, - 0.5623f, - 0.2818f, - 1.7900f, - 1.4600f, - 0.2800f, - 0.5012f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0190f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1770f, - 0.0900f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a medium room in an ice palace). - /// - public static readonly ReverbProperties IcePalaceMediumRoom = new ReverbProperties - ( - 1.0000f, - 0.8700f, - 0.3162f, - 0.5623f, - 0.4467f, - 2.2200f, - 1.5300f, - 0.3200f, - 0.3981f, - 0.0390f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0270f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1860f, - 0.1200f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a large room in an ice palace). - /// - public static readonly ReverbProperties IcePalaceLargeRoom = new ReverbProperties - ( - 1.0000f, - 0.8100f, - 0.3162f, - 0.5623f, - 0.4467f, - 3.1400f, - 1.5300f, - 0.3200f, - 0.2512f, - 0.0390f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.0000f, - 0.0270f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2140f, - 0.1100f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a long passage in an ice palace). - /// - public static readonly ReverbProperties IcePalaceLongPassage = new ReverbProperties - ( - 1.0000f, - 0.7700f, - 0.3162f, - 0.5623f, - 0.3981f, - 3.0100f, - 1.4600f, - 0.2800f, - 0.7943f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0250f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1860f, - 0.0400f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hall in an ice palace). - /// - public static readonly ReverbProperties IcePalaceHall = new ReverbProperties - ( - 1.0000f, - 0.7600f, - 0.3162f, - 0.4467f, - 0.5623f, - 5.4900f, - 1.5300f, - 0.3800f, - 0.1122f, - 0.0540f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.6310f, - 0.0520f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2260f, - 0.1100f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a cupboard in an ice palace). - /// - public static readonly ReverbProperties IcePalaceCupboard = new ReverbProperties - ( - 1.0000f, - 0.8300f, - 0.3162f, - 0.5012f, - 0.2239f, - 0.7600f, - 1.5300f, - 0.2600f, - 1.1220f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.9953f, - 0.0160f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1430f, - 0.0800f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an ice palace courtyard). - /// - public static readonly ReverbProperties IcePalaceCourtyard = new ReverbProperties - ( - 1.0000f, - 0.5900f, - 0.3162f, - 0.2818f, - 0.3162f, - 2.0400f, - 1.2000f, - 0.3800f, - 0.3162f, - 0.1730f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.3162f, - 0.0430f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2350f, - 0.4800f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an alcove in an ice palace). - /// - public static readonly ReverbProperties IcePalaceAlcove = new ReverbProperties - ( - 1.0000f, - 0.8400f, - 0.3162f, - 0.5623f, - 0.2818f, - 2.7600f, - 1.4600f, - 0.2800f, - 1.1220f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.8913f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1610f, - 0.0900f, - 0.2500f, - 0.0000f, - 0.9943f, - 12428.5000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /* Space Station Presets */ - - /// - /// A reverb preset (approximating a small room in a space station). - /// - public static readonly ReverbProperties SpaceStationSmallRoom = new ReverbProperties - ( - 0.2109f, - 0.7000f, - 0.3162f, - 0.7079f, - 0.8913f, - 1.7200f, - 0.8200f, - 0.5500f, - 0.7943f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0130f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1880f, - 0.2600f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a short passage in a space station). - /// - public static readonly ReverbProperties SpaceStationShortPassage = new ReverbProperties - ( - 0.2109f, - 0.8700f, - 0.3162f, - 0.6310f, - 0.8913f, - 3.5700f, - 0.5000f, - 0.5500f, - 1.0000f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0160f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1720f, - 0.2000f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a medium room in a space station). - /// - public static readonly ReverbProperties SpaceStationMediumRoom = new ReverbProperties - ( - 0.2109f, - 0.7500f, - 0.3162f, - 0.6310f, - 0.8913f, - 3.0100f, - 0.5000f, - 0.5500f, - 0.3981f, - 0.0340f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0350f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2090f, - 0.3100f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a large room in a space station). - /// - public static readonly ReverbProperties SpaceStationLargeRoom = new ReverbProperties - ( - 0.3645f, - 0.8100f, - 0.3162f, - 0.6310f, - 0.8913f, - 3.8900f, - 0.3800f, - 0.6100f, - 0.3162f, - 0.0560f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.8913f, - 0.0350f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2330f, - 0.2800f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a long passage in a space station). - /// - public static readonly ReverbProperties SpaceStationLongPassage = new ReverbProperties - ( - 0.4287f, - 0.8200f, - 0.3162f, - 0.6310f, - 0.8913f, - 4.6200f, - 0.6200f, - 0.5500f, - 1.0000f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0310f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.2300f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hall in a space station). - /// - public static readonly ReverbProperties SpaceStationHall = new ReverbProperties - ( - 0.4287f, - 0.8700f, - 0.3162f, - 0.6310f, - 0.8913f, - 7.1100f, - 0.3800f, - 0.6100f, - 0.1778f, - 0.1000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.6310f, - 0.0470f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.2500f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a cupboard in a space station). - /// - public static readonly ReverbProperties SpaceStationCupboard = new ReverbProperties - ( - 0.1715f, - 0.5600f, - 0.3162f, - 0.7079f, - 0.8913f, - 0.7900f, - 0.8100f, - 0.5500f, - 1.4125f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.7783f, - 0.0180f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1810f, - 0.3100f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an alcove in a space station). - /// - public static readonly ReverbProperties SpaceStationAlcove = new ReverbProperties - ( - 0.2109f, - 0.7800f, - 0.3162f, - 0.7079f, - 0.8913f, - 1.1600f, - 0.8100f, - 0.5500f, - 1.4125f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.0000f, - 0.0180f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1920f, - 0.2100f, - 0.2500f, - 0.0000f, - 0.9943f, - 3316.1001f, - 458.2000f, - 0.0000f, - 0x1 - ); - - /* Wooden Galleon Presets */ - - /// - /// A reverb preset (approximating a small room in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonSmallRoom = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.1122f, - 0.3162f, - 0.7900f, - 0.3200f, - 0.8700f, - 1.0000f, - 0.0320f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.8913f, - 0.0290f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a short passage in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonShortPassage = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.1259f, - 0.3162f, - 1.7500f, - 0.5000f, - 0.8700f, - 0.8913f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.6310f, - 0.0240f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a medium room in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonMediumRoom = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.1000f, - 0.2818f, - 1.4700f, - 0.4200f, - 0.8200f, - 0.8913f, - 0.0490f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.8913f, - 0.0290f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a large room in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonLargeRoom = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.0891f, - 0.2818f, - 2.6500f, - 0.3300f, - 0.8200f, - 0.8913f, - 0.0660f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7943f, - 0.0490f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a long passsage in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonLongPassage = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.1000f, - 0.3162f, - 1.9900f, - 0.4000f, - 0.7900f, - 1.0000f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.4467f, - 0.0360f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hall in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonHall = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.0794f, - 0.2818f, - 3.4500f, - 0.3000f, - 0.8200f, - 0.8913f, - 0.0880f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7943f, - 0.0630f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a cupboard in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonCupboard = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.1413f, - 0.3162f, - 0.5600f, - 0.4600f, - 0.9100f, - 1.1220f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0280f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a courtyard on a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonCourtyard = new ReverbProperties - ( - 1.0000f, - 0.6500f, - 0.3162f, - 0.0794f, - 0.3162f, - 1.7900f, - 0.3500f, - 0.7900f, - 0.5623f, - 0.1230f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1000f, - 0.0320f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an alcove in a wooden galleon). - /// - public static readonly ReverbProperties WoodenGalleonAlcove = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.1259f, - 0.3162f, - 1.2200f, - 0.6200f, - 0.9100f, - 1.1220f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7079f, - 0.0240f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4705.0000f, - 99.6000f, - 0.0000f, - 0x1 - ); - - /* Sports Presets */ - - /// - /// A reverb preset (approximating an empty sports stadium). - /// - public static readonly ReverbProperties SportEmptyStadium = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.4467f, - 0.7943f, - 6.2600f, - 0.5100f, - 1.1000f, - 0.0631f, - 0.1830f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.3981f, - 0.0380f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a squash court). - /// - public static readonly ReverbProperties SportSquashCourt = new ReverbProperties - ( - 1.0000f, - 0.7500f, - 0.3162f, - 0.3162f, - 0.7943f, - 2.2200f, - 0.9100f, - 1.1600f, - 0.4467f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7943f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1260f, - 0.1900f, - 0.2500f, - 0.0000f, - 0.9943f, - 7176.8999f, - 211.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a small swimming pool). - /// - public static readonly ReverbProperties SportSmallSwimmingPool = new ReverbProperties - ( - 1.0000f, - 0.7000f, - 0.3162f, - 0.7943f, - 0.8913f, - 2.7600f, - 1.2500f, - 1.1400f, - 0.6310f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7943f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1790f, - 0.1500f, - 0.8950f, - 0.1900f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a large swimming pool). - /// - public static readonly ReverbProperties SportLargeSwimmingPool = new ReverbProperties - ( - 1.0000f, - 0.8200f, - 0.3162f, - 0.7943f, - 1.0000f, - 5.4900f, - 1.3100f, - 1.1400f, - 0.4467f, - 0.0390f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.5012f, - 0.0490f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2220f, - 0.5500f, - 1.1590f, - 0.2100f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a gymnasium). - /// - public static readonly ReverbProperties SportGymnasium = new ReverbProperties - ( - 1.0000f, - 0.8100f, - 0.3162f, - 0.4467f, - 0.8913f, - 3.1400f, - 1.0600f, - 1.3500f, - 0.3981f, - 0.0290f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.5623f, - 0.0450f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1460f, - 0.1400f, - 0.2500f, - 0.0000f, - 0.9943f, - 7176.8999f, - 211.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a full stadium). - /// - public static readonly ReverbProperties SportFullStadium = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.0708f, - 0.7943f, - 5.2500f, - 0.1700f, - 0.8000f, - 0.1000f, - 0.1880f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2818f, - 0.0380f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a tannoy stadium). - /// - public static readonly ReverbProperties SportStadiumTannoy = new ReverbProperties - ( - 1.0000f, - 0.7800f, - 0.3162f, - 0.5623f, - 0.5012f, - 2.5300f, - 0.8800f, - 0.6800f, - 0.2818f, - 0.2300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.5012f, - 0.0630f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.2000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /* Prefab Presets */ - - /// - /// A reverb preset (approximating a workshop). - /// - public static readonly ReverbProperties PrefabWorkshop = new ReverbProperties - ( - 0.4287f, - 1.0000f, - 0.3162f, - 0.1413f, - 0.3981f, - 0.7600f, - 1.0000f, - 1.0000f, - 1.0000f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a school room). - /// - public static readonly ReverbProperties PrefabSchoolRoom = new ReverbProperties - ( - 0.4022f, - 0.6900f, - 0.3162f, - 0.6310f, - 0.5012f, - 0.9800f, - 0.4500f, - 0.1800f, - 1.4125f, - 0.0170f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0150f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.0950f, - 0.1400f, - 0.2500f, - 0.0000f, - 0.9943f, - 7176.8999f, - 211.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a practise room). - /// - public static readonly ReverbProperties PrefabPractiseRoom = new ReverbProperties - ( - 0.4022f, - 0.8700f, - 0.3162f, - 0.3981f, - 0.5012f, - 1.1200f, - 0.5600f, - 0.1800f, - 1.2589f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0110f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.0950f, - 0.1400f, - 0.2500f, - 0.0000f, - 0.9943f, - 7176.8999f, - 211.2000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an outhouse). - /// - public static readonly ReverbProperties PrefabOuthouse = new ReverbProperties - ( - 1.0000f, - 0.8200f, - 0.3162f, - 0.1122f, - 0.1585f, - 1.3800f, - 0.3800f, - 0.3500f, - 0.8913f, - 0.0240f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.6310f, - 0.0440f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1210f, - 0.1700f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 107.5000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a caravan). - /// - public static readonly ReverbProperties PrefabCaravan = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.0891f, - 0.1259f, - 0.4300f, - 1.5000f, - 1.0000f, - 1.0000f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.9953f, - 0.0120f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /* Dome and Pipe Presets */ - - /// - /// A reverb preset (approximating a dome in a tomb). - /// - public static readonly ReverbProperties DomeTomb = new ReverbProperties - ( - 1.0000f, - 0.7900f, - 0.3162f, - 0.3548f, - 0.2239f, - 4.1800f, - 0.2100f, - 0.1000f, - 0.3868f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.6788f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1770f, - 0.1900f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 20.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a small pipe). - /// - public static readonly ReverbProperties PipeSmall = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.3548f, - 0.2239f, - 5.0400f, - 0.1000f, - 0.1000f, - 0.5012f, - 0.0320f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 2.5119f, - 0.0150f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 20.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating the dome in St. Paul's Cathedral, London). - /// - public static readonly ReverbProperties DomeSaintPauls = new ReverbProperties - ( - 1.0000f, - 0.8700f, - 0.3162f, - 0.3548f, - 0.2239f, - 10.4800f, - 0.1900f, - 0.1000f, - 0.1778f, - 0.0900f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0420f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.1200f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 20.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a long, thin pipe). - /// - public static readonly ReverbProperties PipeLongThin = new ReverbProperties - ( - 0.2560f, - 0.9100f, - 0.3162f, - 0.4467f, - 0.2818f, - 9.2100f, - 0.1800f, - 0.1000f, - 0.7079f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7079f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 20.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a large pipe). - /// - public static readonly ReverbProperties PipeLarge = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.3548f, - 0.2239f, - 8.4500f, - 0.1000f, - 0.1000f, - 0.3981f, - 0.0460f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.5849f, - 0.0320f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 20.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a resonant pipe). - /// - public static readonly ReverbProperties PipeResonant = new ReverbProperties - ( - 0.1373f, - 0.9100f, - 0.3162f, - 0.4467f, - 0.2818f, - 6.8100f, - 0.1800f, - 0.1000f, - 0.7079f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.0000f, - 0.0220f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 20.0000f, - 0.0000f, - 0x0 - ); - - /* Outdoors Presets */ - - /// - /// A reverb preset (approximating an outdoors backyard). - /// - public static readonly ReverbProperties OutdoorsBackyard = new ReverbProperties - ( - 1.0000f, - 0.4500f, - 0.3162f, - 0.2512f, - 0.5012f, - 1.1200f, - 0.3400f, - 0.4600f, - 0.4467f, - 0.0690f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.7079f, - 0.0230f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2180f, - 0.3400f, - 0.2500f, - 0.0000f, - 0.9943f, - 4399.1001f, - 242.9000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating rolling plains). - /// - public static readonly ReverbProperties OutdoorsRollingPlains = new ReverbProperties - ( - 1.0000f, - 0.0000f, - 0.3162f, - 0.0112f, - 0.6310f, - 2.1300f, - 0.2100f, - 0.4600f, - 0.1778f, - 0.3000f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.4467f, - 0.0190f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 1.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4399.1001f, - 242.9000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a deep canyon). - /// - public static readonly ReverbProperties OutdoorsDeepCanyon = new ReverbProperties - ( - 1.0000f, - 0.7400f, - 0.3162f, - 0.1778f, - 0.6310f, - 3.8900f, - 0.2100f, - 0.4600f, - 0.3162f, - 0.2230f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.3548f, - 0.0190f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 1.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 4399.1001f, - 242.9000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a creek). - /// - public static readonly ReverbProperties OutdoorsCreek = new ReverbProperties - ( - 1.0000f, - 0.3500f, - 0.3162f, - 0.1778f, - 0.5012f, - 2.1300f, - 0.2100f, - 0.4600f, - 0.3981f, - 0.1150f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.1995f, - 0.0310f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2180f, - 0.3400f, - 0.2500f, - 0.0000f, - 0.9943f, - 4399.1001f, - 242.9000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a valley). - /// - public static readonly ReverbProperties OutdoorsValley = new ReverbProperties - ( - 1.0000f, - 0.2800f, - 0.3162f, - 0.0282f, - 0.1585f, - 2.8800f, - 0.2600f, - 0.3500f, - 0.1413f, - 0.2630f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.3981f, - 0.1000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.3400f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 107.5000f, - 0.0000f, - 0x0 - ); - - /* Mood Presets */ - - /// - /// A reverb preset (approximating a heavenly mood). - /// - public static readonly ReverbProperties MoodHeaven = new ReverbProperties - ( - 1.0000f, - 0.9400f, - 0.3162f, - 0.7943f, - 0.4467f, - 5.0400f, - 1.1200f, - 0.5600f, - 0.2427f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0290f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0800f, - 2.7420f, - 0.0500f, - 0.9977f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a hellish mood). - /// - public static readonly ReverbProperties MoodHell = new ReverbProperties - ( - 1.0000f, - 0.5700f, - 0.3162f, - 0.3548f, - 0.4467f, - 3.5700f, - 0.4900f, - 2.0000f, - 0.0000f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1100f, - 0.0400f, - 2.1090f, - 0.5200f, - 0.9943f, - 5000.0000f, - 139.5000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating an abstract memory). - /// - public static readonly ReverbProperties MoodMemory = new ReverbProperties - ( - 1.0000f, - 0.8500f, - 0.3162f, - 0.6310f, - 0.3548f, - 4.0600f, - 0.8200f, - 0.5600f, - 0.0398f, - 0.0000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.1220f, - 0.0000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.4740f, - 0.4500f, - 0.9886f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /* Driving Presets */ - - /// - /// A reverb preset (approximating a person in the commentator's seat). - /// - public static readonly ReverbProperties DrivingCommentator = new ReverbProperties - ( - 1.0000f, - 0.0000f, - 0.3162f, - 0.5623f, - 0.5012f, - 2.4200f, - 0.8800f, - 0.6800f, - 0.1995f, - 0.0930f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2512f, - 0.0170f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 1.0000f, - 0.2500f, - 0.0000f, - 0.9886f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a pit or garage). - /// - public static readonly ReverbProperties DrivingPitGarage = new ReverbProperties - ( - 0.4287f, - 0.5900f, - 0.3162f, - 0.7079f, - 0.5623f, - 1.7200f, - 0.9300f, - 0.8700f, - 0.5623f, - 0.0000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0160f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.1100f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating driving in a race car). - /// - public static readonly ReverbProperties DrivingInCarRacer = new ReverbProperties - ( - 0.0832f, - 0.8000f, - 0.3162f, - 1.0000f, - 0.7943f, - 0.1700f, - 2.0000f, - 0.4100f, - 1.7783f, - 0.0070f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7079f, - 0.0150f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 10268.2002f, - 251.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating driving in a sports car). - /// - public static readonly ReverbProperties DrivingInCarSports = new ReverbProperties - ( - 0.0832f, - 0.8000f, - 0.3162f, - 0.6310f, - 1.0000f, - 0.1700f, - 0.7500f, - 0.4100f, - 1.0000f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.5623f, - 0.0000f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 10268.2002f, - 251.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating driving in a luxury car). - /// - public static readonly ReverbProperties DrivingInCarLuxury = new ReverbProperties - ( - 0.2560f, - 1.0000f, - 0.3162f, - 0.1000f, - 0.5012f, - 0.1300f, - 0.4100f, - 0.4600f, - 0.7943f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.5849f, - 0.0100f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 10268.2002f, - 251.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating driving on a course with a full grand stand). - /// - public static readonly ReverbProperties DrivingFullGrandStand = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 0.2818f, - 0.6310f, - 3.0100f, - 1.3700f, - 1.2800f, - 0.3548f, - 0.0900f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1778f, - 0.0490f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 10420.2002f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating an empty grand stand). - /// - public static readonly ReverbProperties DrivingEmptyGrandStand = new ReverbProperties - ( - 1.0000f, - 1.0000f, - 0.3162f, - 1.0000f, - 0.7943f, - 4.6200f, - 1.7500f, - 1.4000f, - 0.2082f, - 0.0900f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2512f, - 0.0490f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.0000f, - 0.9943f, - 10420.2002f, - 250.0000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating driving in a tunnel). - /// - public static readonly ReverbProperties DrivingTunnel = new ReverbProperties - ( - 1.0000f, - 0.8100f, - 0.3162f, - 0.3981f, - 0.8913f, - 3.4200f, - 0.9400f, - 1.3100f, - 0.7079f, - 0.0510f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7079f, - 0.0470f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2140f, - 0.0500f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 155.3000f, - 0.0000f, - 0x1 - ); - - /* City Presets */ - - /// - /// A reverb preset (approximating city streets). - /// - public static readonly ReverbProperties CityStreets = new ReverbProperties - ( - 1.0000f, - 0.7800f, - 0.3162f, - 0.7079f, - 0.8913f, - 1.7900f, - 1.1200f, - 0.9100f, - 0.2818f, - 0.0460f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1995f, - 0.0280f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.2000f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a subway). - /// - public static readonly ReverbProperties CitySubway = new ReverbProperties - ( - 1.0000f, - 0.7400f, - 0.3162f, - 0.7079f, - 0.8913f, - 3.0100f, - 1.2300f, - 0.9100f, - 0.7079f, - 0.0460f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0280f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1250f, - 0.2100f, - 0.2500f, - 0.0000f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a museum). - /// - public static readonly ReverbProperties CityMuseum = new ReverbProperties - ( - 1.0000f, - 0.8200f, - 0.3162f, - 0.1778f, - 0.1778f, - 3.2800f, - 1.4000f, - 0.5700f, - 0.2512f, - 0.0390f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.8913f, - 0.0340f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1300f, - 0.1700f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 107.5000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating a library). - /// - public static readonly ReverbProperties CityLibrary = new ReverbProperties - ( - 1.0000f, - 0.8200f, - 0.3162f, - 0.2818f, - 0.0891f, - 2.7600f, - 0.8900f, - 0.4100f, - 0.3548f, - 0.0290f, - new Vector3(0.0000f, 0.0000f, -0.0000f), - 0.8913f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1300f, - 0.1700f, - 0.2500f, - 0.0000f, - 0.9943f, - 2854.3999f, - 107.5000f, - 0.0000f, - 0x0 - ); - - /// - /// A reverb preset (approximating an underpass). - /// - public static readonly ReverbProperties CityUnderpass = new ReverbProperties - ( - 1.0000f, - 0.8200f, - 0.3162f, - 0.4467f, - 0.8913f, - 3.5700f, - 1.1200f, - 0.9100f, - 0.3981f, - 0.0590f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.8913f, - 0.0370f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.1400f, - 0.2500f, - 0.0000f, - 0.9920f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating an abandoned location). - /// - public static readonly ReverbProperties CityAbandoned = new ReverbProperties - ( - 1.0000f, - 0.6900f, - 0.3162f, - 0.7943f, - 0.8913f, - 3.2800f, - 1.1700f, - 0.9100f, - 0.4467f, - 0.0440f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2818f, - 0.0240f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.2000f, - 0.2500f, - 0.0000f, - 0.9966f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /* Misc. Presets */ - - /// - /// A reverb preset (approximating a dusty room). - /// - public static readonly ReverbProperties DustyRoom = new ReverbProperties - ( - 0.3645f, - 0.5600f, - 0.3162f, - 0.7943f, - 0.7079f, - 1.7900f, - 0.3800f, - 0.2100f, - 0.5012f, - 0.0020f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.2589f, - 0.0060f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2020f, - 0.0500f, - 0.2500f, - 0.0000f, - 0.9886f, - 13046.0000f, - 163.3000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a chapel). - /// - public static readonly ReverbProperties Chapel = new ReverbProperties - ( - 1.0000f, - 0.8400f, - 0.3162f, - 0.5623f, - 1.0000f, - 4.6200f, - 0.6400f, - 1.2300f, - 0.4467f, - 0.0320f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.7943f, - 0.0490f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.2500f, - 0.0000f, - 0.2500f, - 0.1100f, - 0.9943f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x1 - ); - - /// - /// A reverb preset (approximating a small, water-filled room). - /// - public static readonly ReverbProperties SmallWaterRoom = new ReverbProperties - ( - 1.0000f, - 0.7000f, - 0.3162f, - 0.4477f, - 1.0000f, - 1.5100f, - 1.2500f, - 1.1400f, - 0.8913f, - 0.0200f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 1.4125f, - 0.0300f, - new Vector3(0.0000f, 0.0000f, 0.0000f), - 0.1790f, - 0.1500f, - 0.8950f, - 0.1900f, - 0.9920f, - 5000.0000f, - 250.0000f, - 0.0000f, - 0x0 - ); -} diff --git a/Robust.Shared/Audio/Effects/ReverbProperties.cs b/Robust.Shared/Audio/Effects/ReverbProperties.cs deleted file mode 100644 index 508cf1893e2..00000000000 --- a/Robust.Shared/Audio/Effects/ReverbProperties.cs +++ /dev/null @@ -1,211 +0,0 @@ -// -// ReverbProperties.cs -// -// Copyright (C) 2019 OpenTK -// -// This software may be modified and distributed under the terms -// of the MIT license. See the LICENSE file for details. -// - -using Robust.Shared.Maths; - -namespace Robust.Shared.Audio.Effects; - -/// -/// Defines a set of predefined reverb properties. -/// -public record struct ReverbProperties -{ - /// - /// Gets the preset value for . - /// - public float Density; - - /// - /// Gets the preset value for . - /// - public float Diffusion; - - /// - /// Gets the preset value for . - /// - public float Gain; - - /// - /// Gets the preset value for . - /// - public float GainHF; - - /// - /// Gets the preset value for . - /// - public float GainLF; - - /// - /// Gets the preset value for . - /// - public float DecayTime; - - /// - /// Gets the preset value for . - /// - public float DecayHFRatio; - - /// - /// Gets the preset value for . - /// - public float DecayLFRatio; - - /// - /// Gets the preset value for . - /// - public float ReflectionsGain; - - /// - /// Gets the preset value for . - /// - public float ReflectionsDelay; - - /// - /// Gets the preset value for . - /// - public Vector3 ReflectionsPan; - - /// - /// Gets the preset value for . - /// - public float LateReverbGain; - - /// - /// Gets the preset value for . - /// - public float LateReverbDelay; - - /// - /// Gets the preset value for . - /// - public Vector3 LateReverbPan; - - /// - /// Gets the preset value for . - /// - public float EchoTime; - - /// - /// Gets the preset value for . - /// - public float EchoDepth; - - /// - /// Gets the preset value for . - /// - public float ModulationTime; - - /// - /// Gets the preset value for . - /// - public float ModulationDepth; - - /// - /// Gets the preset value for . - /// - public float AirAbsorptionGainHF; - - /// - /// Gets the preset value for . - /// - public float HFReference; - - /// - /// Gets the preset value for . - /// - public float LFReference; - - /// - /// Gets the preset value for . - /// - public float RoomRolloffFactor; - - /// - /// Gets the preset value for . - /// - public int DecayHFLimit; - - /// - /// Initializes a new instance of the struct. - /// - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - /// See . - public ReverbProperties - ( - float density, - float diffusion, - float gain, - float gainHF, - float gainLF, - float decayTime, - float decayHFRatio, - float decayLFRatio, - float reflectionsGain, - float reflectionsDelay, - Vector3 reflectionsPan, - float lateReverbGain, - float lateReverbDelay, - Vector3 lateReverbPan, - float echoTime, - float echoDepth, - float modulationTime, - float modulationDepth, - float airAbsorptionGainHF, - float hfReference, - float lfReference, - float roomRolloffFactor, - int decayHFLimit - ) - { - Density = density; - Diffusion = diffusion; - Gain = gain; - GainHF = gainHF; - GainLF = gainLF; - DecayTime = decayTime; - DecayHFRatio = decayHFRatio; - DecayLFRatio = decayLFRatio; - ReflectionsGain = reflectionsGain; - ReflectionsDelay = reflectionsDelay; - ReflectionsPan = reflectionsPan; - LateReverbGain = lateReverbGain; - LateReverbDelay = lateReverbDelay; - LateReverbPan = lateReverbPan; - EchoTime = echoTime; - EchoDepth = echoDepth; - ModulationTime = modulationTime; - ModulationDepth = modulationDepth; - AirAbsorptionGainHF = airAbsorptionGainHF; - HFReference = hfReference; - LFReference = lfReference; - RoomRolloffFactor = roomRolloffFactor; - DecayHFLimit = decayHFLimit; - } -} diff --git a/Robust.Shared/Audio/HeadlessAudioManager.cs b/Robust.Shared/Audio/HeadlessAudioManager.cs deleted file mode 100644 index 6f81ad32f51..00000000000 --- a/Robust.Shared/Audio/HeadlessAudioManager.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Numerics; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Maths; - -namespace Robust.Shared.Audio; - -/// -/// Headless client audio. -/// -internal sealed class HeadlessAudioManager : SharedAudioManager, IAudioInternal -{ - /// - public void InitializePostWindowing() - { - } - - /// - public void Shutdown() - { - } - - /// - public void FlushALDisposeQueues() - { - } - - /// - public IAudioSource? CreateAudioSource(AudioStream stream) - { - return DummyAudioSource.Instance; - } - - /// - public IBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio = false) - { - return DummyBufferedAudioSource.Instance; - } - - /// - public void SetPosition(Vector2 position) - { - } - - /// - public void SetRotation(Angle angle) - { - } - - /// - public void SetMasterVolume(float value) - { - } - - /// - public void SetAttenuation(Attenuation attenuation) - { - } - - /// - public void StopAllAudio() - { - } - - /// - public void SetZOffset(float f) - { - } - - /// - public void _checkAlError(string callerMember = "", int callerLineNumber = -1) - { - } - - /// - public float GetAttenuationGain(float distance, float rolloffFactor, float referenceDistance, float maxDistance) - { - return 0f; - } -} diff --git a/Robust.Shared/Audio/IAudioInternal.cs b/Robust.Shared/Audio/IAudioInternal.cs deleted file mode 100644 index 28c3c55158e..00000000000 --- a/Robust.Shared/Audio/IAudioInternal.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Numerics; -using System.Runtime.CompilerServices; -using Robust.Shared.Audio.Sources; -using Robust.Shared.Maths; - -namespace Robust.Shared.Audio; - -/// -/// Handles clientside audio. -/// -internal interface IAudioInternal -{ - void InitializePostWindowing(); - void Shutdown(); - - /// - /// Flushes all pending queues for disposing of AL sources. - /// - void FlushALDisposeQueues(); - - IAudioSource? CreateAudioSource(AudioStream stream); - - IBufferedAudioSource CreateBufferedAudioSource(int buffers, bool floatAudio=false); - - /// - /// Sets position for the audio listener. - /// - void SetPosition(Vector2 position); - - /// - /// Sets rotation for the audio listener. - /// - void SetRotation(Angle angle); - - void SetMasterVolume(float value); - void SetAttenuation(Attenuation attenuation); - - /// - /// Stops all audio from playing. - /// - void StopAllAudio(); - - /// - /// Sets the Z-offset for the audio listener. - /// - void SetZOffset(float f); - - void _checkAlError([CallerMemberName] string callerMember = "", [CallerLineNumber] int callerLineNumber = -1); - - /// - /// Manually calculates the specified gain for an attenuation source with the specified distance. - /// - float GetAttenuationGain(float distance, float rolloffFactor, float referenceDistance, float maxDistance); -} diff --git a/Robust.Shared/Audio/IPlayingAudioStream.cs b/Robust.Shared/Audio/IPlayingAudioStream.cs new file mode 100644 index 00000000000..4315a5dcbc1 --- /dev/null +++ b/Robust.Shared/Audio/IPlayingAudioStream.cs @@ -0,0 +1,5 @@ +namespace Robust.Shared.Audio; +public interface IPlayingAudioStream +{ + void Stop(); +} diff --git a/Robust.Shared/Audio/SharedAudioManager.cs b/Robust.Shared/Audio/SharedAudioManager.cs deleted file mode 100644 index f08f73a7227..00000000000 --- a/Robust.Shared/Audio/SharedAudioManager.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.IO; -using Robust.Shared.Utility; - -namespace Robust.Shared.Audio; - -internal abstract class SharedAudioManager -{ - #region Loading - - public virtual AudioStream LoadAudioOggVorbis(Stream stream, string? name = null) - { - var vorbis = _readOggVorbis(stream); - var length = TimeSpan.FromSeconds(vorbis.TotalSamples / (double) vorbis.SampleRate); - return new AudioStream(null, length, (int) vorbis.Channels, name, vorbis.Title, vorbis.Artist); - } - - public virtual AudioStream LoadAudioWav(Stream stream, string? name = null) - { - var wav = _readWav(stream); - var length = TimeSpan.FromSeconds(wav.Data.Length / (double) wav.BlockAlign / wav.SampleRate); - return new AudioStream(null, length, wav.NumChannels, name); - } - - public virtual AudioStream LoadAudioRaw(ReadOnlySpan samples, int channels, int sampleRate, string? name = null) - { - var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate); - return new AudioStream(null, length, channels, name); - } - - #endregion - - #region Loading Data - - protected OggVorbisData _readOggVorbis(Stream stream) - { - using (var vorbis = new NVorbis.VorbisReader(stream, false)) - { - var sampleRate = vorbis.SampleRate; - var channels = vorbis.Channels; - var totalSamples = vorbis.TotalSamples; - - var readSamples = 0; - var buffer = new float[totalSamples * channels]; - - while (readSamples < totalSamples) - { - var read = vorbis.ReadSamples(buffer, readSamples * channels, buffer.Length - readSamples); - if (read == 0) - { - break; - } - - readSamples += read; - } - - return new OggVorbisData(totalSamples, sampleRate, channels, buffer, vorbis.Tags.Title, vorbis.Tags.Artist); - } - } - - internal readonly struct OggVorbisData - { - public readonly long TotalSamples; - public readonly long SampleRate; - public readonly long Channels; - public readonly ReadOnlyMemory Data; - public readonly string Title; - public readonly string Artist; - - public OggVorbisData(long totalSamples, long sampleRate, long channels, ReadOnlyMemory data, string title, string artist) - { - TotalSamples = totalSamples; - SampleRate = sampleRate; - Channels = channels; - Data = data; - Title = title; - Artist = artist; - } - } - - /// - /// Load up a WAVE file. - /// - protected static WavData _readWav(Stream stream) - { - var reader = new BinaryReader(stream, EncodingHelpers.UTF8, true); - - void SkipChunk() - { - var length = reader.ReadUInt32(); - stream.Position += length; - } - - // Read outer most chunks. - Span fourCc = stackalloc byte[4]; - while (true) - { - _readFourCC(reader, fourCc); - - if (!fourCc.SequenceEqual("RIFF"u8)) - { - SkipChunk(); - continue; - } - - return _readRiffChunk(reader); - } - } - - private static void _skipChunk(BinaryReader reader) - { - var length = reader.ReadUInt32(); - reader.BaseStream.Position += length; - } - - private static void _readFourCC(BinaryReader reader, Span fourCc) - { - fourCc[0] = reader.ReadByte(); - fourCc[1] = reader.ReadByte(); - fourCc[2] = reader.ReadByte(); - fourCc[3] = reader.ReadByte(); - } - - private static WavData _readRiffChunk(BinaryReader reader) - { - Span format = stackalloc byte[4]; - reader.ReadUInt32(); - _readFourCC(reader, format); - if (!format.SequenceEqual("WAVE"u8)) - { - throw new InvalidDataException("File is not a WAVE file."); - } - - _readFourCC(reader, format); - if (!format.SequenceEqual("fmt "u8)) - { - throw new InvalidDataException("Expected fmt chunk."); - } - - // Read fmt chunk. - - var size = reader.ReadInt32(); - var afterFmtPos = reader.BaseStream.Position + size; - - var audioType = (WavAudioFormatType) reader.ReadInt16(); - var channels = reader.ReadInt16(); - var sampleRate = reader.ReadInt32(); - var byteRate = reader.ReadInt32(); - var blockAlign = reader.ReadInt16(); - var bitsPerSample = reader.ReadInt16(); - - if (audioType != WavAudioFormatType.PCM) - { - throw new NotImplementedException("Unable to support audio types other than PCM."); - } - - DebugTools.Assert(byteRate == sampleRate * channels * bitsPerSample / 8); - - // Fmt is not of guaranteed size, so use the size header to skip to the end. - reader.BaseStream.Position = afterFmtPos; - - while (true) - { - _readFourCC(reader, format); - if (!format.SequenceEqual("data"u8)) - { - _skipChunk(reader); - continue; - } - - break; - } - - // We are in the data chunk. - size = reader.ReadInt32(); - var data = reader.ReadBytes(size); - - return new WavData(audioType, channels, sampleRate, byteRate, blockAlign, bitsPerSample, data); - } - - /// - /// See http://soundfile.sapp.org/doc/WaveFormat/ for reference. - /// - internal readonly struct WavData - { - public readonly WavAudioFormatType AudioType; - public readonly short NumChannels; - public readonly int SampleRate; - public readonly int ByteRate; - public readonly short BlockAlign; - public readonly short BitsPerSample; - public readonly ReadOnlyMemory Data; - - public WavData(WavAudioFormatType audioType, short numChannels, int sampleRate, int byteRate, - short blockAlign, short bitsPerSample, ReadOnlyMemory data) - { - AudioType = audioType; - NumChannels = numChannels; - SampleRate = sampleRate; - ByteRate = byteRate; - BlockAlign = blockAlign; - BitsPerSample = bitsPerSample; - Data = data; - } - } - - internal enum WavAudioFormatType : short - { - Unknown = 0, - PCM = 1, - // There's a bunch of other types, those are all unsupported. - } - - #endregion -} diff --git a/Robust.Shared/Audio/SoundSystem.cs b/Robust.Shared/Audio/SoundSystem.cs new file mode 100644 index 00000000000..dcb5dc05854 --- /dev/null +++ b/Robust.Shared/Audio/SoundSystem.cs @@ -0,0 +1,69 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Player; +using System; + +namespace Robust.Shared.Audio +{ + /// + /// A static proxy class for interfacing with the AudioSystem. + /// + public static class SoundSystem + { + // These functions are obsolete and I CBF adding new arguments to them. + private static bool _recordReplay = false; + + /// + /// Play an audio file globally, without position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The set of players that will hear the sound. + /// Audio parameters to apply when playing the sound. + [Obsolete("Use SharedAudioSystem.PlayGlobal()")] + public static IPlayingAudioStream? Play(string filename, Filter playerFilter, AudioParams? audioParams = null) + { + var entSystMan = IoCManager.Resolve(); + + // Some timers try to play audio after the system has shut down? + entSystMan.TryGetEntitySystem(out SharedAudioSystem? audio); + return audio?.PlayGlobal(filename, playerFilter, _recordReplay, audioParams); + } + + /// + /// Play an audio file following an entity. + /// + /// The resource path to the OGG Vorbis file to play. + /// The set of players that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. + [Obsolete("Use SharedAudioSystem")] + public static IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityUid uid, + AudioParams? audioParams = null) + { + var entSystMan = IoCManager.Resolve(); + + // Some timers try to play audio after the system has shut down? + entSystMan.TryGetEntitySystem(out SharedAudioSystem? audio); + return audio?.Play(filename, playerFilter, uid, _recordReplay, audioParams); + } + + /// + /// Play an audio file at a static position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The set of players that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + [Obsolete("Use SharedAudioSystem")] + public static IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityCoordinates coordinates, + AudioParams? audioParams = null) + { + var entSystMan = IoCManager.Resolve(); + + // Some timers try to play audio after the system has shut down? + entSystMan.TryGetEntitySystem(out SharedAudioSystem? audio); + return audio?.Play(filename, playerFilter, coordinates, _recordReplay, audioParams); + } + } +} diff --git a/Robust.Shared/Audio/Sources/DummyAudioSource.cs b/Robust.Shared/Audio/Sources/DummyAudioSource.cs deleted file mode 100644 index 75338b61cea..00000000000 --- a/Robust.Shared/Audio/Sources/DummyAudioSource.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System.Numerics; -using Robust.Shared.Audio.Effects; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Robust.Shared.Audio.Sources; - -/// -/// Hey look, it's AudioSource's evil twin brother! -/// -[Virtual] -[DataDefinition] -internal partial class DummyAudioSource : IAudioSource -{ - public static DummyAudioSource Instance { get; } = new(); - - public void Dispose() - { - } - - /// - public void Pause() - { - } - - /// - public void StartPlaying() - { - } - - /// - public void StopPlaying() - { - } - - /// - public bool Playing { get; set; } - - /// - [DataField] - public bool Looping { get; set; } - - /// - [DataField] - public bool Global { get; set; } - - /// - public Vector2 Position { get; set; } - - /// - [DataField] - public float Pitch { get; set; } - - /// - public float Volume { get; set; } - - /// - public float Gain { get; set; } - - /// - [DataField] - public float MaxDistance { get; set; } - - /// - [DataField] - public float RolloffFactor { get; set; } - - /// - [DataField] - public float ReferenceDistance { get; set; } - - /// - public float Occlusion { get; set; } - - /// - public float PlaybackPosition { get; set; } - - /// - public Vector2 Velocity { get; set; } - - public void SetAuxiliary(IAuxiliaryAudio? audio) - { - } -} diff --git a/Robust.Shared/Audio/Sources/IAudioSource.cs b/Robust.Shared/Audio/Sources/IAudioSource.cs deleted file mode 100644 index f81f8505330..00000000000 --- a/Robust.Shared/Audio/Sources/IAudioSource.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Numerics; -using Robust.Shared.Audio.Effects; -using Robust.Shared.GameObjects; - -namespace Robust.Shared.Audio.Sources; - -/// -/// Engine audio source that directly interacts with OpenAL. -/// -/// -/// This just exists so client can interact with OpenAL and server can interact with nothing. -/// -internal interface IAudioSource : IDisposable -{ - void Pause(); - - /// - /// Tries to start playing the audio if not already playing. - /// - void StartPlaying(); - - /// - /// Stops playing a source if it is currently playing. - /// - void StopPlaying(); - - /// - /// Is the audio source currently playing. - /// - bool Playing { get; set; } - - /// - /// Will the audio source loop when finished playing? - /// - bool Looping { get; set; } - - /// - /// Is the audio source relative to the listener or to the world (global or local). - /// - bool Global { get; set; } - - /// - /// Position of the audio relative to listener. - /// - Vector2 Position { get; set; } - - float Pitch { get; set; } - - /// - /// Decibels of the audio. Converted to gain when setting. - /// - float Volume { get; set; } - - /// - /// Direct gain for audio. - /// - float Gain { get; set; } - - float MaxDistance { get; set; } - - float RolloffFactor { get; set; } - - float ReferenceDistance { get; set; } - - float Occlusion { get; set; } - - /// - /// Current playback position. - /// - float PlaybackPosition { get; set; } - - /// - /// Audio source velocity. Used for the doppler effect. - /// - Vector2 Velocity { get; set; } - - /// - /// Set the auxiliary sendfilter for this audio source. - /// - void SetAuxiliary(IAuxiliaryAudio? audio); -} diff --git a/Robust.Shared/Audio/Sources/IBufferedAudioSource.cs b/Robust.Shared/Audio/Sources/IBufferedAudioSource.cs deleted file mode 100644 index 41c7fefba4b..00000000000 --- a/Robust.Shared/Audio/Sources/IBufferedAudioSource.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Robust.Shared.Audio.Sources; - -internal interface IBufferedAudioSource : IAudioSource -{ - int SampleRate { get; set; } - int GetNumberOfBuffersProcessed(); - void GetBuffersProcessed(Span handles); - void WriteBuffer(int handle, ReadOnlySpan data); - void WriteBuffer(int handle, ReadOnlySpan data); - void QueueBuffers(ReadOnlySpan handles); - void EmptyBuffers(); -} diff --git a/Robust.Shared/Audio/Systems/SharedAudioSystem.Effects.cs b/Robust.Shared/Audio/Systems/SharedAudioSystem.Effects.cs deleted file mode 100644 index 9e5eca6dfa5..00000000000 --- a/Robust.Shared/Audio/Systems/SharedAudioSystem.Effects.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System.Collections.Generic; -using Robust.Shared.Audio.Components; -using Robust.Shared.Audio.Effects; -using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Utility; - -namespace Robust.Shared.Audio.Systems; - -public abstract partial class SharedAudioSystem -{ - /* - * This is somewhat limiting but also is the easiest way to expose it to content for now. - */ - - /// - /// Pre-calculated auxiliary effect slots for audio presets. - /// - public IReadOnlyDictionary Auxiliaries => _auxiliaries; - - protected readonly Dictionary _auxiliaries = new(); - - protected virtual void InitializeEffect() - { - SubscribeLocalEvent(OnPresetStartup); - SubscribeLocalEvent(OnPresetShutdown); - } - - private void OnPresetStartup(EntityUid uid, AudioPresetComponent component, ComponentStartup args) - { - _auxiliaries[component.Preset] = uid; - } - - private void OnPresetShutdown(EntityUid uid, AudioPresetComponent component, ComponentShutdown args) - { - _auxiliaries.Remove(component.Preset); - } - - /// - /// Creates an auxiliary audio slot that can have an audio source or audio effect applied to it. - /// - public virtual (EntityUid Entity, AudioAuxiliaryComponent Component) CreateAuxiliary() - { - var ent = Spawn(null, MapCoordinates.Nullspace); - var comp = AddComp(ent); - return (ent, comp); - } - - /// - /// Creates an audio effect that can be used with an auxiliary audio slot. - /// - public virtual (EntityUid Entity, AudioEffectComponent Component) CreateEffect() - { - var ent = Spawn(null, MapCoordinates.Nullspace); - var comp = AddComp(ent); - return (ent, comp); - } - - /// - /// Sets the auxiliary effect slot for a specified audio source. - /// - public virtual void SetAuxiliary(EntityUid uid, AudioComponent audio, EntityUid? auxUid) - { - DebugTools.Assert(auxUid == null || HasComp(auxUid)); - audio.Auxiliary = auxUid; - Dirty(uid, audio); - } - - /// - /// Sets the audio effect for a specified auxiliary effect slot. - /// - public virtual void SetEffect(EntityUid auxUid, AudioAuxiliaryComponent aux, EntityUid? effectUid) - { - DebugTools.Assert(effectUid == null || HasComp(effectUid)); - aux.Effect = effectUid; - Dirty(auxUid, aux); - } - - public void SetEffect(EntityUid? audioUid, AudioComponent? component, string effectProto) - { - if (audioUid == null || component == null) - return; - - SetAuxiliary(audioUid.Value, component, _auxiliaries[effectProto]); - } - - /// - /// Applies an audio preset prototype to an audio effect entity. - /// - public void SetEffectPreset(EntityUid effectUid, AudioEffectComponent effectComp, AudioPresetPrototype preset) - { - effectComp.Density = preset.Density; - effectComp.Diffusion = preset.Diffusion; - effectComp.Gain = preset.Gain; - effectComp.GainHF = preset.GainHF; - effectComp.GainLF = preset.GainLF; - effectComp.DecayTime = preset.DecayTime; - effectComp.DecayHFRatio = preset.DecayHFRatio; - effectComp.DecayLFRatio = preset.DecayLFRatio; - effectComp.ReflectionsGain = preset.ReflectionsGain; - effectComp.ReflectionsDelay = preset.ReflectionsDelay; - effectComp.ReflectionsPan = preset.ReflectionsPan; - effectComp.LateReverbGain = preset.LateReverbGain; - effectComp.LateReverbDelay = preset.LateReverbDelay; - effectComp.LateReverbPan = preset.LateReverbPan; - effectComp.EchoTime = preset.EchoTime; - effectComp.EchoDepth = preset.EchoDepth; - effectComp.ModulationTime = preset.ModulationTime; - effectComp.ModulationDepth = preset.ModulationDepth; - effectComp.AirAbsorptionGainHF = preset.AirAbsorptionGainHF; - effectComp.HFReference = preset.HFReference; - effectComp.LFReference = preset.LFReference; - effectComp.RoomRolloffFactor = preset.RoomRolloffFactor; - effectComp.DecayHFLimit = preset.DecayHFLimit; - - Dirty(effectUid, effectComp); - } - - /// - /// Applies an EAX reverb effect preset to an audio effect. - /// - public void SetEffectPreset(EntityUid effectUid, AudioEffectComponent effectComp, ReverbProperties preset) - { - effectComp.Density = preset.Density; - effectComp.Diffusion = preset.Diffusion; - effectComp.Gain = preset.Gain; - effectComp.GainHF = preset.GainHF; - effectComp.GainLF = preset.GainLF; - effectComp.DecayTime = preset.DecayTime; - effectComp.DecayHFRatio = preset.DecayHFRatio; - effectComp.DecayLFRatio = preset.DecayLFRatio; - effectComp.ReflectionsGain = preset.ReflectionsGain; - effectComp.ReflectionsDelay = preset.ReflectionsDelay; - effectComp.ReflectionsPan = preset.ReflectionsPan; - effectComp.LateReverbGain = preset.LateReverbGain; - effectComp.LateReverbDelay = preset.LateReverbDelay; - effectComp.LateReverbPan = preset.LateReverbPan; - effectComp.EchoTime = preset.EchoTime; - effectComp.EchoDepth = preset.EchoDepth; - effectComp.ModulationTime = preset.ModulationTime; - effectComp.ModulationDepth = preset.ModulationDepth; - effectComp.AirAbsorptionGainHF = preset.AirAbsorptionGainHF; - effectComp.HFReference = preset.HFReference; - effectComp.LFReference = preset.LFReference; - effectComp.RoomRolloffFactor = preset.RoomRolloffFactor; - effectComp.DecayHFLimit = preset.DecayHFLimit; - - Dirty(effectUid, effectComp); - } -} diff --git a/Robust.Shared/Audio/Systems/SharedAudioSystem.cs b/Robust.Shared/Audio/Systems/SharedAudioSystem.cs deleted file mode 100644 index 9649e239134..00000000000 --- a/Robust.Shared/Audio/Systems/SharedAudioSystem.cs +++ /dev/null @@ -1,489 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Robust.Shared.Audio.Components; -using Robust.Shared.Configuration; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Network; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.ResourceManagement.ResourceTypes; -using Robust.Shared.Serialization; -using Robust.Shared.Spawners; -using Robust.Shared.Timing; -using Robust.Shared.Utility; - -namespace Robust.Shared.Audio.Systems; - -/// -/// Handles audio for robust toolbox inside of the sim. -/// -/// -/// Interacts with AudioManager internally. -/// -public abstract partial class SharedAudioSystem : EntitySystem -{ - [Dependency] protected readonly IConfigurationManager CfgManager = default!; - [Dependency] protected readonly IGameTiming Timing = default!; - [Dependency] private readonly INetManager _netManager = default!; - [Dependency] protected readonly IPrototypeManager ProtoMan = default!; - [Dependency] private readonly IResourceCache _resource = default!; - [Dependency] protected readonly IRobustRandom RandMan = default!; - - /// - /// Default max range at which the sound can be heard. - /// - public const float DefaultSoundRange = 20; - - /// - /// Used in the PAS to designate the physics collision mask of occluders. - /// - public int OcclusionCollisionMask { get; set; } - - public float ZOffset; - - public override void Initialize() - { - base.Initialize(); - InitializeEffect(); - ZOffset = CfgManager.GetCVar(CVars.AudioZOffset); - CfgManager.OnValueChanged(CVars.AudioZOffset, SetZOffset); - SubscribeLocalEvent(OnAudioGetStateAttempt); - SubscribeLocalEvent(OnAudioUnpaused); - } - - public override void Shutdown() - { - base.Shutdown(); - CfgManager.UnsubValueChanged(CVars.AudioZOffset, SetZOffset); - } - - protected virtual void SetZOffset(float value) - { - var query = AllEntityQuery(); - var oldZOffset = ZOffset; - ZOffset = value; - - while (query.MoveNext(out var uid, out var audio)) - { - // Pythagoras back to normal then adjust. - var maxDistance = MathF.Pow(audio.Params.MaxDistance, 2) - oldZOffset; - var refDistance = MathF.Pow(audio.Params.ReferenceDistance, 2) - oldZOffset; - - audio.Params.MaxDistance = maxDistance; - audio.Params.ReferenceDistance = refDistance; - audio.Params = GetAdjustedParams(audio.Params); - Dirty(uid, audio); - } - } - - protected virtual void OnAudioUnpaused(EntityUid uid, AudioComponent component, ref EntityUnpausedEvent args) - { - component.AudioStart += args.PausedTime; - } - - private void OnAudioGetStateAttempt(EntityUid uid, AudioComponent component, ref ComponentGetStateAttemptEvent args) - { - var playerEnt = args.Player?.AttachedEntity; - - if ((component.ExcludedEntity != null && playerEnt == component.ExcludedEntity) || - (playerEnt != null && component.IncludedEntities != null && !component.IncludedEntities.Contains(playerEnt.Value))) - { - args.Cancelled = true; - } - } - - /// - /// Considers Z-offset for audio and gets the adjusted distance. - /// - /// - /// Really it's just doing pythagoras for you. - /// - public float GetAudioDistance(float length) - { - return MathF.Sqrt(MathF.Pow(length, 2) + MathF.Pow(ZOffset, 2)); - } - - /// - /// Resolves the filepath to a sound file. - /// - public string GetSound(SoundSpecifier specifier) - { - switch (specifier) - { - case SoundPathSpecifier path: - return path.Path == default ? string.Empty : path.Path.ToString(); - - case SoundCollectionSpecifier collection: - { - if (collection.Collection == null) - return string.Empty; - - var soundCollection = ProtoMan.Index(collection.Collection); - return RandMan.Pick(soundCollection.PickFiles).ToString(); - } - } - - return string.Empty; - } - - #region AudioParams - - protected AudioComponent SetupAudio(EntityUid uid, string fileName, AudioParams? audioParams) - { - DebugTools.Assert(!string.IsNullOrEmpty(fileName)); - audioParams ??= AudioParams.Default; - var comp = AddComp(uid); - comp.FileName = fileName; - comp.Params = GetAdjustedParams(audioParams.Value); - comp.AudioStart = Timing.CurTime; - - if (!audioParams.Value.Loop) - { - var length = GetAudioLength(fileName); - - var despawn = AddComp(uid); - // Don't want to clip audio too short due to imprecision. - despawn.Lifetime = (float) length.TotalSeconds + 0.01f; - } - - return comp; - } - - /// - /// Accounts for ZOffset on audio distance. - /// - private AudioParams GetAdjustedParams(AudioParams audioParams) - { - var maxDistance = GetAudioDistance(audioParams.MaxDistance); - var refDistance = GetAudioDistance(audioParams.ReferenceDistance); - - return audioParams - .WithMaxDistance(maxDistance) - .WithReferenceDistance(refDistance); - } - - /// - /// Sets the audio params volume for an entity. - /// - public void SetVolume(EntityUid? entity, float value, Components.AudioComponent? component = null) - { - if (entity == null || !Resolve(entity.Value, ref component)) - return; - - if (component.Params.Volume.Equals(value)) - return; - - component.Params.Volume = value; - Dirty(entity.Value, component); - } - - #endregion - - /// - /// Gets the timespan of the specified audio. - /// - public TimeSpan GetAudioLength(string filename) - { - var resource = _resource.GetResource(filename); - return resource.AudioStream.Length; - } - - /// - /// Stops the specified audio entity from playing. - /// - /// - /// Returns null so you can inline the call. - /// - public EntityUid? Stop(EntityUid? uid, Components.AudioComponent? component = null) - { - // One morbillion warnings for logging missing. - if (uid == null || !Resolve(uid.Value, ref component, false)) - return null; - - if (!Timing.IsFirstTimePredicted || (_netManager.IsClient && !IsClientSide(uid.Value))) - return null; - - QueueDel(uid); - return null; - } - - /// - /// Play an audio file globally, without position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The set of players that will hear the sound. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null); - - /// - /// Play an audio file globally, without position. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The set of players that will hear the sound. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(SoundSpecifier? sound, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) - { - return sound == null ? null : PlayGlobal(GetSound(sound), playerFilter, recordReplay, sound.Params); - } - - /// - /// Play an audio file globally, without position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The player that will hear the sound. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null); - - /// - /// Play an audio file globally, without position. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The player that will hear the sound. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(SoundSpecifier? sound, ICommonSession recipient) - { - return sound == null ? null : PlayGlobal(GetSound(sound), recipient, sound.Params); - } - - /// - /// Play an audio file globally, without position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The player that will hear the sound. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null); - - /// - /// Play an audio file globally, without position. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The player that will hear the sound. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(SoundSpecifier? sound, EntityUid recipient, AudioParams? audioParams = null) - { - return sound == null ? null : PlayGlobal(GetSound(sound), recipient, sound.Params); - } - - /// - /// Play an audio file following an entity. - /// - /// The resource path to the OGG Vorbis file to play. - /// The set of players that will hear the sound. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null); - - /// - /// Play an audio file following an entity. - /// - /// The resource path to the OGG Vorbis file to play. - /// The player that will hear the sound. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null); - - /// - /// Play an audio file following an entity. - /// - /// The resource path to the OGG Vorbis file to play. - /// The player that will hear the sound. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null); - - /// - /// Play an audio file following an entity. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The set of players that will hear the sound. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(SoundSpecifier? sound, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null) - { - return sound == null ? null : PlayEntity(GetSound(sound), playerFilter, uid, recordReplay, audioParams ?? sound.Params); - } - - /// - /// Play an audio file following an entity. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The player that will hear the sound. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(SoundSpecifier? sound, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) - { - return sound == null ? null : PlayEntity(GetSound(sound), recipient, uid, audioParams ?? sound.Params); - } - - /// - /// Play an audio file following an entity. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The player that will hear the sound. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(SoundSpecifier? sound, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) - { - return sound == null ? null : PlayEntity(GetSound(sound), recipient, uid, audioParams ?? sound.Params); - } - - /// - /// Play an audio file following an entity for every entity in PVS range. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(SoundSpecifier? sound, EntityUid uid, AudioParams? audioParams = null) - { - return sound == null ? null : PlayPvs(GetSound(sound), uid, audioParams ?? sound.Params); - } - - /// - /// Play an audio file at the specified EntityCoordinates for every entity in PVS range. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The EntityCoordinates to attach the audio source to. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(SoundSpecifier? sound, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - return sound == null ? null : PlayPvs(GetSound(sound), coordinates, audioParams ?? sound.Params); - } - - /// - /// Play an audio file at the specified EntityCoordinates for every entity in PVS range. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The EntityCoordinates to attach the audio source to. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(string filename, - EntityCoordinates coordinates, AudioParams? audioParams = null); - - /// - /// Play an audio file following an entity for every entity in PVS range. - /// - /// The resource path to the OGG Vorbis file to play. - /// The UID of the entity "emitting" the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(string filename, EntityUid uid, - AudioParams? audioParams = null); - - /// - /// Plays a predicted sound following an entity. The server will send the sound to every player in PVS range, - /// unless that player is attached to the "user" entity that initiated the sound. The client-side system plays - /// this sound as normal - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The UID of the entity "emitting" the audio. - /// The UID of the user that initiated this sound. This is usually some player's controlled entity. - [return: NotNullIfNotNull("sound")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPredicted(SoundSpecifier? sound, EntityUid source, EntityUid? user, AudioParams? audioParams = null); - - /// - /// Plays a predicted sound following an EntityCoordinates. The server will send the sound to every player in PVS range, - /// unless that player is attached to the "user" entity that initiated the sound. The client-side system plays - /// this sound as normal - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The entitycoordinates "emitting" the audio - /// The UID of the user that initiated this sound. This is usually some player's controlled entity. - [return: NotNullIfNotNull("sound")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPredicted(SoundSpecifier? sound, EntityCoordinates coordinates, EntityUid? user, AudioParams? audioParams = null); - - /// - /// Play an audio file at a static position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The set of players that will hear the sound. - /// The coordinates at which to play the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null); - - /// - /// Play an audio file at a static position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The player that will hear the sound. - /// The coordinates at which to play the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); - - /// - /// Play an audio file at a static position. - /// - /// The resource path to the OGG Vorbis file to play. - /// The player that will hear the sound. - /// The coordinates at which to play the audio. - [return: NotNullIfNotNull("filename")] - public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); - - /// - /// Play an audio file at a static position. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The set of players that will hear the sound. - /// The coordinates at which to play the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(SoundSpecifier? sound, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) - { - return sound == null ? null : PlayStatic(GetSound(sound), playerFilter, coordinates, recordReplay); - } - - /// - /// Play an audio file at a static position. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The player that will hear the sound. - /// The coordinates at which to play the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(SoundSpecifier? sound, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - return sound == null ? null : PlayStatic(GetSound(sound), recipient, coordinates, audioParams ?? sound.Params); - } - - /// - /// Play an audio file at a static position. - /// - /// The sound specifier that points the audio file(s) that should be played. - /// The player that will hear the sound. - /// The coordinates at which to play the audio. - [return: NotNullIfNotNull("sound")] - public (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(SoundSpecifier? sound, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) - { - return sound == null ? null : PlayStatic(GetSound(sound), recipient, coordinates, audioParams ?? sound.Params); - } - - // These are just here for replays now. - // We don't actually need them in shared, or netserializable, but this makes net serialization - // and replays happy - - // TODO: This is quite bandwidth intensive. - // Sending bus names and file names as strings is expensive and can be optimized. - // Also there's redundant fields in AudioParams in most cases. - [NetSerializable, Serializable] - protected abstract class AudioMessage : EntityEventArgs - { - public string FileName = string.Empty; - public AudioParams AudioParams; - } - - [NetSerializable, Serializable] - protected sealed class PlayAudioGlobalMessage : AudioMessage - { - } - - [NetSerializable, Serializable] - protected sealed class PlayAudioPositionalMessage : AudioMessage - { - public NetCoordinates Coordinates; - } - - [NetSerializable, Serializable] - protected sealed class PlayAudioEntityMessage : AudioMessage - { - public NetEntity NetEntity; - } -} diff --git a/Robust.Shared/CVars.cs b/Robust.Shared/CVars.cs index 484357e944f..d05eb795812 100644 --- a/Robust.Shared/CVars.cs +++ b/Robust.Shared/CVars.cs @@ -3,7 +3,6 @@ using System.Threading; using Lidgren.Network; using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Log; @@ -54,6 +53,16 @@ protected CVars() public static readonly CVarDef NetReceiveBufferSize = CVarDef.Create("net.receivebuffersize", 131071, CVar.ARCHIVE); + /// + /// Size of the pool for Lidgren's array buffers to send messages. + /// Set to 0 to disable pooling; max is 8192. + /// + /// + /// Higher just means more potentially wasted space and slower pool retrieval. + /// + public static readonly CVarDef NetPoolSize = + CVarDef.Create("net.pool_size", 512, CVar.CLIENT | CVar.SERVER); + /// /// Maximum UDP payload size to send. /// @@ -120,6 +129,13 @@ protected CVars() public static readonly CVarDef NetBufferSize = CVarDef.Create("net.buffer_size", 2, CVar.ARCHIVE | CVar.CLIENTONLY); + /// + /// The maximum size of the game state buffer. If this is exceeded the client will request a full game state. + /// Values less than will be ignored. + /// + public static readonly CVarDef NetMaxBufferSize = + CVarDef.Create("net.max_buffer_size", 512, CVar.ARCHIVE | CVar.CLIENTONLY); + /// /// Enable verbose game state/networking logging. /// @@ -1001,7 +1017,7 @@ protected CVars() */ public static readonly CVarDef AudioAttenuation = - CVarDef.Create("audio.attenuation", (int) Attenuation.LinearDistanceClamped, CVar.REPLICATED | CVar.ARCHIVE); + CVarDef.Create("audio.attenuation", (int) Attenuation.Default, CVar.REPLICATED | CVar.ARCHIVE); /// /// Audio device to try to output audio to by default. @@ -1021,10 +1037,6 @@ protected CVars() public static readonly CVarDef AudioRaycastLength = CVarDef.Create("audio.raycast_length", SharedAudioSystem.DefaultSoundRange, CVar.ARCHIVE | CVar.CLIENTONLY); - public static readonly CVarDef AudioZOffset = - CVarDef.Create("audio.z_offset", -1f, CVar.REPLICATED); - - /* * PLAYER */ diff --git a/Robust.Shared/Console/Commands/MapCommands.cs b/Robust.Shared/Console/Commands/MapCommands.cs index e0ff05b29f8..b9dfe0be647 100644 --- a/Robust.Shared/Console/Commands/MapCommands.cs +++ b/Robust.Shared/Console/Commands/MapCommands.cs @@ -131,6 +131,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) internal sealed class ListMapsCommand : LocalizedCommands { + [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IMapManager _map = default!; public override string Command => "lsmap"; @@ -144,10 +145,13 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) foreach (var mapId in _map.GetAllMapIds().OrderBy(id => id.Value)) { - msg.AppendFormat("{0}: init: {1}, paused: {2}, ent: {3}, grids: {4}\n", - mapId, _map.IsMapInitialized(mapId), + var mapUid = _map.GetMapEntityId(mapId); + + msg.AppendFormat("{0}: {1}, init: {2}, paused: {3}, nent: {4}, grids: {5}\n", + mapId, _entManager.GetComponent(mapUid).EntityName, + _map.IsMapInitialized(mapId), _map.IsMapPaused(mapId), - _map.GetMapEntityId(mapId), + _entManager.GetNetEntity(_map.GetMapEntityId(mapId)), string.Join(",", _map.GetAllGrids(mapId).Select(grid => grid.Owner))); } diff --git a/Robust.Shared/Containers/BaseContainer.cs b/Robust.Shared/Containers/BaseContainer.cs index 8108930b571..5e07ae6c8cc 100644 --- a/Robust.Shared/Containers/BaseContainer.cs +++ b/Robust.Shared/Containers/BaseContainer.cs @@ -79,20 +79,7 @@ internal void Init(string id, EntityUid owner, ContainerManagerComponent compone Owner = owner; } - /// - /// Attempts to insert the entity into this container. - /// - /// - /// If the insertion is successful, the inserted entity will end up parented to the - /// container entity, and the inserted entity's local position will be set to the zero vector. - /// - /// The entity to insert. - /// - /// False if the entity could not be inserted. - /// - /// Thrown if this container is a child of the entity, - /// which would cause infinite loops. - /// + [Obsolete("Use container system method")] public bool Insert( EntityUid toinsert, IEntityManager? entMan = null, @@ -138,16 +125,23 @@ public bool Insert( return false; } + transform ??= transformQuery.GetComponent(toinsert); + //Verify we can insert into this container - if (!force && !containerSys.CanInsert(toinsert, this)) + if (!force && !containerSys.CanInsert(toinsert, this, containerXform: ownerTransform)) return false; // Please somebody ecs containers var lookupSys = entMan.EntitySysManager.GetEntitySystem(); var xformSys = entMan.EntitySysManager.GetEntitySystem(); - transform ??= transformQuery.GetComponent(toinsert); meta ??= entMan.GetComponent(toinsert); + if (meta.EntityLifeStage >= EntityLifeStage.Terminating) + { + Logger.ErrorS("container", + $"Attempted to insert a terminating entity {entMan.ToPrettyString(toinsert)} into a container {ID} in entity: {entMan.ToPrettyString(Owner)}."); + return false; + } // remove from any old containers. if ((meta.Flags & MetaDataFlags.InContainer) != 0 && @@ -270,16 +264,7 @@ internal void RecursivelyUpdateJoints( /// Whether to assume that the container is currently empty. protected internal virtual bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan) => true; - /// - /// Attempts to remove the entity from this container. - /// - /// If false, this operation will not rigger a move or parent change event. Ignored if - /// destination is not null - /// If true, this will not perform can-remove checks. - /// Where to place the entity after removing. Avoids unnecessary broadphase updates. - /// If not specified, and reparent option is true, then the entity will either be inserted into a parent - /// container, the grid, or the map. - /// Optional final local rotation after removal. Avoids redundant move events. + [Obsolete("Use container system method")] public bool Remove( EntityUid toRemove, IEntityManager? entMan = null, @@ -359,7 +344,7 @@ public bool Remove( return true; } - [Obsolete("use force option in Remove()")] + [Obsolete("Use container system method")] public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null) => Remove(toRemove, entMan, meta: meta, reparent: false, force: true); diff --git a/Robust.Shared/Containers/SharedContainerSystem.Insert.cs b/Robust.Shared/Containers/SharedContainerSystem.Insert.cs index 681696fb699..62bd224d201 100644 --- a/Robust.Shared/Containers/SharedContainerSystem.Insert.cs +++ b/Robust.Shared/Containers/SharedContainerSystem.Insert.cs @@ -1,10 +1,41 @@ +using System; using Robust.Shared.GameObjects; -using Robust.Shared.Utility; +using Robust.Shared.Physics.Components; namespace Robust.Shared.Containers; public abstract partial class SharedContainerSystem { + + /// + /// Attempts to insert the entity into this container. + /// + /// + /// If the insertion is successful, the inserted entity will end up parented to the + /// container entity, and the inserted entity's local position will be set to the zero vector. + /// + /// The entity to insert. + /// The container to insert into. + /// The container's transform component. + /// Whether to bypass normal insertion checks. + /// False if the entity could not be inserted. + /// + /// Thrown if this container is a child of the entity, + /// which would cause infinite loops. + /// + public bool Insert(Entity toInsert, + BaseContainer container, + TransformComponent? containerXform = null, + bool force = false) + { + // Cannot Use Resolve(ref toInsert) as the physics component is optional + if (!Resolve(toInsert.Owner, ref toInsert.Comp1, ref toInsert.Comp2)) + return false; + + // TODO move logic over to the system. + return container.Insert(toInsert, EntityManager, toInsert, containerXform, toInsert, toInsert, force); + } + /// /// Checks if the entity can be inserted into the given container. /// @@ -13,8 +44,8 @@ public abstract partial class SharedContainerSystem public bool CanInsert( EntityUid toInsert, BaseContainer container, - TransformComponent? toInsertXform = null, - bool assumeEmpty = false) + bool assumeEmpty = false, + TransformComponent? containerXform = null) { if (container.Owner == toInsert) return false; @@ -25,15 +56,12 @@ public bool CanInsert( if (!container.CanInsert(toInsert, assumeEmpty, EntityManager)) return false; - if (!TransformQuery.Resolve(toInsert, ref toInsertXform)) - return false; - // no, you can't put maps or grids into containers if (_mapQuery.HasComponent(toInsert) || _gridQuery.HasComponent(toInsert)) return false; // Prevent circular insertion. - if (_transform.ContainsEntity(toInsertXform, container.Owner)) + if (_transform.ContainsEntity(toInsert, (container.Owner, containerXform))) return false; var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(container, toInsert, assumeEmpty); diff --git a/Robust.Shared/Containers/SharedContainerSystem.Remove.cs b/Robust.Shared/Containers/SharedContainerSystem.Remove.cs index 7662ae7b50f..512136abcd7 100644 --- a/Robust.Shared/Containers/SharedContainerSystem.Remove.cs +++ b/Robust.Shared/Containers/SharedContainerSystem.Remove.cs @@ -1,9 +1,43 @@ using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Maths; namespace Robust.Shared.Containers; public abstract partial class SharedContainerSystem { + /// + /// Attempts to remove the entity from this container. + /// + /// + /// If the insertion is successful, the inserted entity will end up parented to the + /// container entity, and the inserted entity's local position will be set to the zero vector. + /// + /// The entity to remove. + /// The container to remove from. + /// If false, this operation will not rigger a move or parent change event. Ignored if + /// destination is not null + /// If true, this will not perform can-remove checks. + /// Where to place the entity after removing. Avoids unnecessary broadphase updates. + /// If not specified, and reparent option is true, then the entity will either be inserted into a parent + /// container, the grid, or the map. + /// Optional final local rotation after removal. Avoids redundant move events. + public bool Remove( + Entity toRemove, + BaseContainer container, + bool reparent = true, + bool force = false, + EntityCoordinates? destination = null, + Angle? localRotation = null) + { + // Cannot Use Resolve(ref toInsert) as the physics component is optional + if (!Resolve(toRemove.Owner, ref toRemove.Comp1, ref toRemove.Comp2)) + return false; + + // TODO move logic over to the system. + return container.Remove(toRemove, EntityManager, toRemove, toRemove, reparent, force, destination, localRotation); + } + /// /// Checks if the entity can be removed from this container. /// diff --git a/Robust.Shared/Containers/SharedContainerSystem.cs b/Robust.Shared/Containers/SharedContainerSystem.cs index ad348b061b4..113c1731352 100644 --- a/Robust.Shared/Containers/SharedContainerSystem.cs +++ b/Robust.Shared/Containers/SharedContainerSystem.cs @@ -216,35 +216,27 @@ public bool IsEntityInContainer(EntityUid uid, MetaDataComponent? meta = null) } /// - /// Recursively if the entity, or any parent entity, is inside of a container. + /// Recursively check if the entity or any parent is inside of a container. /// /// If the entity is inside of a container. public bool IsEntityOrParentInContainer( EntityUid uid, MetaDataComponent? meta = null, - TransformComponent? xform = null, - EntityQuery? metas = null, - EntityQuery? xforms = null) + TransformComponent? xform = null) { - if (meta == null) - { - metas ??= GetEntityQuery(); - meta = metas.Value.GetComponent(uid); - } + if (!MetaQuery.Resolve(uid, ref meta)) + return false; if ((meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer) return true; - if (xform == null) - { - xforms ??= GetEntityQuery(); - xform = xforms.Value.GetComponent(uid); - } + if (!TransformQuery.Resolve(uid, ref xform)) + return false; if (!xform.ParentUid.Valid) return false; - return IsEntityOrParentInContainer(xform.ParentUid, metas: metas, xforms: xforms); + return IsEntityOrParentInContainer(xform.ParentUid); } /// diff --git a/Robust.Shared/ContentPack/Sandbox.yml b/Robust.Shared/ContentPack/Sandbox.yml index bb55fa1a523..c895dfcafff 100644 --- a/Robust.Shared/ContentPack/Sandbox.yml +++ b/Robust.Shared/ContentPack/Sandbox.yml @@ -73,6 +73,12 @@ WhitelistedNamespaces: # * The API is not *relevant* to content. e.g. System.Type.IsAnsiClass. # * I am lazy these API lists are huge dude. Types: + NetSerializer: + NetListAsArray`1: + Fields: + - "System.Collections.Generic.IReadOnlyCollection`1 Value" + Methods: + - "bool get_HasContents()" Lidgren.Network: NetBuffer: All: True diff --git a/Robust.Shared/GameObjects/ComponentEventArgs.cs b/Robust.Shared/GameObjects/ComponentEventArgs.cs index 82ac345d32d..367cb2570e8 100644 --- a/Robust.Shared/GameObjects/ComponentEventArgs.cs +++ b/Robust.Shared/GameObjects/ComponentEventArgs.cs @@ -31,9 +31,9 @@ public ComponentEventArgs(IComponent component, EntityUid owner) public readonly struct AddedComponentEventArgs { public readonly ComponentEventArgs BaseArgs; - public readonly CompIdx ComponentType; + public readonly ComponentRegistration ComponentType; - public AddedComponentEventArgs(ComponentEventArgs baseArgs, CompIdx componentType) + public AddedComponentEventArgs(ComponentEventArgs baseArgs, ComponentRegistration componentType) { BaseArgs = baseArgs; ComponentType = componentType; diff --git a/Robust.Shared/GameObjects/Components/Eye/EyeComponent.cs b/Robust.Shared/GameObjects/Components/Eye/EyeComponent.cs index 13eb1354926..4f2684f2886 100644 --- a/Robust.Shared/GameObjects/Components/Eye/EyeComponent.cs +++ b/Robust.Shared/GameObjects/Components/Eye/EyeComponent.cs @@ -12,16 +12,10 @@ namespace Robust.Shared.GameObjects [RegisterComponent, NetworkedComponent, Access(typeof(SharedEyeSystem)), AutoGenerateComponentState(true)] public sealed partial class EyeComponent : Component { - #region Client - - [ViewVariables] internal Eye? _eye = default!; - - public IEye? Eye => _eye; + public const int DefaultVisibilityMask = 1; [ViewVariables] - public MapCoordinates? Position => _eye?.Position; - - #endregion + public readonly Eye Eye = new(); /// /// If not null, this entity is used to update the eye's position instead of just using the component's owner. @@ -37,6 +31,9 @@ public sealed partial class EyeComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("drawFov"), AutoNetworkedField] public bool DrawFov = true; + [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public bool DrawLight = true; + // yes it's not networked, don't ask. [ViewVariables(VVAccess.ReadWrite), DataField("rotation")] public Angle Rotation; @@ -47,8 +44,6 @@ public sealed partial class EyeComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("offset"), AutoNetworkedField] public Vector2 Offset; - public const int DefaultVisibilityMask = 1; - /// /// The visibility mask for this eye. /// The player will be able to get updates for entities whose layers match the mask. @@ -58,7 +53,7 @@ public sealed partial class EyeComponent : Component } /// - /// Single layer used for Eye visiblity. Controls what entities they are allowed to see. + /// Single layer used for Eye visibility. Controls what entities they are allowed to see. /// public sealed class VisibilityMaskLayer {} } diff --git a/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs b/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs index 214a73fc7e3..b02cfa5d98d 100644 --- a/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs +++ b/Robust.Shared/GameObjects/Components/Transform/TransformComponent.cs @@ -311,6 +311,7 @@ public EntityCoordinates Coordinates /// This is effectively a more complete version of /// [ViewVariables(VVAccess.ReadWrite)] + [Obsolete("Use TransformSystem.GetMapCoordinates")] public MapCoordinates MapPosition => new(WorldPosition, MapID); /// diff --git a/Robust.Shared/GameObjects/EntityEventBus.Directed.cs b/Robust.Shared/GameObjects/EntityEventBus.Directed.cs index bf495ad42cb..3f8da6f0672 100644 --- a/Robust.Shared/GameObjects/EntityEventBus.Directed.cs +++ b/Robust.Shared/GameObjects/EntityEventBus.Directed.cs @@ -346,7 +346,7 @@ public void OnComponentAdded(in AddedComponentEventArgs e) { _subscriptionLock = true; - EntAddComponent(e.BaseArgs.Owner, e.ComponentType); + EntAddComponent(e.BaseArgs.Owner, e.ComponentType.Idx); } public void OnComponentRemoved(in RemovedComponentEventArgs e) diff --git a/Robust.Shared/GameObjects/EntityManager.Components.cs b/Robust.Shared/GameObjects/EntityManager.Components.cs index 9f3e3fa08c8..7d1b538a75f 100644 --- a/Robust.Shared/GameObjects/EntityManager.Components.cs +++ b/Robust.Shared/GameObjects/EntityManager.Components.cs @@ -258,7 +258,7 @@ internal void AddComponentInternal(EntityUid uid, T component, ComponentRegis component.Networked = false; } - var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg.Idx); + var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg); ComponentAdded?.Invoke(eventArgs); _eventBus.OnComponentAdded(eventArgs); @@ -1196,6 +1196,15 @@ public TComp1 GetComponent(EntityUid uid) : (TComp1)_manager.GetComponent(uid, _type.Value.Type); } + [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] + public Entity Get(EntityUid uid) + { + if (_traitDict.TryGetValue(uid, out var comp) && !comp.Deleted) + return new Entity(uid, (TComp1) comp); + + throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(TComp1)}"); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component) @@ -1269,6 +1278,12 @@ public bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp1? component, bo return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining), Pure] + public bool Resolve(ref Entity entity, bool logMissing = true) + { + return Resolve(entity.Owner, ref entity.Comp, logMissing); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public TComp1? CompOrNull(EntityUid uid) diff --git a/Robust.Shared/GameObjects/EntityManager.Network.cs b/Robust.Shared/GameObjects/EntityManager.Network.cs index f2aa154dacd..cd0285bb336 100644 --- a/Robust.Shared/GameObjects/EntityManager.Network.cs +++ b/Robust.Shared/GameObjects/EntityManager.Network.cs @@ -256,8 +256,7 @@ public virtual EntityCoordinates EnsureCoordinates(NetCoordinates netCoordina /// public HashSet GetEntitySet(HashSet netEntities) { - var entities = new HashSet(); - entities.EnsureCapacity(netEntities.Count); + var entities = new HashSet(netEntities.Count); foreach (var netEntity in netEntities) { @@ -292,6 +291,16 @@ public HashSet EnsureEntitySet(HashSet netEntities, Ent return entities; } + public void EnsureEntitySet(HashSet netEntities, EntityUid callerEntity, HashSet entities) + { + entities.Clear(); + entities.EnsureCapacity(netEntities.Count); + foreach (var netEntity in netEntities) + { + entities.Add(EnsureEntity(netEntity, callerEntity)); + } + } + /// public List EnsureEntityList(List netEntities, EntityUid callerEntity) { @@ -305,6 +314,16 @@ public List EnsureEntityList(List netEntities, EntityUi return entities; } + public void EnsureEntityList(List netEntities, EntityUid callerEntity, List entities) + { + entities.Clear(); + entities.EnsureCapacity(netEntities.Count); + foreach (var netEntity in netEntities) + { + entities.Add(EnsureEntity(netEntity, callerEntity)); + } + } + /// public List GetEntityList(ICollection netEntities) { diff --git a/Robust.Shared/GameObjects/EntitySystem.Proxy.cs b/Robust.Shared/GameObjects/EntitySystem.Proxy.cs index d9a706d838d..ce2ba0bd558 100644 --- a/Robust.Shared/GameObjects/EntitySystem.Proxy.cs +++ b/Robust.Shared/GameObjects/EntitySystem.Proxy.cs @@ -154,9 +154,26 @@ protected void Dirty(EntityUid uid, IComponent component, MetaDataComponent? met /// Marks a component as dirty. This also implicitly dirties the entity this component belongs to. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void Dirty(Entity ent, MetaDataComponent? meta = null) where T : IComponent + protected void Dirty(Entity ent, MetaDataComponent? meta = null) where T : IComponent? { - EntityManager.Dirty(ent.Owner, ent.Comp, meta); + var comp = ent.Comp; + if (comp == null && !EntityManager.TryGetComponent(ent.Owner, out comp)) + return; + + EntityManager.Dirty(ent.Owner, comp, meta); + } + + /// + /// Marks a component as dirty. This also implicitly dirties the entity this component belongs to. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void Dirty(Entity ent) where T : IComponent? + { + var comp = ent.Comp1; + if (comp == null && !EntityManager.TryGetComponent(ent.Owner, out comp)) + return; + + EntityManager.Dirty(ent.Owner, comp, ent.Comp2); } /// @@ -993,12 +1010,24 @@ protected HashSet EnsureEntitySet(HashSet netEntities, return EntityManager.EnsureEntitySet(netEntities, callerEntity); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void EnsureEntitySet(HashSet netEntities, EntityUid callerEntity, HashSet entities) + { + EntityManager.EnsureEntitySet(netEntities, callerEntity, entities); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected List EnsureEntityList(List netEntities, EntityUid callerEntity) { return EntityManager.EnsureEntityList(netEntities, callerEntity); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected void EnsureEntityList(List netEntities, EntityUid callerEntity, List entities) + { + EntityManager.EnsureEntityList(netEntities, callerEntity, entities); + } + /// /// Returns the of a . Returns if it doesn't exist. /// diff --git a/Robust.Shared/GameObjects/EntitySystem.Resolve.cs b/Robust.Shared/GameObjects/EntitySystem.Resolve.cs index c1750ee0976..1f54929faf0 100644 --- a/Robust.Shared/GameObjects/EntitySystem.Resolve.cs +++ b/Robust.Shared/GameObjects/EntitySystem.Resolve.cs @@ -32,7 +32,7 @@ protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TComp? comp return found; } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref MetaDataComponent? component, bool logMissing = true) @@ -40,7 +40,7 @@ protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref MetaDataComponent? return EntityManager.MetaQuery.Resolve(uid, ref component); } - /// + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] protected bool Resolve(EntityUid uid, [NotNullWhen(true)] ref TransformComponent? component, bool logMissing = true) diff --git a/Robust.Shared/GameObjects/EntitySystemMessages/AudioMessages.cs b/Robust.Shared/GameObjects/EntitySystemMessages/AudioMessages.cs new file mode 100644 index 00000000000..cbc5e56f31b --- /dev/null +++ b/Robust.Shared/GameObjects/EntitySystemMessages/AudioMessages.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Audio; +using Robust.Shared.Map; +using Robust.Shared.Serialization; +using System; + +#nullable disable + +namespace Robust.Shared.GameObjects; +// TODO: This is quite bandwidth intensive. +// Sending bus names and file names as strings is expensive and can be optimized. +// Also there's redundant fields in AudioParams in most cases. +[Serializable, NetSerializable] +public abstract class AudioMessage : EntityEventArgs +{ + public uint Identifier { get; set; } + public string FileName { get; set; } + public AudioParams AudioParams { get; set; } +} + +[Serializable, NetSerializable] +public sealed class StopAudioMessageClient : EntityEventArgs +{ + public uint Identifier {get; set;} +} + +[Serializable, NetSerializable] +public sealed class PlayAudioGlobalMessage : AudioMessage +{ +} + +[Serializable, NetSerializable] +public sealed class PlayAudioPositionalMessage : AudioMessage +{ + public NetCoordinates Coordinates { get; set; } + public NetCoordinates FallbackCoordinates { get; set; } +} + +[Serializable, NetSerializable] +public sealed class PlayAudioEntityMessage : AudioMessage +{ + public NetEntity NetEntity { get; set; } + public NetCoordinates Coordinates { get; set; } + public NetCoordinates FallbackCoordinates { get; set; } +} diff --git a/Robust.Shared/GameObjects/IResourceCache.cs b/Robust.Shared/GameObjects/IResourceCache.cs deleted file mode 100644 index d3431b0b83e..00000000000 --- a/Robust.Shared/GameObjects/IResourceCache.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Robust.Shared.ResourceManagement; -using Robust.Shared.Utility; - -namespace Robust.Shared.GameObjects; - -/// -/// Handles caching of -/// -public interface IResourceCache -{ - T GetResource(string path, bool useFallback = true) - where T : BaseResource, new(); - - T GetResource(ResPath path, bool useFallback = true) - where T : BaseResource, new(); - - bool TryGetResource(string path, [NotNullWhen(true)] out T? resource) - where T : BaseResource, new(); - - bool TryGetResource(ResPath path, [NotNullWhen(true)] out T? resource) - where T : BaseResource, new(); - - void ReloadResource(string path) - where T : BaseResource, new(); - - void ReloadResource(ResPath path) - where T : BaseResource, new(); - - void CacheResource(string path, T resource) - where T : BaseResource, new(); - - void CacheResource(ResPath path, T resource) - where T : BaseResource, new(); - - T GetFallback() - where T : BaseResource, new(); - - IEnumerable> GetAllResources() where T : BaseResource, new(); -} - diff --git a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs index c75b3bd02af..163c7a69a01 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookup.Queries.cs @@ -171,11 +171,16 @@ private bool AnyEntitiesIntersecting(EntityUid lookupUid, private void RecursiveAdd(EntityUid uid, ref ValueList toAdd) { - var childEnumerator = _xformQuery.GetComponent(uid).ChildEnumerator; + if (!_xformQuery.TryGetComponent(uid, out var xform)) + { + Log.Error($"Encountered deleted entity {uid} while performing entity lookup."); + return; + } + toAdd.Add(uid); + var childEnumerator = xform.ChildEnumerator; while (childEnumerator.MoveNext(out var child)) { - toAdd.Add(child.Value); RecursiveAdd(child.Value, ref toAdd); } } @@ -185,6 +190,11 @@ private void AddContained(HashSet intersecting, LookupFlags flags) if ((flags & LookupFlags.Contained) == 0x0 || intersecting.Count == 0) return; + // TODO PERFORMANCE. + // toAdd only exists because we can't add directly to intersecting w/o enumeration issues. + // If we assume that there are more entities in containers than there are entities in the intersecting set, then + // we would be better off creating a fixed-size EntityUid array and coping all intersecting entities into that + // instead of creating a value list here that needs to be resized. var toAdd = new ValueList(); foreach (var uid in intersecting) @@ -196,7 +206,6 @@ private void AddContained(HashSet intersecting, LookupFlags flags) { foreach (var contained in con.ContainedEntities) { - toAdd.Add(contained); RecursiveAdd(contained, ref toAdd); } } diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs index 83d0aaaaa69..c91b5609024 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.ComponentQueries.cs @@ -6,7 +6,11 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Maths; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision; +using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Systems; using Robust.Shared.Utility; namespace Robust.Shared.GameObjects; @@ -15,18 +19,6 @@ public sealed partial class EntityLookupSystem { #region Private - private void AddComponentsIntersecting( - EntityUid lookupUid, - HashSet intersecting, - Box2 worldAABB, - LookupFlags flags, - EntityQuery query) where T : IComponent - { - var intersectingEntities = new HashSet>(); - AddEntitiesIntersecting(lookupUid, intersectingEntities, worldAABB, flags, query); - intersecting.UnionWith(intersectingEntities.Select(e => e.Comp)); - } - private void AddEntitiesIntersecting( EntityUid lookupUid, HashSet> intersecting, @@ -88,6 +80,155 @@ private void AddEntitiesIntersecting( } } + private void AddEntitiesIntersecting( + EntityUid lookupUid, + HashSet> intersecting, + IPhysShape shape, + Box2 worldAABB, + LookupFlags flags, + EntityQuery query) where T : IComponent + { + var lookup = _broadQuery.GetComponent(lookupUid); + var invMatrix = _transform.GetInvWorldMatrix(lookupUid); + var localAABB = invMatrix.TransformBox(worldAABB); + var transform = new Transform(0); + var state = new QueryState( + intersecting, + shape, + transform, + _fixtures, + _physics, + _transform, + _manifoldManager, + query, + _fixturesQuery, + (flags & LookupFlags.Sensors) != 0 + ); + + if ((flags & LookupFlags.Dynamic) != 0x0) + { + lookup.DynamicTree.QueryAabb(ref state, static (ref QueryState state, in FixtureProxy value) => + { + if (!state.Sensors && !value.Fixture.Hard) + return true; + + if (!state.Query.TryGetComponent(value.Entity, out var comp)) + return true; + + var intersectingTransform = state.Physics.GetPhysicsTransform(value.Entity); + if (!state.Manifolds.TestOverlap(state.Shape, 0, value.Fixture.Shape, value.ChildIndex, state.Transform, intersectingTransform)) + { + return true; + } + + state.Intersecting.Add((value.Entity, comp)); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + + if ((flags & (LookupFlags.Static)) != 0x0) + { + lookup.StaticTree.QueryAabb(ref state, static (ref QueryState state, in FixtureProxy value) => + { + if (!state.Sensors && !value.Fixture.Hard) + return true; + + if (!state.Query.TryGetComponent(value.Entity, out var comp)) + return true; + + var intersectingTransform = state.Physics.GetPhysicsTransform(value.Entity); + if (!state.Manifolds.TestOverlap(state.Shape, 0, value.Fixture.Shape, value.ChildIndex, state.Transform, intersectingTransform)) + { + return true; + } + + state.Intersecting.Add((value.Entity, comp)); + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + + if ((flags & LookupFlags.StaticSundries) == LookupFlags.StaticSundries) + { + lookup.StaticSundriesTree.QueryAabb(ref state, static (ref QueryState state, in EntityUid value) => + { + if (!state.Query.TryGetComponent(value, out var comp)) + return true; + + var intersectingTransform = state.Physics.GetPhysicsTransform(value); + + if (state.FixturesQuery.TryGetComponent(value, out var fixtures)) + { + bool anyFixture = false; + foreach (var fixture in fixtures.Fixtures.Values) + { + if (!state.Sensors && !fixture.Hard) + continue; + + anyFixture = true; + for (var i = 0; i < fixture.Shape.ChildCount; i++) + { + if (state.Manifolds.TestOverlap(state.Shape, 0, fixture.Shape, i, state.Transform, + intersectingTransform)) + { + state.Intersecting.Add((value, comp)); + return true; + } + } + } + + if (anyFixture) + return true; + } + + if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) + state.Intersecting.Add((value, comp)); + + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + + if ((flags & LookupFlags.Sundries) != 0x0) + { + lookup.SundriesTree.QueryAabb(ref state, static (ref QueryState state, + in EntityUid value) => + { + if (!state.Query.TryGetComponent(value, out var comp)) + return true; + + var intersectingTransform = state.Physics.GetPhysicsTransform(value); + + if (state.FixturesQuery.TryGetComponent(value, out var fixtures)) + { + bool anyFixture = false; + foreach (var fixture in fixtures.Fixtures.Values) + { + if (!state.Sensors && !fixture.Hard) + continue; + + anyFixture = true; + for (var i = 0; i < fixture.Shape.ChildCount; i++) + { + if (state.Manifolds.TestOverlap(state.Shape, 0, fixture.Shape, i, state.Transform, + intersectingTransform)) + { + state.Intersecting.Add((value, comp)); + return true; + } + } + } + + if (anyFixture) + return true; + } + + if (state.Fixtures.TestPoint(state.Shape, state.Transform, intersectingTransform.Position)) + state.Intersecting.Add((value, comp)); + + return true; + }, localAABB, (flags & LookupFlags.Approximate) != 0x0); + } + } + private bool AnyComponentsIntersecting( EntityUid lookupUid, Box2 worldAABB, @@ -270,7 +411,7 @@ public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, En if (xform.MapID != mapId || !worldAABB.Contains(_transform.GetWorldPosition(xform)) || ((flags & LookupFlags.Contained) == 0x0 && - _container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform, _metaQuery, _xformQuery))) + _container.IsEntityOrParentInContainer(uid, null, xform))) { continue; } @@ -308,15 +449,6 @@ public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, En return false; } - [Obsolete] - public HashSet GetComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) - { - var intersectingEntities = new HashSet>(); - GetEntitiesIntersecting(type, mapId, worldAABB, intersectingEntities, flags); - var intersecting = new HashSet(intersectingEntities.Select(e => e.Comp)); - return intersecting; - } - public void GetEntitiesIntersecting(Type type, MapId mapId, Box2 worldAABB, HashSet> intersecting, LookupFlags flags = DefaultFlags) { DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type)); @@ -332,7 +464,7 @@ public void GetEntitiesIntersecting(Type type, MapId mapId, Box2 worldAABB, Hash if (xform.MapID != mapId || !worldAABB.Contains(_transform.GetWorldPosition(xform)) || ((flags & LookupFlags.Contained) == 0x0 && - _container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform, _metaQuery, _xformQuery))) + _container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform))) { continue; } @@ -366,14 +498,6 @@ public void GetEntitiesIntersecting(Type type, MapId mapId, Box2 worldAABB, Hash } } - [Obsolete] - public HashSet GetComponentsIntersecting(MapId mapId, Box2 worldAABB, LookupFlags flags = DefaultFlags) where T : IComponent - { - var intersectingEntities = new HashSet>(); - GetEntitiesIntersecting(mapId, worldAABB, intersectingEntities, flags); - return new HashSet(intersectingEntities.Select(e => e.Comp)); - } - public void GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, HashSet> entities, LookupFlags flags = DefaultFlags) where T : IComponent { if (mapId == MapId.Nullspace) return; @@ -416,14 +540,163 @@ public void GetEntitiesIntersecting(MapId mapId, Box2 worldAABB, HashSet GetComponentsInRange(EntityCoordinates coordinates, float range) where T : IComponent + public void GetEntitiesIntersecting(Type type, MapId mapId, IPhysShape shape, HashSet> intersecting, LookupFlags flags = DefaultFlags) { - var mapPos = coordinates.ToMap(EntityManager, _transform); - return GetComponentsInRange(mapPos, range); + DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type)); + if (mapId == MapId.Nullspace) + return; + + var shapeTransform = new Transform(0); + var worldAABB = shape.ComputeAABB(shapeTransform, 0); + var sensors = (flags & LookupFlags.Sensors) != 0; + if (!UseBoundsQuery(type, worldAABB.Height * worldAABB.Width)) + { + foreach (var (uid, comp) in EntityManager.GetAllComponents(type, true)) + { + var xform = _xformQuery.GetComponent(uid); + var (pos, rot) = _transform.GetWorldPositionRotation(xform); + + if (xform.MapID != mapId || + !worldAABB.Contains(pos) || + ((flags & LookupFlags.Contained) == 0x0 && + _container.IsEntityOrParentInContainer(uid, _metaQuery.GetComponent(uid), xform))) + { + continue; + } + + if (_fixturesQuery.TryGetComponent(uid, out var fixtures)) + { + var transform = new Transform(pos, rot); + + bool anyFixture = false; + foreach (var fixture in fixtures.Fixtures.Values) + { + if (!sensors && !fixture.Hard) + continue; + + anyFixture = true; + for (var i = 0; i < fixture.Shape.ChildCount; i++) + { + if (_manifoldManager.TestOverlap(shape, 0, fixture.Shape, i, shapeTransform, transform)) + { + goto found; + } + } + } + + if (anyFixture) + continue; + } + + if (!_fixtures.TestPoint(shape, shapeTransform, pos)) + continue; + + found: + intersecting.Add((uid, comp)); + } + } + else + { + var query = EntityManager.GetEntityQuery(type); + + // Get grid entities + var state = new GridQueryState(intersecting, shape, worldAABB, this, flags, query); + + _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, + static (EntityUid uid, MapGridComponent grid, ref GridQueryState state) => + { + state.Lookup.AddEntitiesIntersecting(uid, state.Intersecting, state.Shape, state.WorldAABB, state.Flags, state.Query); + return true; + }, (flags & LookupFlags.Approximate) != 0x0); + + // Get map entities + var mapUid = _mapManager.GetMapEntityId(mapId); + AddEntitiesIntersecting(mapUid, intersecting, shape, worldAABB, flags, query); + AddContained(intersecting, flags, query); + } + } + + public void GetEntitiesIntersecting(MapId mapId, IPhysShape shape, HashSet> entities, LookupFlags flags = DefaultFlags) where T : IComponent + { + if (mapId == MapId.Nullspace) return; + + var shapeTransform = new Transform(0); + var worldAABB = shape.ComputeAABB(shapeTransform, 0); + var sensors = (flags & LookupFlags.Sensors) != 0; + if (!UseBoundsQuery(worldAABB.Height * worldAABB.Width)) + { + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var comp, out var xform)) + { + var (pos, rot) = _transform.GetWorldPositionRotation(xform); + + if (xform.MapID != mapId || !worldAABB.Contains(pos)) + continue; + + if (_fixturesQuery.TryGetComponent(uid, out var fixtures)) + { + var transform = new Transform(pos, rot); + bool anyFixture = false; + foreach (var fixture in fixtures.Fixtures.Values) + { + if (!sensors && !fixture.Hard) + continue; + + anyFixture = true; + for (var i = 0; i < fixture.Shape.ChildCount; i++) + { + if (_manifoldManager.TestOverlap(shape, 0, fixture.Shape, i, shapeTransform, transform)) + { + goto found; + } + } + } + + if (anyFixture) + continue; + } + + if (!_fixtures.TestPoint(shape, shapeTransform, pos)) + continue; + + found: + entities.Add((uid, comp)); + } + } + else + { + var query = GetEntityQuery(); + + // Get grid entities + var state = (this, shape, worldAABB, flags, query, entities); + + _mapManager.FindGridsIntersecting(mapId, worldAABB, ref state, + static (EntityUid uid, MapGridComponent grid, + ref (EntityLookupSystem system, + IPhysShape shape, + Box2 worldAABB, + LookupFlags flags, + EntityQuery query, + HashSet> intersecting) tuple) => + { + tuple.system.AddEntitiesIntersecting(uid, tuple.intersecting, tuple.shape, tuple.worldAABB, tuple.flags, tuple.query); + return true; + }, (flags & LookupFlags.Approximate) != 0x0); + + // Get map entities + var mapUid = _mapManager.GetMapEntityId(mapId); + AddEntitiesIntersecting(mapUid, entities, shape, worldAABB, flags, query); + AddContained(entities, flags, query); + } } + #endregion + + #region EntityCoordinates + public void GetEntitiesInRange(EntityCoordinates coordinates, float range, HashSet> entities) where T : IComponent { var mapPos = coordinates.ToMap(EntityManager, _transform); @@ -441,35 +714,34 @@ public HashSet> GetEntitiesInRange(EntityCoordinates coordinates, f #region MapCoordinates - [Obsolete] - public HashSet GetComponentsInRange(Type type, MapCoordinates coordinates, float range) + public HashSet> GetEntitiesInRange(Type type, MapCoordinates coordinates, float range) { - DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type)); - return GetComponentsInRange(type, coordinates.MapId, coordinates.Position, range); + var entities = new HashSet>(); + GetEntitiesInRange(type, coordinates, range, entities); + return entities; } - public HashSet> GetEntitiesInRange(Type type, MapCoordinates coordinates, float range) + public void GetEntitiesInRange(Type type, MapCoordinates coordinates, float range, HashSet> entities) { DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type)); - var entities = new HashSet>(); GetEntitiesInRange(type, coordinates.MapId, coordinates.Position, range, entities); - return entities; } + [Obsolete] public HashSet GetComponentsInRange(MapCoordinates coordinates, float range) where T : IComponent { return GetComponentsInRange(coordinates.MapId, coordinates.Position, range); } - public void GetEntitiesInRange(MapCoordinates coordinates, float range, HashSet> entities) where T : IComponent + public void GetEntitiesInRange(MapCoordinates coordinates, float range, HashSet> entities, LookupFlags flags = DefaultFlags) where T : IComponent { - GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities); + GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities, flags); } - public HashSet> GetEntitiesInRange(MapCoordinates coordinates, float range) where T : IComponent + public HashSet> GetEntitiesInRange(MapCoordinates coordinates, float range, LookupFlags flags = DefaultFlags) where T : IComponent { var entities = new HashSet>(); - GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities); + GetEntitiesInRange(coordinates.MapId, coordinates.Position, range, entities, flags); return entities; } @@ -477,40 +749,15 @@ public HashSet> GetEntitiesInRange(MapCoordinates coordinates, floa #region MapId - public bool AnyComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range) - { - DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type)); - DebugTools.Assert(range > 0, "Range must be a positive float"); - - if (mapId == MapId.Nullspace) return false; - - // TODO: Actual circles - var rangeVec = new Vector2(range, range); - - var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec); - return AnyComponentsIntersecting(type, mapId, worldAABB); - } - - [Obsolete] - public HashSet GetComponentsInRange(Type type, MapId mapId, Vector2 worldPos, float range) - { - var entities = new HashSet>(); - GetEntitiesInRange(type, mapId, worldPos, range, entities); - return new HashSet(entities.Select(e => e.Comp)); - } - - public void GetEntitiesInRange(Type type, MapId mapId, Vector2 worldPos, float range, HashSet> entities) + public void GetEntitiesInRange(Type type, MapId mapId, Vector2 worldPos, float range, HashSet> entities, LookupFlags flags = DefaultFlags) { DebugTools.Assert(typeof(IComponent).IsAssignableFrom(type)); DebugTools.Assert(range > 0, "Range must be a positive float"); if (mapId == MapId.Nullspace) return; - // TODO: Actual circles - var rangeVec = new Vector2(range, range); - - var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec); - GetEntitiesIntersecting(type, mapId, worldAABB, entities); + var circle = new PhysShapeCircle(range, worldPos); + GetEntitiesIntersecting(type, mapId, circle, entities, flags); } [Obsolete] @@ -521,18 +768,41 @@ public HashSet GetComponentsInRange(MapId mapId, Vector2 worldPos, float r return new HashSet(entities.Select(e => e.Comp)); } - public void GetEntitiesInRange(MapId mapId, Vector2 worldPos, float range, HashSet> entities) where T : IComponent + public void GetEntitiesInRange(MapId mapId, Vector2 worldPos, float range, HashSet> entities, LookupFlags flags = DefaultFlags) where T : IComponent { - DebugTools.Assert(range > 0, "Range must be a positive float"); + GetEntitiesInRange(mapId, new PhysShapeCircle(range, worldPos), entities, flags); + } - if (mapId == MapId.Nullspace) return; + public void GetEntitiesInRange(MapId mapId, IPhysShape shape, HashSet> entities, LookupFlags flags = DefaultFlags) where T : IComponent + { + DebugTools.Assert(shape.Radius > 0, "Range must be a positive float"); - // TODO: Actual circles - var rangeVec = new Vector2(range, range); + if (mapId == MapId.Nullspace) return; - var worldAABB = new Box2(worldPos - rangeVec, worldPos + rangeVec); - GetEntitiesIntersecting(mapId, worldAABB, entities); + GetEntitiesIntersecting(mapId, shape, entities, flags); } #endregion + + private readonly record struct GridQueryState( + HashSet> Intersecting, + IPhysShape Shape, + Box2 WorldAABB, + EntityLookupSystem Lookup, + LookupFlags Flags, + EntityQuery Query + ) where T : IComponent; + + private readonly record struct QueryState( + HashSet> Intersecting, + IPhysShape Shape, + Transform Transform, + FixtureSystem Fixtures, + SharedPhysicsSystem Physics, + SharedTransformSystem TransformSystem, + IManifoldManager Manifolds, + EntityQuery Query, + EntityQuery FixturesQuery, + bool Sensors + ) where T : IComponent; } diff --git a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs index 6af17752337..996ac39488f 100644 --- a/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs +++ b/Robust.Shared/GameObjects/Systems/EntityLookupSystem.cs @@ -11,9 +11,11 @@ using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.BroadPhase; +using Robust.Shared.Physics.Collision; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -72,11 +74,14 @@ public record struct WorldAABBEvent public sealed partial class EntityLookupSystem : EntitySystem { + [Dependency] private readonly IManifoldManager _manifoldManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; private EntityQuery _broadQuery; @@ -361,7 +366,7 @@ private void AddOrUpdatePhysicsTree( FixturesComponent manager, EntityQuery xformQuery) { - DebugTools.Assert(!_container.IsEntityOrParentInContainer(body.Owner, null, xform, null, xformQuery)); + DebugTools.Assert(!_container.IsEntityOrParentInContainer(body.Owner, null, xform)); DebugTools.Assert(xform.Broadphase == null || xform.Broadphase == new BroadphaseData(broadphase.Owner, physicsMap.Owner, body.CanCollide, body.BodyType == BodyType.Static)); DebugTools.Assert(broadphase.Owner == broadUid); @@ -836,7 +841,7 @@ public bool TryFindBroadphase( TransformComponent xform, [NotNullWhen(true)] out BroadphaseComponent? broadphase) { - if (xform.MapID == MapId.Nullspace || _container.IsEntityOrParentInContainer(xform.Owner, null, xform, null, _xformQuery)) + if (xform.MapID == MapId.Nullspace || _container.IsEntityOrParentInContainer(xform.Owner, null, xform)) { broadphase = null; return false; diff --git a/Robust.Shared/GameObjects/Systems/SharedAudioSystem.cs b/Robust.Shared/GameObjects/Systems/SharedAudioSystem.cs new file mode 100644 index 00000000000..17c1bbdd892 --- /dev/null +++ b/Robust.Shared/GameObjects/Systems/SharedAudioSystem.cs @@ -0,0 +1,302 @@ +using Robust.Shared.Audio; +using Robust.Shared.Configuration; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Robust.Shared.GameObjects; +public abstract class SharedAudioSystem : EntitySystem +{ + [Dependency] protected readonly IConfigurationManager CfgManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] protected readonly IRobustRandom RandMan = default!; + [Dependency] protected readonly ISharedPlayerManager PlayerManager = default!; + [Dependency] private readonly SharedMapSystem _map = default!; + + /// + /// Default max range at which the sound can be heard. + /// + public const float DefaultSoundRange = 25; + + /// + /// Used in the PAS to designate the physics collision mask of occluders. + /// + public int OcclusionCollisionMask { get; set; } + + public string GetSound(SoundSpecifier specifier) + { + switch (specifier) + { + case SoundPathSpecifier path: + return path.Path == default ? string.Empty : path.Path.ToString(); + + case SoundCollectionSpecifier collection: + { + if (collection.Collection == null) + return string.Empty; + + var soundCollection = _protoMan.Index(collection.Collection); + return RandMan.Pick(soundCollection.PickFiles).ToString(); + } + } + + return string.Empty; + } + + /// + /// Play an audio file globally, without position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The set of players that will hear the sound. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayGlobal(string filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null); + + + /// + /// Play an audio file globally, without position. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The set of players that will hear the sound. + /// Audio parameters to apply when playing the sound. + public IPlayingAudioStream? PlayGlobal(SoundSpecifier? sound, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null) + { + return sound == null ? null : PlayGlobal(GetSound(sound), playerFilter, recordReplay, audioParams ?? sound.Params); + } + + /// + /// Play an audio file globally, without position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The player that will hear the sound. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayGlobal(string filename, ICommonSession recipient, AudioParams? audioParams = null); + + /// + /// Play an audio file globally, without position. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The player that will hear the sound. + /// Audio parameters to apply when playing the sound. + public IPlayingAudioStream? PlayGlobal(SoundSpecifier? sound, ICommonSession recipient, AudioParams? audioParams = null) + { + return sound == null ? null : PlayGlobal(GetSound(sound), recipient, audioParams ?? sound.Params); + } + + /// + /// Play an audio file globally, without position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The player that will hear the sound. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayGlobal(string filename, EntityUid recipient, AudioParams? audioParams = null); + + /// + /// Play an audio file globally, without position. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The player that will hear the sound. + /// Audio parameters to apply when playing the sound. + public IPlayingAudioStream? PlayGlobal(SoundSpecifier? sound, EntityUid recipient, AudioParams? audioParams = null) + { + return sound == null ? null : PlayGlobal(GetSound(sound), recipient, audioParams ?? sound.Params); + } + + // TODO rename to PlayEntity + /// + /// Play an audio file following an entity. + /// + /// The resource path to the OGG Vorbis file to play. + /// The set of players that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null); + + /// + /// Play an audio file following an entity. + /// + /// The resource path to the OGG Vorbis file to play. + /// The player that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayEntity(string filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null); + + /// + /// Play an audio file following an entity. + /// + /// The resource path to the OGG Vorbis file to play. + /// The player that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayEntity(string filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null); + + // TODO rename to PlayEntity + /// + /// Play an audio file following an entity. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The set of players that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public IPlayingAudioStream? Play(SoundSpecifier? sound, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null) + { + return sound == null ? null : Play(GetSound(sound), playerFilter, uid, recordReplay, audioParams ?? sound.Params); + } + + /// + /// Play an audio file following an entity. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The player that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public IPlayingAudioStream? PlayEntity(SoundSpecifier? sound, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null) + { + return sound == null ? null : PlayEntity(GetSound(sound), recipient, uid, audioParams ?? sound.Params); + } + + /// + /// Play an audio file following an entity. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The player that will hear the sound. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public IPlayingAudioStream? PlayEntity(SoundSpecifier? sound, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null) + { + return sound == null ? null : PlayEntity(GetSound(sound), recipient, uid, audioParams ?? sound.Params); + } + + /// + /// Play an audio file following an entity for every entity in PVS range. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public IPlayingAudioStream? PlayPvs(SoundSpecifier? sound, EntityUid uid, AudioParams? audioParams = null) + { + return sound == null ? null : PlayPvs(GetSound(sound), uid, audioParams ?? sound.Params); + } + + /// + /// Play an audio file at the specified EntityCoordinates for every entity in PVS range. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The EntityCoordinates to attach the audio source to. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public IPlayingAudioStream? PlayPvs(SoundSpecifier? sound, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + return sound == null ? null : Play(GetSound(sound), Filter.Pvs(coordinates, entityMan: EntityManager, playerMan: PlayerManager), coordinates, true, audioParams ?? sound.Params); + } + + /// + /// Play an audio file following an entity for every entity in PVS range. + /// + /// The resource path to the OGG Vorbis file to play. + /// The UID of the entity "emitting" the audio. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public IPlayingAudioStream? PlayPvs(string filename, EntityUid uid, AudioParams? audioParams = null) + { + return Play(filename, Filter.Pvs(uid, entityManager: EntityManager, playerManager:PlayerManager, cfgManager:CfgManager), uid, true, audioParams); + } + + /// + /// Plays a predicted sound following an entity. The server will send the sound to every player in PVS range, + /// unless that player is attached to the "user" entity that initiated the sound. The client-side system plays + /// this sound as normal + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The UID of the entity "emitting" the audio. + /// The UID of the user that initiated this sound. This is usually some player's controlled entity. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public abstract IPlayingAudioStream? PlayPredicted(SoundSpecifier? sound, EntityUid source, EntityUid? user, AudioParams? audioParams = null); + + /// + /// Plays a predicted sound following an EntityCoordinates. The server will send the sound to every player in PVS range, + /// unless that player is attached to the "user" entity that initiated the sound. The client-side system plays + /// this sound as normal + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The entitycoordinates "emitting" the audio + /// The UID of the user that initiated this sound. This is usually some player's controlled entity. + /// Audio parameters to apply when playing the sound. Defaults to using the sound specifier's parameters + public abstract IPlayingAudioStream? PlayPredicted(SoundSpecifier? sound, EntityCoordinates coordinates, EntityUid? user, AudioParams? audioParams = null); + + // TODO rename to play static + /// + /// Play an audio file at a static position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The set of players that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? Play(string filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null); + + /// + /// Play an audio file at a static position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The player that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayStatic(string filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); + + /// + /// Play an audio file at a static position. + /// + /// The resource path to the OGG Vorbis file to play. + /// The player that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + public abstract IPlayingAudioStream? PlayStatic(string filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null); + + // TODO rename to play static + /// + /// Play an audio file at a static position. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The set of players that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + public IPlayingAudioStream? Play(SoundSpecifier? sound, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null) + { + return sound == null ? null : Play(GetSound(sound), playerFilter, coordinates, recordReplay, audioParams ?? sound.Params); + } + + /// + /// Play an audio file at a static position. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The player that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + public IPlayingAudioStream? PlayStatic(SoundSpecifier? sound, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + return sound == null ? null : PlayStatic(GetSound(sound), recipient, coordinates, audioParams ?? sound.Params); + } + + /// + /// Play an audio file at a static position. + /// + /// The sound specifier that points the audio file(s) that should be played. + /// The player that will hear the sound. + /// The coordinates at which to play the audio. + /// Audio parameters to apply when playing the sound. + public IPlayingAudioStream? PlayStatic(SoundSpecifier? sound, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null) + { + return sound == null ? null : PlayStatic(GetSound(sound), recipient, coordinates, audioParams ?? sound.Params); + } + + protected EntityCoordinates GetFallbackCoordinates(MapCoordinates mapCoordinates) + { + if (_mapManager.TryFindGridAt(mapCoordinates, out var gridUid, out var mapGrid)) + return new EntityCoordinates(gridUid, _map.WorldToLocal(gridUid, mapGrid, mapCoordinates.Position)); + + if (_mapManager.HasMapEntity(mapCoordinates.MapId)) + return new EntityCoordinates(_mapManager.GetMapEntityId(mapCoordinates.MapId), mapCoordinates.Position); + + return EntityCoordinates.Invalid; + } +} diff --git a/Robust.Shared/GameObjects/Systems/SharedEyeSystem.cs b/Robust.Shared/GameObjects/Systems/SharedEyeSystem.cs index 6575f9a0ac8..41473b22c61 100644 --- a/Robust.Shared/GameObjects/Systems/SharedEyeSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedEyeSystem.cs @@ -6,20 +6,20 @@ namespace Robust.Shared.GameObjects; public abstract class SharedEyeSystem : EntitySystem { - [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - /// /// Refreshes all values for IEye with the component. /// - public void UpdateEye(EyeComponent component) + public void UpdateEye(Entity entity) { - if (component._eye == null) + var component = entity.Comp; + if (!Resolve(entity, ref component)) return; - component._eye.Offset = component.Offset; - component._eye.DrawFov = component.DrawFov; - component._eye.Rotation = component.Rotation; - component._eye.Zoom = component.Zoom; + component.Eye.Offset = component.Offset; + component.Eye.DrawFov = component.DrawFov; + component.Eye.DrawLight = component.DrawLight; + component.Eye.Rotation = component.Rotation; + component.Eye.Zoom = component.Zoom; } public void SetOffset(EntityUid uid, Vector2 value, EyeComponent? eyeComponent = null) @@ -31,10 +31,7 @@ public void SetOffset(EntityUid uid, Vector2 value, EyeComponent? eyeComponent = return; eyeComponent.Offset = value; - if (eyeComponent._eye != null) - { - eyeComponent._eye.Offset = value; - } + eyeComponent.Eye.Offset = value; Dirty(uid, eyeComponent); } @@ -47,13 +44,23 @@ public void SetDrawFov(EntityUid uid, bool value, EyeComponent? eyeComponent = n return; eyeComponent.DrawFov = value; - if (eyeComponent._eye != null) - { - eyeComponent._eye.DrawFov = value; - } + eyeComponent.Eye.DrawFov = value; Dirty(uid, eyeComponent); } + public void SetDrawLight(Entity entity, bool value) + { + if (!Resolve(entity, ref entity.Comp)) + return; + + if (entity.Comp.DrawLight == value) + return; + + entity.Comp.DrawLight = value; + entity.Comp.Eye.DrawLight = value; + Dirty(entity); + } + public void SetRotation(EntityUid uid, Angle rotation, EyeComponent? eyeComponent = null) { if (!Resolve(uid, ref eyeComponent)) @@ -63,10 +70,7 @@ public void SetRotation(EntityUid uid, Angle rotation, EyeComponent? eyeComponen return; eyeComponent.Rotation = rotation; - if (eyeComponent._eye != null) - { - eyeComponent._eye.Rotation = rotation; - } + eyeComponent.Eye.Rotation = rotation; } public void SetTarget(EntityUid uid, EntityUid? value, EyeComponent? eyeComponent = null) @@ -90,10 +94,7 @@ public void SetZoom(EntityUid uid, Vector2 value, EyeComponent? eyeComponent = n return; eyeComponent.Zoom = value; - if (eyeComponent._eye != null) - { - eyeComponent._eye.Zoom = value; - } + eyeComponent.Eye.Zoom = value; } public void SetVisibilityMask(EntityUid uid, int value, EyeComponent? eyeComponent = null) diff --git a/Robust.Shared/GameObjects/Systems/SharedGridTraversalSystem.cs b/Robust.Shared/GameObjects/Systems/SharedGridTraversalSystem.cs index e0bd3110076..05e038002a5 100644 --- a/Robust.Shared/GameObjects/Systems/SharedGridTraversalSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedGridTraversalSystem.cs @@ -103,7 +103,7 @@ private void HandleMove( // Attach them to map / they are on an invalid grid if (oldGridId != null) { - _transform.SetParent(entity, xform, _mapManager.GetMapEntityIdOrThrow(xform.MapID)); + _transform.SetParent(entity, xform, xform.MapUid!.Value); var ev = new ChangedGridEvent(entity, oldGridId, null); RaiseLocalEvent(entity, ref ev); } diff --git a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs index 4a5ddbf3f50..847b95f3b99 100644 --- a/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs +++ b/Robust.Shared/GameObjects/Systems/SharedMapSystem.Grid.cs @@ -985,12 +985,25 @@ public IEnumerable GetAnchoredEntities(EntityUid uid, MapGridComponen // create an entire chunk for it. var gridChunkPos = GridTileToChunkIndices(uid, grid, pos); - if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk)) return Enumerable.Empty(); + if (!grid.Chunks.TryGetValue(gridChunkPos, out var chunk)) + return Enumerable.Empty(); var chunkTile = chunk.GridTileToChunkTile(pos); return chunk.GetSnapGridCell((ushort)chunkTile.X, (ushort)chunkTile.Y); } + public void GetAnchoredEntities(Entity grid, Vector2i pos, List list) + { + var gridChunkPos = GridTileToChunkIndices(grid.Owner, grid.Comp, pos); + if (!grid.Comp.Chunks.TryGetValue(gridChunkPos, out var chunk)) + return; + + var chunkTile = chunk.GridTileToChunkTile(pos); + var anchored = chunk.GetSnapGrid((ushort) chunkTile.X, (ushort) chunkTile.Y); + if (anchored != null) + list.AddRange(anchored); + } + public AnchoredEntitiesEnumerator GetAnchoredEntitiesEnumerator(EntityUid uid, MapGridComponent grid, Vector2i pos) { var gridChunkPos = GridTileToChunkIndices(uid, grid, pos); diff --git a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs index 51baa68e999..de6d681042c 100644 --- a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs +++ b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.Component.cs @@ -1,11 +1,8 @@ using JetBrains.Annotations; using Robust.Shared.GameStates; -using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Physics; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; using Robust.Shared.Utility; using System; using System.Linq; @@ -65,12 +62,7 @@ internal void ReAnchor( RaiseLocalEvent(uid, ref ev); } - [Obsolete("Use overload that takes an explicit EntityUid for the grid instead.")] - public bool AnchorEntity(EntityUid uid, TransformComponent xform, MapGridComponent grid, Vector2i tileIndices) - { - return AnchorEntity(uid, xform, grid.Owner, grid, tileIndices); - } - + [Obsolete("Use Entity variant")] public bool AnchorEntity( EntityUid uid, TransformComponent xform, @@ -78,12 +70,22 @@ public bool AnchorEntity( MapGridComponent grid, Vector2i tileIndices) { - if (!_map.AddToSnapGridCell(gridUid, grid, tileIndices, uid)) + return AnchorEntity((uid, xform), (gridUid, grid), tileIndices); + } + + public bool AnchorEntity( + Entity entity, + Entity grid, + Vector2i tileIndices) + { + var (uid, xform) = entity; + if (!_map.AddToSnapGridCell(grid, grid, tileIndices, uid)) return false; - var wasAnchored = xform._anchored; - Dirty(uid, xform); + var wasAnchored = entity.Comp._anchored; xform._anchored = true; + var meta = MetaData(uid); + Dirty(entity, meta); // Mark as static before doing position changes, to avoid the velocity change on parent change. _physics.TrySetBodyType(uid, BodyType.Static, xform: xform); @@ -95,22 +97,36 @@ public bool AnchorEntity( } // Anchor snapping. If there is a coordinate change, it will dirty the component for us. - var pos = new EntityCoordinates(gridUid, _map.GridTileToLocal(gridUid, grid, tileIndices).Position); - SetCoordinates(uid, xform, pos, unanchor: false); - + var pos = new EntityCoordinates(grid, _map.GridTileToLocal(grid, grid, tileIndices).Position); + SetCoordinates((uid, xform, meta), pos, unanchor: false); return true; } + [Obsolete("Use Entity variants")] public bool AnchorEntity(EntityUid uid, TransformComponent xform, MapGridComponent grid) { var tileIndices = _map.TileIndicesFor(grid.Owner, grid, xform.Coordinates); - return AnchorEntity(uid, xform, grid, tileIndices); + return AnchorEntity(uid, xform, grid.Owner, grid, tileIndices); } public bool AnchorEntity(EntityUid uid, TransformComponent xform) { - return _mapManager.TryGetGrid(xform.GridUid, out var grid) - && AnchorEntity(uid, xform, grid, _map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates)); + return AnchorEntity((uid, xform)); + } + + public bool AnchorEntity(Entity entity, Entity? grid = null) + { + DebugTools.Assert(grid == null || grid.Value.Owner == entity.Comp.GridUid); + + if (grid == null) + { + if (!TryComp(entity.Comp.GridUid, out MapGridComponent? gridComp)) + return false; + grid = (entity.Comp.GridUid.Value, gridComp); + } + + var tileIndices = _map.TileIndicesFor(grid.Value, grid.Value, entity.Comp.Coordinates); + return AnchorEntity(entity, grid.Value, tileIndices); } public void Unanchor(EntityUid uid, TransformComponent xform, bool setPhysics = true) @@ -145,39 +161,23 @@ public void Unanchor(EntityUid uid, TransformComponent xform, bool setPhysics = #region Contains /// - /// Returns whether the given entity is a child of this transform or one of its descendants. + /// Checks whether the first entity or one of it's children is the parent of some other entity. /// - public bool ContainsEntity(TransformComponent xform, EntityUid entity) + public bool ContainsEntity(EntityUid parent, Entity child) { - return ContainsEntity(xform, entity, XformQuery); - } - - /// - public bool ContainsEntity(TransformComponent xform, EntityUid entity, EntityQuery xformQuery) - { - return ContainsEntity(xform, xformQuery.GetComponent(entity), xformQuery); - } - - /// - public bool ContainsEntity(TransformComponent xform, TransformComponent entityTransform) - { - return ContainsEntity(xform, entityTransform, XformQuery); - } + if (!Resolve(child.Owner, ref child.Comp)) + return false; - /// - public bool ContainsEntity(TransformComponent xform, TransformComponent entityTransform, EntityQuery xformQuery) - { - // Is the entity the scene root - if (!entityTransform.ParentUid.IsValid()) + if (!child.Comp.ParentUid.IsValid()) return false; - // Is this the direct parent of the entity - if (xform.Owner == entityTransform.ParentUid) + if (parent == child.Comp.ParentUid) return true; - // Recursively search up the parents for this object - var parentXform = xformQuery.GetComponent(entityTransform.ParentUid); - return ContainsEntity(xform, parentXform, xformQuery); + if (!XformQuery.TryGetComponent(child.Comp.ParentUid, out var parentXform)) + return false; + + return ContainsEntity(parent, (child.Comp.ParentUid, parentXform)); } #endregion @@ -237,7 +237,7 @@ static MapId FindMapIdAndSet(EntityUid uid, TransformComponent xform, IEntityMan { var msg = $"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(component.ParentUid)}, new parent: {ToPrettyString(uid)}"; #if EXCEPTION_TOLERANCE - Logger.Error(msg); + Log.Error(msg); Del(uid); #else throw new InvalidOperationException(msg); @@ -432,7 +432,7 @@ public void SetLocalRotation(TransformComponent xform, Angle value) public void SetCoordinates(EntityUid uid, EntityCoordinates value) { - SetCoordinates(uid, Transform(uid), value); + SetCoordinates((uid, Transform(uid), MetaData(uid)), value); } /// @@ -443,8 +443,15 @@ public void SetCoordinates(EntityUid uid, EntityCoordinates value) /// Whether or not to unanchor the entity before moving. Note that this will still move the /// entity even when false. If you set this to false, you need to manually manage the grid lookup changes and ensure /// the final position is valid - public void SetCoordinates(EntityUid uid, TransformComponent xform, EntityCoordinates value, Angle? rotation = null, bool unanchor = true, TransformComponent? newParent = null, TransformComponent? oldParent = null) - { + public void SetCoordinates( + Entity entity, + EntityCoordinates value, + Angle? rotation = null, + bool unanchor = true, + TransformComponent? newParent = null, + TransformComponent? oldParent = null) + { + var (uid, xform, meta) = entity; // NOTE: This setter must be callable from before initialize. if (xform.ParentUid == value.EntityId @@ -460,8 +467,23 @@ public void SetCoordinates(EntityUid uid, TransformComponent xform, EntityCoordi if (xform.Anchored && unanchor) Unanchor(uid, xform); + if (value.EntityId != xform.ParentUid && value.EntityId.IsValid()) + { + if (meta.EntityLifeStage >= EntityLifeStage.Terminating) + { + Log.Error($"{ToPrettyString(uid)} is attempting to move while terminating. New parent: {ToPrettyString(value.EntityId)}. Trace: {Environment.StackTrace}"); + return; + } + + if (TerminatingOrDeleted(value.EntityId)) + { + Log.Error($"{ToPrettyString(uid)} is attempting to attach itself to a terminating entity {ToPrettyString(value.EntityId)}. Trace: {Environment.StackTrace}"); + return; + } + } + // Set new values - Dirty(uid, xform); + Dirty(uid, xform, meta); xform.MatricesDirty = true; xform._localPosition = value.Position; @@ -583,6 +605,18 @@ public void SetCoordinates(EntityUid uid, TransformComponent xform, EntityCoordi RaiseLocalEvent(uid, ref moveEvent, true); } + public void SetCoordinates( + EntityUid uid, + TransformComponent xform, + EntityCoordinates value, + Angle? rotation = null, + bool unanchor = true, + TransformComponent? newParent = null, + TransformComponent? oldParent = null) + { + SetCoordinates((uid, xform, MetaData(uid)), value, rotation, unanchor, newParent, oldParent); + } + #endregion #region Parent @@ -827,6 +861,27 @@ public Vector2 GetWorldPosition(TransformComponent component, EntityQuery entity) + { + return GetMapCoordinates(entity.Comp); + } + [Pure] public (Vector2 WorldPosition, Angle WorldRotation) GetWorldPositionRotation(EntityUid uid) { diff --git a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs index 038b1ce3f8f..0081ca146de 100644 --- a/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedTransformSystem.cs @@ -49,21 +49,6 @@ public override void Initialize() SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnGridAdd); - SubscribeLocalEvent(OnParentChange); - } - - private void OnParentChange(ref EntParentChangedMessage ev) - { - // TODO: when PVS errors on live servers get fixed, wrap this whole subscription in an #if DEBUG block to speed up parent changes & entity deletion. - if (ev.Transform.ParentUid == EntityUid.Invalid) - return; - - if (LifeStage(ev.Entity) >= EntityLifeStage.Terminating) - Log.Error($"Entity {ToPrettyString(ev.Entity)} is getting attached to a new parent while terminating. New parent: {ToPrettyString(ev.Transform.ParentUid)}. Trace: {Environment.StackTrace}"); - - - if (LifeStage(ev.Transform.ParentUid) >= EntityLifeStage.Terminating) - Log.Error($"Entity {ToPrettyString(ev.Entity)} is attaching itself to a terminating entity {ToPrettyString(ev.Transform.ParentUid)}. Trace: {Environment.StackTrace}"); } private void MapManagerOnTileChanged(ref TileChangedEvent e) @@ -261,6 +246,39 @@ public Vector2i GetGridOrMapTilePosition(EntityUid uid, TransformComponent? xfor // We're on a grid, need to convert the coordinates to grid tiles. return _map.CoordinatesToTile(xform.GridUid.Value, Comp(xform.GridUid.Value), xform.Coordinates); } + + /// + /// Helper method that returns the grid tile an entity is on. + /// + public Vector2i GetGridTilePositionOrDefault(Entity entity, MapGridComponent? grid = null) + { + var xform = entity.Comp; + if(!Resolve(entity.Owner, ref xform) || xform.GridUid == null) + return Vector2i.Zero; + + if (!Resolve(xform.GridUid.Value, ref grid)) + return Vector2i.Zero; + + return _map.CoordinatesToTile(xform.GridUid.Value, grid, xform.Coordinates); + } + + /// + /// Helper method that returns the grid tile an entity is on. + /// + public bool TryGetGridTilePosition(Entity entity, out Vector2i indices, MapGridComponent? grid = null) + { + indices = default; + var xform = entity.Comp; + if(!Resolve(entity.Owner, ref xform) || xform.GridUid == null) + return false; + + if (!Resolve(xform.GridUid.Value, ref grid)) + return false; + + indices = _map.CoordinatesToTile(xform.GridUid.Value, grid, xform.Coordinates); + return true; + } + } [ByRefEvent] diff --git a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs index 241085d5ed1..2b6bc8ea11b 100644 --- a/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs +++ b/Robust.Shared/GameObjects/Systems/SharedUserInterfaceSystem.cs @@ -1,10 +1,16 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Robust.Shared.Enums; using Robust.Shared.Player; +using Robust.Shared.Utility; namespace Robust.Shared.GameObjects; public abstract class SharedUserInterfaceSystem : EntitySystem { + protected readonly Dictionary> OpenInterfaces = new(); + public override void Initialize() { base.Initialize(); @@ -99,6 +105,73 @@ protected virtual void CloseShared(PlayerBoundUserInterface bui, ICommonSession { } + public bool TryGetUi(EntityUid uid, Enum uiKey, [NotNullWhen(true)] out PlayerBoundUserInterface? bui, UserInterfaceComponent? ui = null) + { + bui = null; + + return Resolve(uid, ref ui, false) && ui.Interfaces.TryGetValue(uiKey, out bui); + } + + /// + /// Switches between closed and open for a specific client. + /// + public virtual bool TryToggleUi(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null) + { + if (!TryGetUi(uid, uiKey, out var bui, ui)) + return false; + + ToggleUi(bui, session); + return true; + } + + /// + /// Switches between closed and open for a specific client. + /// + public void ToggleUi(PlayerBoundUserInterface bui, ICommonSession session) + { + if (bui._subscribedSessions.Contains(session)) + CloseUi(bui, session); + else + OpenUi(bui, session); + } + + public bool TryOpen(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null) + { + if (!TryGetUi(uid, uiKey, out var bui, ui)) + return false; + + return OpenUi(bui, session); + } + + /// + /// Opens this interface for a specific client. + /// + public bool OpenUi(PlayerBoundUserInterface bui, ICommonSession session) + { + if (session.Status == SessionStatus.Connecting || session.Status == SessionStatus.Disconnected) + return false; + + if (!bui._subscribedSessions.Add(session)) + return false; + + OpenInterfaces.GetOrNew(session).Add(bui); + RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session)); + + RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.Channel); + + // Fun fact, clients needs to have BUIs open before they can receive the state..... + if (bui.LastStateMsg != null) + RaiseNetworkEvent(bui.LastStateMsg, session.Channel); + + ActivateInterface(bui); + return true; + } + + private void ActivateInterface(PlayerBoundUserInterface ui) + { + EnsureComp(ui.Owner).Interfaces.Add(ui); + } + internal bool TryCloseUi(ICommonSession? session, EntityUid uid, Enum uiKey, bool remoteCall = false, UserInterfaceComponent? uiComp = null) { if (!Resolve(uid, ref uiComp)) @@ -119,6 +192,27 @@ internal bool TryCloseUi(ICommonSession? session, EntityUid uid, Enum uiKey, boo return true; } + public bool TryClose(EntityUid uid, Enum uiKey, ICommonSession session, UserInterfaceComponent? ui = null) + { + if (!TryGetUi(uid, uiKey, out var bui, ui)) + return false; + + return CloseUi(bui, session); + } + + /// + /// Close this interface for a specific client. + /// + public bool CloseUi(PlayerBoundUserInterface bui, ICommonSession session, ActiveUserInterfaceComponent? activeUis = null) + { + if (!bui._subscribedSessions.Remove(session)) + return false; + + RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.Channel); + CloseShared(bui, session, activeUis); + return true; + } + /// /// Raised by client-side UIs to send to server. /// diff --git a/Robust.Shared/Graphics/Eye.cs b/Robust.Shared/Graphics/Eye.cs index 3966fbf5102..aca7eea5245 100644 --- a/Robust.Shared/Graphics/Eye.cs +++ b/Robust.Shared/Graphics/Eye.cs @@ -19,6 +19,10 @@ public class Eye : IEye [ViewVariables(VVAccess.ReadWrite)] public bool DrawFov { get; set; } = true; + /// + [ViewVariables] + public bool DrawLight { get; set; } = true; + /// [ViewVariables(VVAccess.ReadWrite)] public virtual MapCoordinates Position diff --git a/Robust.Shared/Graphics/IClydeHandle.cs b/Robust.Shared/Graphics/IClydeHandle.cs deleted file mode 100644 index bdff0d4c05a..00000000000 --- a/Robust.Shared/Graphics/IClydeHandle.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Robust.Shared.Graphics; - -public interface IClydeHandle -{ - long Value { get; } -} - diff --git a/Robust.Shared/Graphics/IEye.cs b/Robust.Shared/Graphics/IEye.cs index 0cdf769a25f..d2e33614de9 100644 --- a/Robust.Shared/Graphics/IEye.cs +++ b/Robust.Shared/Graphics/IEye.cs @@ -17,6 +17,11 @@ public interface IEye /// bool DrawFov { get; set; } + /// + /// Whether to draw lights for this eye. + /// + bool DrawLight { get; set; } + /// /// Current position of the center of the eye in the game world. /// diff --git a/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs b/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs index 22480039d53..76eedfff8fe 100644 --- a/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs +++ b/Robust.Shared/Map/Enumerators/GridTileEnumerator.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Robust.Shared.GameObjects; using Robust.Shared.Maths; +using Robust.Shared.Utility; namespace Robust.Shared.Map.Enumerators; @@ -27,32 +28,33 @@ internal GridTileEnumerator(EntityUid gridUid, Dictionary.En public bool MoveNext([NotNullWhen(true)] out TileRef? tileRef) { - if (_index == _chunkSize * _chunkSize) + while (true) { - if (!_chunkEnumerator.MoveNext()) + if (_index == _chunkSize * _chunkSize) { - tileRef = null; - return false; - } + if (!_chunkEnumerator.MoveNext()) + { + tileRef = null; + return false; + } - _index = 0; - } + _index = 0; + } - var (chunkOrigin, chunk) = _chunkEnumerator.Current; + var (chunkOrigin, chunk) = _chunkEnumerator.Current; + DebugTools.Assert(chunk.FilledTiles > 0, $"Encountered empty chunk while enumerating tiles"); + var x = (ushort) (_index / _chunkSize); + var y = (ushort) (_index % _chunkSize); + var tile = chunk.GetTile(x, y); + _index++; - var x = (ushort) (_index / _chunkSize); - var y = (ushort) (_index % _chunkSize); - var tile = chunk.GetTile(x, y); - _index++; + if (_ignoreEmpty && tile.IsEmpty) + continue; - if (_ignoreEmpty && tile.IsEmpty) - { - return MoveNext(out tileRef); + var gridX = x + chunkOrigin.X * _chunkSize; + var gridY = y + chunkOrigin.Y * _chunkSize; + tileRef = new TileRef(_gridUid, gridX, gridY, tile); + return true; } - - var gridX = x + chunkOrigin.X * _chunkSize; - var gridY = y + chunkOrigin.Y * _chunkSize; - tileRef = new TileRef(_gridUid, gridX, gridY, tile); - return true; } -} \ No newline at end of file +} diff --git a/Robust.Shared/Map/MapManager.MapCollection.cs b/Robust.Shared/Map/MapManager.MapCollection.cs index 4d351393e5b..ba4f74f2095 100644 --- a/Robust.Shared/Map/MapManager.MapCollection.cs +++ b/Robust.Shared/Map/MapManager.MapCollection.cs @@ -244,6 +244,7 @@ MapId IMapManagerInternal.CreateMap(MapId? mapId, EntityUid entityUid) } } + var args = new MapEventArgs(actualId); return actualId; } } diff --git a/Robust.Shared/Network/NetManager.cs b/Robust.Shared/Network/NetManager.cs index 2b9a4623ce7..7f0ef8b2a9c 100644 --- a/Robust.Shared/Network/NetManager.cs +++ b/Robust.Shared/Network/NetManager.cs @@ -576,6 +576,17 @@ private NetPeerConfiguration _getBaseNetPeerConfig() // ping the client once per second. netConfig.PingInterval = 1f; + var poolSize = _config.GetCVar(CVars.NetPoolSize); + + if (poolSize <= 0) + { + netConfig.UseMessageRecycling = false; + } + else + { + netConfig.RecycledCacheMaxCount = Math.Min(poolSize, 8192); + } + netConfig.SendBufferSize = _config.GetCVar(CVars.NetSendBufferSize); netConfig.ReceiveBufferSize = _config.GetCVar(CVars.NetReceiveBufferSize); netConfig.MaximumHandshakeAttempts = 5; diff --git a/Robust.Shared/Physics/Dynamics/Contacts/ContactPositionConstraint.cs b/Robust.Shared/Physics/Dynamics/Contacts/ContactPositionConstraint.cs index f3f2cae0255..4d96a81801f 100644 --- a/Robust.Shared/Physics/Dynamics/Contacts/ContactPositionConstraint.cs +++ b/Robust.Shared/Physics/Dynamics/Contacts/ContactPositionConstraint.cs @@ -21,7 +21,6 @@ */ using System.Numerics; -using Robust.Shared.Maths; using Robust.Shared.Physics.Collision; namespace Robust.Shared.Physics.Dynamics.Contacts diff --git a/Robust.Shared/Physics/Dynamics/Contacts/ContactVelocityConstraint.cs b/Robust.Shared/Physics/Dynamics/Contacts/ContactVelocityConstraint.cs index 14e325e672a..a1e876c3bc1 100644 --- a/Robust.Shared/Physics/Dynamics/Contacts/ContactVelocityConstraint.cs +++ b/Robust.Shared/Physics/Dynamics/Contacts/ContactVelocityConstraint.cs @@ -21,7 +21,6 @@ */ using System.Numerics; -using Robust.Shared.Maths; namespace Robust.Shared.Physics.Dynamics.Contacts { diff --git a/Robust.Shared/Physics/Systems/SharedJointSystem.Relay.cs b/Robust.Shared/Physics/Systems/SharedJointSystem.Relay.cs index 03443b8c931..65c1f48177c 100644 --- a/Robust.Shared/Physics/Systems/SharedJointSystem.Relay.cs +++ b/Robust.Shared/Physics/Systems/SharedJointSystem.Relay.cs @@ -43,8 +43,7 @@ private void OnRelayHandleState(EntityUid uid, JointRelayTargetComponent compone if (args.Current is not JointRelayComponentState state) return; - component.Relayed.Clear(); - component.Relayed.UnionWith(EnsureEntitySet(state.Entities, uid)); + EnsureEntitySet(state.Entities, uid, component.Relayed); } private void OnRelayShutdown(EntityUid uid, JointRelayTargetComponent component, ComponentShutdown args) diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs index beece398247..6cd9566cfe1 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Components.cs @@ -614,10 +614,11 @@ public bool WakeBody(EntityUid uid, bool force = false, FixturesComponent? manag public Transform GetPhysicsTransform(EntityUid uid, TransformComponent? xform = null, EntityQuery? xformQuery = null) { - if (!_xformQuery.Resolve(uid, ref xform)) - return Physics.Transform.Empty; + if (!Resolve(uid, ref xform)) + return new Transform(); - var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform); + xformQuery ??= _xformQuery; + var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform, xformQuery.Value); return new Transform(worldPos, worldRot); } @@ -630,7 +631,7 @@ public Box2 GetWorldAABB(EntityUid uid, FixturesComponent? manager = null, Physi if (!Resolve(uid, ref manager, ref body, ref xform)) return new Box2(); - var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform); + var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform, GetEntityQuery()); var transform = new Transform(worldPos, (float) worldRot.Theta); @@ -652,10 +653,10 @@ public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, Physic { if (!Resolve(uid, ref body, ref xform, ref manager)) { - return Box2.Empty; + return new Box2(); } - var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform); + var (worldPos, worldRot) = _transform.GetWorldPositionRotation(xform, GetEntityQuery()); var transform = new Transform(worldPos, (float) worldRot.Theta); diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs index 5d18bff6c98..649121e7111 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Queries.cs @@ -457,14 +457,14 @@ public bool TryGetNearestPoints(EntityUid uidA, EntityUid uidB, } public bool TryGetNearest(EntityUid uidA, EntityUid uidB, - out Vector2 point, + out Vector2 pointA, out Vector2 pointB, out float distance, Transform xfA, Transform xfB, FixturesComponent? managerA = null, FixturesComponent? managerB = null, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null) { - point = Vector2.Zero; + pointA = Vector2.Zero; pointB = Vector2.Zero; if (!Resolve(uidA, ref managerA, ref bodyA) || @@ -489,83 +489,29 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, if (bodyA.Hard && !fixtureA.Hard) continue; - DebugTools.Assert(fixtureA.ProxyCount <= 1); - - foreach (var fixtureB in managerB.Fixtures.Values) + for (var i = 0; i < fixtureA.Shape.ChildCount; i++) { - if (bodyB.Hard && !fixtureB.Hard) - continue; - - DebugTools.Assert(fixtureB.ProxyCount <= 1); - input.ProxyA.Set(fixtureA.Shape, 0); - input.ProxyB.Set(fixtureB.Shape, 0); - DistanceManager.ComputeDistance(out var output, out _, input); - - if (distance < output.Distance) - continue; - - point = output.PointA; - pointB = output.PointB; - distance = output.Distance; - } - } - - return true; - } - - /// - /// Gets the nearest points in map terms and the distance between them. - /// If a body is hard it only considers hard fixtures. - /// - public bool TryGetNearest(EntityUid uid, MapCoordinates coordinates, - out Vector2 point, out float distance, - TransformComponent? xformA = null, FixturesComponent? manager = null, PhysicsComponent? body = null) - { - if (!Resolve(uid, ref xformA) || - xformA.MapID != coordinates.MapId) - { - point = Vector2.Zero; - distance = 0f; - return false; - } - - point = Vector2.Zero; - - if (!Resolve(uid, ref manager, ref body) || - manager.FixtureCount == 0) - { - distance = 0f; - return false; - } - - var xfA = GetPhysicsTransform(uid, xformA); - var xfB = new Transform(coordinates.Position, Angle.Zero); + input.ProxyA.Set(fixtureA.Shape, i); - distance = float.MaxValue; - var input = new DistanceInput(); - - input.TransformA = xfA; - input.TransformB = xfB; - input.UseRadii = true; - var pointShape = new PhysShapeCircle(10 * float.Epsilon, Vector2.Zero); - - // No requirement on collision being enabled so chainshapes will fail - foreach (var fixtureA in manager.Fixtures.Values) - { - if (body.Hard && !fixtureA.Hard) - continue; - - DebugTools.Assert(fixtureA.ProxyCount <= 1); + foreach (var fixtureB in managerB.Fixtures.Values) + { + if (bodyB.Hard && !fixtureB.Hard) + continue; - input.ProxyA.Set(fixtureA.Shape, 0); - input.ProxyB.Set(pointShape, 0); - DistanceManager.ComputeDistance(out var output, out _, input); + for (var j = 0; j < fixtureB.Shape.ChildCount; j++) + { + input.ProxyB.Set(fixtureB.Shape, j); + DistanceManager.ComputeDistance(out var output, out _, input); - if (distance < output.Distance) - continue; + if (distance < output.Distance) + continue; - point = output.PointA; - distance = output.Distance; + pointA = output.PointA; + pointB = output.PointB; + distance = output.Distance; + } + } + } } return true; @@ -576,7 +522,7 @@ public bool TryGetNearest(EntityUid uid, MapCoordinates coordinates, /// If a body is hard it only considers hard fixtures. /// public bool TryGetNearest(EntityUid uidA, EntityUid uidB, - out Vector2 point, + out Vector2 pointA, out Vector2 pointB, out float distance, TransformComponent? xformA = null, TransformComponent? xformB = null, @@ -586,7 +532,7 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, if (!Resolve(uidA, ref xformA) || !Resolve(uidB, ref xformB) || xformA.MapID != xformB.MapID) { - point = Vector2.Zero; + pointA = Vector2.Zero; pointB = Vector2.Zero; distance = 0f; return false; @@ -595,7 +541,7 @@ public bool TryGetNearest(EntityUid uidA, EntityUid uidB, var xfA = GetPhysicsTransform(uidA, xformA); var xfB = GetPhysicsTransform(uidB, xformB); - return TryGetNearest(uidA, uidB, out point, out pointB, out distance, xfA, xfB, managerA, managerB, bodyA, bodyB); + return TryGetNearest(uidA, uidB, out pointA, out pointB, out distance, xfA, xfB, managerA, managerB, bodyA, bodyB); } #endregion diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Solver.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Solver.cs index 5130a8e235d..d1b8d9b3df2 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Solver.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Solver.cs @@ -67,12 +67,8 @@ private void ResetSolver( velocityConstraint.TangentSpeed = contact.TangentSpeed; velocityConstraint.IndexA = bodyA.IslandIndex[island.Index]; velocityConstraint.IndexB = bodyB.IslandIndex[island.Index]; - velocityConstraint.Points = new VelocityConstraintPoint[2]; - - for (var j = 0; j < 2; j++) - { - velocityConstraint.Points[j] = new VelocityConstraintPoint(); - } + Array.Resize(ref velocityConstraint.Points, 2); + // Don't need to reset point data as it all gets set below. var (invMassA, invMassB) = GetInvMass(bodyA, bodyB); @@ -91,7 +87,7 @@ private void ResetSolver( (positionConstraint.InvMassA, positionConstraint.InvMassB) = (invMassA, invMassB); positionConstraint.LocalCenterA = bodyA.LocalCenter; positionConstraint.LocalCenterB = bodyB.LocalCenter; - positionConstraint.LocalPoints = new Vector2[2]; + Array.Resize(ref positionConstraint.LocalPoints, 2); positionConstraint.InvIA = bodyA.InvI; positionConstraint.InvIB = bodyB.InvI; diff --git a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs index 13f3c65c543..27a294e204d 100644 --- a/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs +++ b/Robust.Shared/Physics/Systems/SharedPhysicsSystem.Velocities.cs @@ -43,6 +43,9 @@ public Vector2 GetMapLinearVelocity( EntityQuery? xformQuery = null, EntityQuery? physicsQuery = null) { + if (!Resolve(uid, ref component)) + return Vector2.Zero; + xformQuery ??= EntityManager.GetEntityQuery(); physicsQuery ??= EntityManager.GetEntityQuery(); @@ -50,7 +53,7 @@ public Vector2 GetMapLinearVelocity( var parent = xform.ParentUid; var localPos = xform.LocalPosition; - var velocity = component?.LinearVelocity ?? Vector2.Zero; + var velocity = component.LinearVelocity; Vector2 angularComponent = Vector2.Zero; while (parent != xform.MapUid && parent.IsValid()) diff --git a/Robust.Shared/Physics/Transform.cs b/Robust.Shared/Physics/Transform.cs index 8aee88eb1ec..ba0cfde4b37 100644 --- a/Robust.Shared/Physics/Transform.cs +++ b/Robust.Shared/Physics/Transform.cs @@ -32,8 +32,6 @@ namespace Robust.Shared.Physics // TODO: Probably replace this internally with just the Vector2 and radians but I'd need to re-learn trig so yeah.... public struct Transform { - public static readonly Transform Empty = new Transform(); - public Vector2 Position; public Quaternion2D Quaternion2D; diff --git a/Robust.Shared/Player/ActorSystem.cs b/Robust.Shared/Player/ActorSystem.cs new file mode 100644 index 00000000000..b725556e1ee --- /dev/null +++ b/Robust.Shared/Player/ActorSystem.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; + +namespace Robust.Shared.Player; + +/// +/// System that handles . +/// +public sealed class ActorSystem : EntitySystem +{ + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnActorShutdown); + } + + private void OnActorShutdown(EntityUid entity, ActorComponent component, ComponentShutdown args) + { + _playerManager.SetAttachedEntity(component.PlayerSession, null); + } +} diff --git a/Robust.Shared/Player/CommonSession.cs b/Robust.Shared/Player/CommonSession.cs index e70be2cd84d..3dbc40459c7 100644 --- a/Robust.Shared/Player/CommonSession.cs +++ b/Robust.Shared/Player/CommonSession.cs @@ -17,10 +17,10 @@ internal sealed class CommonSession : ICommonSession public NetUserId UserId { get; } [ViewVariables] - public string Name { get; set; } = ""; + public string Name { get; internal set; } = ""; [ViewVariables] - public short Ping { get; set; } + public short Ping { get; internal set; } [ViewVariables] public DateTime ConnectedTime { get; set; } @@ -34,6 +34,8 @@ internal sealed class CommonSession : ICommonSession [ViewVariables] public SessionData Data { get; } + public bool ClientSide { get; set; } + [ViewVariables] public INetChannel Channel { get; set; } = default!; @@ -54,4 +56,4 @@ public CommonSession(NetUserId user, string name, SessionData data) Name = name; Data = data; } -} \ No newline at end of file +} diff --git a/Robust.Shared/Player/Events.cs b/Robust.Shared/Player/Events.cs new file mode 100644 index 00000000000..22b6db449cc --- /dev/null +++ b/Robust.Shared/Player/Events.cs @@ -0,0 +1,63 @@ +using Robust.Shared.GameObjects; + +namespace Robust.Shared.Player; + +/// +/// Event that gets raised when a player has been attached to an entity. This event is both raised directed at the +/// entity and broadcast. +/// +public sealed class PlayerAttachedEvent : EntityEventArgs +{ + public readonly EntityUid Entity; + public readonly ICommonSession Player; + + public PlayerAttachedEvent(EntityUid entity, ICommonSession player) + { + Entity = entity; + Player = player; + } +} + +/// +/// Event that gets raised when a player has been detached from an entity. This event is both raised directed at the +/// entity and broadcast. +/// +public sealed class PlayerDetachedEvent : EntityEventArgs +{ + public readonly EntityUid Entity; + public readonly ICommonSession Player; + + public PlayerDetachedEvent(EntityUid entity, ICommonSession player) + { + Entity = entity; + Player = player; + } +} + +/// +/// Variant of that gets raised by the client when the local session gets attached to +/// a new entity. This event will also get raised if the local session changes. +/// +public sealed class LocalPlayerAttachedEvent : EntityEventArgs +{ + public readonly EntityUid Entity; + + public LocalPlayerAttachedEvent(EntityUid entity) + { + Entity = entity; + } +} + +/// +/// Variant of that gets raised by the client when the local session gets attached to +/// a new entity. This event will also get raised if the local session changes. +/// +public sealed class LocalPlayerDetachedEvent : EntityEventArgs +{ + public readonly EntityUid Entity; + + public LocalPlayerDetachedEvent(EntityUid entity) + { + Entity = entity; + } +} diff --git a/Robust.Shared/Player/Filter.cs b/Robust.Shared/Player/Filter.cs index 4ba9544ce5b..2348ec9de71 100644 --- a/Robust.Shared/Player/Filter.cs +++ b/Robust.Shared/Player/Filter.cs @@ -22,8 +22,6 @@ private Filter() { } public bool SendReliable { get; private set; } - public int Count => _recipients.Count; - public IEnumerable Recipients => _recipients; /// diff --git a/Robust.Shared/Player/ICommonSession.cs b/Robust.Shared/Player/ICommonSession.cs index 4fb96d9ffc8..3ff1ff00e5a 100644 --- a/Robust.Shared/Player/ICommonSession.cs +++ b/Robust.Shared/Player/ICommonSession.cs @@ -30,12 +30,12 @@ public interface ICommonSession /// /// Current name of this player. /// - string Name { get; set; } + string Name { get; } /// /// Current connection latency of this session from the server to their client. /// - short Ping { get; internal set; } + short Ping { get; } /// /// The current network channel for this session. @@ -43,8 +43,9 @@ public interface ICommonSession /// /// On the Server every player has a network channel, /// on the Client only the LocalPlayer has a network channel, and that channel points to the server. + /// Unless you know what you are doing, you shouldn't be modifying this directly. /// - INetChannel Channel { get; } + INetChannel Channel { get; set; } LoginType AuthType { get; } @@ -67,4 +68,10 @@ public interface ICommonSession [Obsolete("Just use the Channel field instead.")] INetChannel ConnectedClient => Channel; -} \ No newline at end of file + + /// + /// If true, this indicates that this is a client-side session, and should be ignored when applying a server's + /// game state. + /// + bool ClientSide { get; set; } +} diff --git a/Robust.Shared/Player/ISharedPlayerManager.cs b/Robust.Shared/Player/ISharedPlayerManager.cs index aaf465a1aee..c1aaf33834a 100644 --- a/Robust.Shared/Player/ISharedPlayerManager.cs +++ b/Robust.Shared/Player/ISharedPlayerManager.cs @@ -87,7 +87,7 @@ public interface ISharedPlayerManager /// /// Attempts to get the session with the given . /// - bool TryGetSessionById(NetUserId user, [NotNullWhen(true)] out ICommonSession? session); + bool TryGetSessionById([NotNullWhen(true)] NetUserId? user, [NotNullWhen(true)] out ICommonSession? session); /// /// Attempts to get the session with the given . @@ -121,16 +121,49 @@ public interface ISharedPlayerManager void RemoveSession(ICommonSession session, bool removeData = false); void RemoveSession(NetUserId user, bool removeData = false); + ICommonSession CreateAndAddSession(INetChannel channel); + + ICommonSession CreateAndAddSession(NetUserId user, string name); + + /// + /// Sets a session's attached entity, optionally kicking any sessions already attached to it. + /// + /// The player whose attached entity should get updated + /// The entity to attach the player to, if any. + /// Whether to kick any existing players that are already attached to the entity + /// The player that was forcefully kicked, if any. + /// Whether the attach succeeded, or not. + bool SetAttachedEntity( + [NotNullWhen(true)] ICommonSession? session, + EntityUid? entity, + out ICommonSession? kicked, + bool force = false); + /// - /// Updates a session's + /// Sets a session's attached entity, optionally kicking any sessions already attached to it. /// - void SetAttachedEntity(ICommonSession session, EntityUid? uid); + /// The player whose attached entity should get updated + /// The entity to attach the player to, if any. + /// Whether to kick any existing players that are already attached to the entity + /// Whether the attach succeeded, or not. + bool SetAttachedEntity([NotNullWhen(true)] ICommonSession? session, EntityUid? entity, bool force = false) + => SetAttachedEntity(session, entity, out _, force); /// /// Updates a session's /// void SetStatus(ICommonSession session, SessionStatus status); + /// + /// Updates a session's + /// + void SetPing(ICommonSession session, short ping); + + /// + /// Updates a session's + /// + public void SetName(ICommonSession session, string name); + /// /// Set the session's status to . /// @@ -138,8 +171,4 @@ public interface ISharedPlayerManager [Obsolete("Use GetSessionById()")] ICommonSession GetSessionByUserId(NetUserId user) => GetSessionById(user); - - [Obsolete("Use TryGetSessionById()")] - bool TryGetSessionByUserId(NetUserId user, [NotNullWhen(true)] out ICommonSession? session) - => TryGetSessionById(user, out session); } diff --git a/Robust.Shared/Player/SharedPlayerManager.Sessions.cs b/Robust.Shared/Player/SharedPlayerManager.Sessions.cs index bcaec3769c0..b615514814d 100644 --- a/Robust.Shared/Player/SharedPlayerManager.Sessions.cs +++ b/Robust.Shared/Player/SharedPlayerManager.Sessions.cs @@ -50,12 +50,18 @@ public ICommonSession[] Sessions } } - public bool TryGetSessionById(NetUserId user, [NotNullWhen(true)] out ICommonSession? session) + public bool TryGetSessionById([NotNullWhen(true)] NetUserId? user, [NotNullWhen(true)] out ICommonSession? session) { + if (user == null) + { + session = null; + return false; + } + Lock.EnterReadLock(); try { - return InternalSessions.TryGetValue(user, out session); + return InternalSessions.TryGetValue(user.Value, out session); } finally { @@ -93,7 +99,14 @@ protected virtual CommonSession CreateSession(NetUserId user, string name, Sessi return new CommonSession(user, name, data); } - internal CommonSession CreateAndAddSession(NetUserId user, string name) + public ICommonSession CreateAndAddSession(INetChannel channel) + { + var session = CreateAndAddSession(channel.UserId, channel.UserName); + session.Channel = channel; + return session; + } + + public ICommonSession CreateAndAddSession(NetUserId user, string name) { Lock.EnterWriteLock(); CommonSession session; @@ -134,13 +147,78 @@ public void RemoveSession(NetUserId user, bool removeData = false) } } - public virtual void SetAttachedEntity(ICommonSession session, EntityUid? uid) + /// + public virtual bool SetAttachedEntity( + [NotNullWhen(true)] ICommonSession? session, + EntityUid? uid, + out ICommonSession? kicked, + bool force = false) { + kicked = null; + if (session == null) + return false; + if (session.AttachedEntity == uid) + { + DebugTools.Assert(uid == null || EntManager.HasComponent(uid)); + return true; + } + + if (uid != null) + return Attach(session, uid.Value, out kicked, force); + + Detach(session); + return true; + } + + private void Detach(ICommonSession session) + { + if (session.AttachedEntity is not {} uid) return; + ((CommonSession) session).AttachedEntity = null; + UpdateState(session); + + if (EntManager.TryGetComponent(uid, out ActorComponent? actor) && actor.LifeStage <= ComponentLifeStage.Running) + { + actor.PlayerSession = default!; + EntManager.RemoveComponent(uid, actor); + } + + EntManager.EventBus.RaiseLocalEvent(uid, new PlayerDetachedEvent(uid, session), true); + } + + private bool Attach(ICommonSession session, EntityUid uid, out ICommonSession? kicked, bool force = false) + { + kicked = null; + if (!EntManager.TryGetComponent(uid, out MetaDataComponent? meta)) + return false; + + if (meta.EntityLifeStage >= EntityLifeStage.Terminating) + return false; + + if (EntManager.EnsureComponent(uid, out var actor)) + { + // component already existed. + DebugTools.AssertNotNull(actor.PlayerSession); + if (!force) + return false; + + kicked = actor.PlayerSession; + Detach(kicked); + } + + if (_netMan.IsServer) + EntManager.EnsureComponent(uid); + + if (session.AttachedEntity != null) + Detach(session); + ((CommonSession) session).AttachedEntity = uid; + actor.PlayerSession = session; UpdateState(session); + EntManager.EventBus.RaiseLocalEvent(uid, new PlayerAttachedEvent(uid, session), true); + return true; } public void SetStatus(ICommonSession session, SessionStatus status) @@ -155,6 +233,18 @@ public void SetStatus(ICommonSession session, SessionStatus status) PlayerStatusChanged?.Invoke(this, new SessionStatusEventArgs(session, old, status)); } + public void SetPing(ICommonSession session, short ping) + { + ((CommonSession) session).Ping = ping; + UpdateState(session); + } + + public void SetName(ICommonSession session, string name) + { + ((CommonSession) session).Name = name; + UpdateState(session); + } + public void JoinGame(ICommonSession session) { // This currently just directly sets the session's status, as this was the old behaviour. diff --git a/Robust.Shared/Player/SharedPlayerManager.cs b/Robust.Shared/Player/SharedPlayerManager.cs index 9a9deedada8..92f14fd4cdc 100644 --- a/Robust.Shared/Player/SharedPlayerManager.cs +++ b/Robust.Shared/Player/SharedPlayerManager.cs @@ -14,6 +14,7 @@ internal abstract partial class SharedPlayerManager : ISharedPlayerManager [Dependency] protected readonly IEntityManager EntManager = default!; [Dependency] protected readonly ILogManager LogMan = default!; [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] private readonly INetManager _netMan = default!; protected ISawmill Sawmill = default!; @@ -26,7 +27,7 @@ internal abstract partial class SharedPlayerManager : ISharedPlayerManager public int PlayerCount => InternalSessions.Count; [ViewVariables] - public ICommonSession? LocalSession { get; set; } + public ICommonSession? LocalSession { get; protected set; } [ViewVariables] public NetUserId? LocalUser => LocalSession?.UserId; diff --git a/Robust.Shared/Prototypes/Exceptions.cs b/Robust.Shared/Prototypes/Exceptions.cs index 96bf1e1b122..3f0f185d603 100644 --- a/Robust.Shared/Prototypes/Exceptions.cs +++ b/Robust.Shared/Prototypes/Exceptions.cs @@ -28,22 +28,13 @@ public PrototypeLoadException(SerializationInfo info, StreamingContext context) [Virtual] public class UnknownPrototypeException : Exception { - public override string Message => "Unknown prototype: " + Prototype; - public readonly string? Prototype; + public override string Message => $"Unknown {Kind.Name} prototype: {Prototype}" ; + public readonly string Prototype; + public readonly Type Kind; - public UnknownPrototypeException(string prototype) + public UnknownPrototypeException(string prototype, Type kind) { Prototype = prototype; - } - - public UnknownPrototypeException(SerializationInfo info, StreamingContext context) : base(info, context) - { - Prototype = (string?) info.GetValue("prototype", typeof(string)); - } - - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - info.AddValue("prototype", Prototype, typeof(string)); + Kind = kind; } } diff --git a/Robust.Shared/Prototypes/PrototypeManager.cs b/Robust.Shared/Prototypes/PrototypeManager.cs index 3c642a4d176..9d89f74fe89 100644 --- a/Robust.Shared/Prototypes/PrototypeManager.cs +++ b/Robust.Shared/Prototypes/PrototypeManager.cs @@ -196,7 +196,7 @@ public T Index(string id) where T : class, IPrototype } catch (KeyNotFoundException) { - throw new UnknownPrototypeException(id); + throw new UnknownPrototypeException(id, typeof(T)); } } @@ -594,7 +594,7 @@ public bool HasIndex(string id) where T : class, IPrototype { if (!_kinds.TryGetValue(typeof(T), out var index)) { - throw new UnknownPrototypeException(id); + throw new UnknownPrototypeException(id, typeof(T)); } return index.Instances.ContainsKey(id); @@ -625,7 +625,7 @@ public bool TryIndex(Type kind, string id, [NotNullWhen(true)] out IPrototype? p { if (!_kinds.TryGetValue(kind, out var index)) { - throw new UnknownPrototypeException(id); + throw new UnknownPrototypeException(id, kind); } return index.Instances.TryGetValue(id, out prototype); @@ -648,7 +648,7 @@ public bool HasMapping(string id) { if (!_kinds.TryGetValue(typeof(T), out var index)) { - throw new UnknownPrototypeException(id); + throw new UnknownPrototypeException(id, typeof(T)); } return index.Results.ContainsKey(id); diff --git a/Robust.Shared/ResourceManagement/BaseResource.cs b/Robust.Shared/ResourceManagement/BaseResource.cs deleted file mode 100644 index e400f6cf03a..00000000000 --- a/Robust.Shared/ResourceManagement/BaseResource.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using Robust.Shared.Audio; -using Robust.Shared.IoC; -using Robust.Shared.Utility; - -namespace Robust.Shared.ResourceManagement; - -/// -/// Base resource for the cache. -/// -public abstract class BaseResource : IDisposable -{ - /// - /// Fallback resource path if this one does not exist. - /// - public virtual ResPath? Fallback => null; - - /// - /// Disposes this resource. - /// - public virtual void Dispose() - { - } - - /// - /// Deserializes the resource from the VFS. - /// - public abstract void Load(IDependencyCollection dependencies, ResPath path); - - public virtual void Reload(IDependencyCollection dependencies, ResPath path, CancellationToken ct = default) - { - - } -} diff --git a/Robust.Shared/ResourceManagement/ResourceTypes/AudioResource.cs b/Robust.Shared/ResourceManagement/ResourceTypes/AudioResource.cs deleted file mode 100644 index b04ddfd6b37..00000000000 --- a/Robust.Shared/ResourceManagement/ResourceTypes/AudioResource.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.IO; -using Robust.Shared.Audio; -using Robust.Shared.ContentPack; -using Robust.Shared.IoC; -using Robust.Shared.Utility; - -namespace Robust.Shared.ResourceManagement.ResourceTypes; - -public sealed class AudioResource : BaseResource -{ - public AudioStream AudioStream { get; private set; } = default!; - - public override void Load(IDependencyCollection dependencies, ResPath path) - { - var cache = dependencies.Resolve(); - - if (!cache.ContentFileExists(path)) - { - throw new FileNotFoundException("Content file does not exist for audio sample."); - } - - using (var fileStream = cache.ContentFileRead(path)) - { - if (path.Extension == "ogg") - { - AudioStream = dependencies.Resolve().LoadAudioOggVorbis(fileStream, path.ToString()); - } - else if (path.Extension == "wav") - { - AudioStream = dependencies.Resolve().LoadAudioWav(fileStream, path.ToString()); - } - else - { - throw new NotSupportedException("Unable to load audio files outside of ogg Vorbis or PCM wav"); - } - } - } - - public static implicit operator AudioStream(AudioResource res) - { - return res.AudioStream; - } -} diff --git a/Robust.Shared/ResourceManagement/SharedResourceCache.cs b/Robust.Shared/ResourceManagement/SharedResourceCache.cs deleted file mode 100644 index e80f9c3907c..00000000000 --- a/Robust.Shared/ResourceManagement/SharedResourceCache.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using Robust.Shared.Audio; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Log; -using Robust.Shared.Utility; - -namespace Robust.Shared.ResourceManagement; - -[Virtual] -public class SharedResourceCache : IResourceCache -{ -private readonly Dictionary> _cachedResources = - new(); - - private readonly Dictionary _fallbacks = new(); - - public T GetResource(string path, bool useFallback = true) where T : BaseResource, new() - { - return GetResource(new ResPath(path), useFallback); - } - - public T GetResource(ResPath path, bool useFallback = true) where T : BaseResource, new() - { - var cache = GetTypeDict(); - if (cache.TryGetValue(path, out var cached)) - { - return (T) cached; - } - - var resource = new T(); - try - { - var dependencies = IoCManager.Instance!; - resource.Load(dependencies, path); - cache[path] = resource; - return resource; - } - catch (Exception e) - { - if (useFallback && resource.Fallback != null) - { - Logger.Error( - $"Exception while loading resource {typeof(T)} at '{path}', resorting to fallback.\n{Environment.StackTrace}\n{e}"); - return GetResource(resource.Fallback.Value, false); - } - else - { - Logger.Error( - $"Exception while loading resource {typeof(T)} at '{path}', no fallback available\n{Environment.StackTrace}\n{e}"); - throw; - } - } - } - - public bool TryGetResource(string path, [NotNullWhen(true)] out T? resource) where T : BaseResource, new() - { - return TryGetResource(new ResPath(path), out resource); - } - - public bool TryGetResource(ResPath path, [NotNullWhen(true)] out T? resource) where T : BaseResource, new() - { - var cache = GetTypeDict(); - if (cache.TryGetValue(path, out var cached)) - { - resource = (T) cached; - return true; - } - - var _resource = new T(); - try - { - var dependencies = IoCManager.Instance!; - _resource.Load(dependencies, path); - resource = _resource; - cache[path] = resource; - return true; - } - catch - { - resource = null; - return false; - } - } - - public void ReloadResource(string path) where T : BaseResource, new() - { - ReloadResource(new ResPath(path)); - } - - public void ReloadResource(ResPath path) where T : BaseResource, new() - { - var cache = GetTypeDict(); - - if (!cache.TryGetValue(path, out var res)) - { - return; - } - - try - { - var dependencies = IoCManager.Instance!; - res.Reload(dependencies, path); - } - catch (Exception e) - { - Logger.Error($"Exception while reloading resource {typeof(T)} at '{path}'\n{e}"); - throw; - } - } - - public bool HasResource(string path) where T : BaseResource, new() - { - return HasResource(new ResPath(path)); - } - - public bool HasResource(ResPath path) where T : BaseResource, new() - { - return TryGetResource(path, out var _); - } - - public void CacheResource(string path, T resource) where T : BaseResource, new() - { - CacheResource(new ResPath(path), resource); - } - - public void CacheResource(ResPath path, T resource) where T : BaseResource, new() - { - GetTypeDict()[path] = resource; - } - - public T GetFallback() where T : BaseResource, new() - { - if (_fallbacks.TryGetValue(typeof(T), out var fallback)) - { - return (T) fallback; - } - - var res = new T(); - if (res.Fallback == null) - { - throw new InvalidOperationException($"Resource of type '{typeof(T)}' has no fallback."); - } - - fallback = GetResource(res.Fallback.Value, useFallback: false); - _fallbacks.Add(typeof(T), fallback); - return (T) fallback; - } - - public IEnumerable> GetAllResources() where T : BaseResource, new() - { - return GetTypeDict().Select(p => new KeyValuePair(p.Key, (T) p.Value)); - } - - #region IDisposable Members - - private bool disposed = false; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool disposing) - { - if (disposed) - { - return; - } - - if (disposing) - { - foreach (var res in _cachedResources.Values.SelectMany(dict => dict.Values)) - { - res.Dispose(); - } - } - - disposed = true; - } - - ~SharedResourceCache() - { - Dispose(false); - } - - #endregion IDisposable Members - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected Dictionary GetTypeDict() - { - if (!_cachedResources.TryGetValue(typeof(T), out var ret)) - { - ret = new Dictionary(); - _cachedResources.Add(typeof(T), ret); - } - - return ret; - } -} diff --git a/Robust.Shared/Robust.Shared.csproj b/Robust.Shared/Robust.Shared.csproj index e972fcbe5d6..3e3bf02e366 100644 --- a/Robust.Shared/Robust.Shared.csproj +++ b/Robust.Shared/Robust.Shared.csproj @@ -11,7 +11,6 @@ - diff --git a/Robust.Shared/Spawners/SharedTimedDespawnSystem.cs b/Robust.Shared/Spawners/SharedTimedDespawnSystem.cs index fee8ae92287..51430e406f9 100644 --- a/Robust.Shared/Spawners/SharedTimedDespawnSystem.cs +++ b/Robust.Shared/Spawners/SharedTimedDespawnSystem.cs @@ -1,4 +1,3 @@ -using Robust.Shared.Audio; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Timing; diff --git a/Robust.Shared/Utility/CollectionExtensions.cs b/Robust.Shared/Utility/CollectionExtensions.cs index 438a55f8139..7907dfc6462 100644 --- a/Robust.Shared/Utility/CollectionExtensions.cs +++ b/Robust.Shared/Utility/CollectionExtensions.cs @@ -222,6 +222,17 @@ public static TValue GetOrNew(this Dictionary dict, return entry!; } + public static TValue GetOrNew(this Dictionary dict, TKey key, out bool exists) + where TValue : new() + where TKey : notnull + { + ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out exists); + if (!exists) + entry = new TValue(); + + return entry!; + } + // More efficient than LINQ. public static KeyValuePair[] ToArray(this Dictionary dict) where TKey : notnull diff --git a/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs b/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs index eb6ec27d7db..bb91e6128e8 100644 --- a/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs +++ b/Robust.UnitTesting/Server/GameObjects/Components/Transform_Test.cs @@ -21,6 +21,7 @@ sealed class Transform_Test : RobustUnitTest private IServerEntityManagerInternal EntityManager = default!; private IMapManager MapManager = default!; + private SharedTransformSystem XformSystem => EntityManager.System(); const string Prototypes = @" - type: entity @@ -35,9 +36,9 @@ sealed class Transform_Test : RobustUnitTest "; private MapId MapA; - private Entity GridA = default!; + private Entity GridA; private MapId MapB; - private Entity GridB = default!; + private Entity GridB; private static readonly EntityCoordinates InitialPos = new(EntityUid.FirstUid, new Vector2(0, 0)); @@ -84,39 +85,38 @@ public void ParentMapSwitchTest() var childTrans = EntityManager.GetComponent(child); // that are not on the same map - parentTrans.Coordinates = new EntityCoordinates(GridA, new Vector2(5, 5)); - childTrans.Coordinates = new EntityCoordinates(GridB, new Vector2(4, 4)); + XformSystem.SetCoordinates(parent, parentTrans, new EntityCoordinates(GridA, new Vector2(5, 5))); + XformSystem.SetCoordinates(child, childTrans, new EntityCoordinates(GridB, new Vector2(4, 4))); // if they are parented, the child keeps its world position, but moves to the parents map - childTrans.AttachParent(parentTrans); - + XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans); Assert.Multiple(() => { - Assert.That(childTrans.MapID, Is.EqualTo(parentTrans.MapID)); - Assert.That(childTrans.GridUid, Is.EqualTo(parentTrans.GridUid)); - Assert.That(childTrans.Coordinates, Is.EqualTo(new EntityCoordinates(parent, new Vector2(-1, -1)))); - Assert.That(childTrans.WorldPosition, Is.EqualTo(new Vector2(4, 4))); + Assert.That(childTrans.MapID, NUnit.Framework.Is.EqualTo(parentTrans.MapID)); + Assert.That(childTrans.GridUid, NUnit.Framework.Is.EqualTo(parentTrans.GridUid)); + Assert.That(childTrans.Coordinates, NUnit.Framework.Is.EqualTo(new EntityCoordinates(parent, new Vector2(-1, -1)))); + Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(4, 4))); }); // move the parent, and the child should move with it - childTrans.LocalPosition = new Vector2(6, 6); - parentTrans.WorldPosition = new Vector2(-8, -8); + XformSystem.SetLocalPosition(child, new Vector2(6, 6), childTrans); + XformSystem.SetWorldPosition(parentTrans, new Vector2(-8, -8)); - Assert.That(childTrans.WorldPosition, Is.EqualTo(new Vector2(-2, -2))); + Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(-2, -2))); // if we detach parent, the child should be left where it was, still relative to parents grid var oldLpos = new Vector2(-2, -2); - var oldWpos = childTrans.WorldPosition; + var oldWpos = XformSystem.GetWorldPosition(childTrans); - childTrans.AttachToGridOrMap(); + XformSystem.AttachToGridOrMap(child, childTrans); // the gridId won't match, because we just detached from the grid entity Assert.Multiple(() => { - Assert.That(childTrans.Coordinates.Position, Is.EqualTo(oldLpos)); - Assert.That(childTrans.WorldPosition, Is.EqualTo(oldWpos)); + Assert.That(childTrans.Coordinates.Position, NUnit.Framework.Is.EqualTo(oldLpos)); + Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(oldWpos)); }); } @@ -131,16 +131,16 @@ public void ParentAttachMoveTest() var child = EntityManager.SpawnEntity(null, InitialPos); var parentTrans = EntityManager.GetComponent(parent); var childTrans = EntityManager.GetComponent(child); - parentTrans.WorldPosition = new Vector2(5, 5); - childTrans.WorldPosition = new Vector2(6, 6); + XformSystem.SetWorldPosition(parentTrans, new Vector2(5, 5)); + XformSystem.SetWorldPosition(childTrans, new Vector2(6, 6)); // Act - var oldWpos = childTrans.WorldPosition; - childTrans.AttachParent(parentTrans); - var newWpos = childTrans.WorldPosition; + var oldWpos = XformSystem.GetWorldPosition(childTrans); + XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans); + var newWpos = XformSystem.GetWorldPosition(childTrans); // Assert - Assert.That(oldWpos == newWpos); + Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos)); } /// @@ -156,25 +156,25 @@ public void ParentDoubleAttachMoveTest() var parentTrans = EntityManager.GetComponent(parent); var childOneTrans = EntityManager.GetComponent(childOne); var childTwoTrans = EntityManager.GetComponent(childTwo); - parentTrans.WorldPosition = new Vector2(1, 1); - childOneTrans.WorldPosition = new Vector2(2, 2); - childTwoTrans.WorldPosition = new Vector2(3, 3); + XformSystem.SetWorldPosition(parentTrans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(childOneTrans, new Vector2(2, 2)); + XformSystem.SetWorldPosition(childTwoTrans, new Vector2(3, 3)); // Act - var oldWpos = childOneTrans.WorldPosition; - childOneTrans.AttachParent(parentTrans); - var newWpos = childOneTrans.WorldPosition; - Assert.That(oldWpos, Is.EqualTo(newWpos)); - - oldWpos = childTwoTrans.WorldPosition; - childTwoTrans.AttachParent(parentTrans); - newWpos = childTwoTrans.WorldPosition; - Assert.That(oldWpos, Is.EqualTo(newWpos)); - - oldWpos = childTwoTrans.WorldPosition; - childTwoTrans.AttachParent(childOneTrans); - newWpos = childTwoTrans.WorldPosition; - Assert.That(oldWpos, Is.EqualTo(newWpos)); + var oldWpos = XformSystem.GetWorldPosition(childOneTrans); + XformSystem.SetParent(childOne, childOneTrans, parent, parentXform: parentTrans); + var newWpos = XformSystem.GetWorldPosition(childOneTrans); + Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos)); + + oldWpos = XformSystem.GetWorldPosition(childTwoTrans); + XformSystem.SetParent(childOne, childOneTrans, parent, parentXform: parentTrans); + newWpos = XformSystem.GetWorldPosition(childTwoTrans); + Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos)); + + oldWpos = XformSystem.GetWorldPosition(childTwoTrans); + XformSystem.SetParent(childTwo, childTwoTrans, childOne, parentXform: childOneTrans); + newWpos = XformSystem.GetWorldPosition(childTwoTrans); + Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos)); } /// @@ -188,15 +188,15 @@ public void ParentRotateTest() var child = EntityManager.SpawnEntity(null, InitialPos); var parentTrans = EntityManager.GetComponent(parent); var childTrans = EntityManager.GetComponent(child); - parentTrans.WorldPosition = new Vector2(0, 0); - childTrans.WorldPosition = new Vector2(2, 0); - childTrans.AttachParent(parentTrans); + XformSystem.SetWorldPosition(parentTrans, new Vector2(0, 0)); + XformSystem.SetWorldPosition(childTrans, new Vector2(2, 0)); + XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans); //Act parentTrans.LocalRotation = new Angle(MathHelper.Pi / 2); //Assert - var result = childTrans.WorldPosition; + var result = XformSystem.GetWorldPosition(childTrans); Assert.Multiple(() => { Assert.That(MathHelper.CloseToPercent(result.X, 0)); @@ -215,15 +215,15 @@ public void ParentTransRotateTest() var child = EntityManager.SpawnEntity(null, InitialPos); var parentTrans = EntityManager.GetComponent(parent); var childTrans = EntityManager.GetComponent(child); - parentTrans.WorldPosition = new Vector2(1, 1); - childTrans.WorldPosition = new Vector2(2, 1); - childTrans.AttachParent(parentTrans); + XformSystem.SetWorldPosition(parentTrans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(childTrans, new Vector2(2, 1)); + XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans); //Act parentTrans.LocalRotation = new Angle(MathHelper.Pi / 2); //Assert - var result = childTrans.WorldPosition; + var result = XformSystem.GetWorldPosition(childTrans); Assert.Multiple(() => { Assert.That(MathHelper.CloseToPercent(result.X, 1)); @@ -248,20 +248,20 @@ public void PositionCompositionTest() var node3Trans = EntityManager.GetComponent(node3); var node4Trans = EntityManager.GetComponent(node4); - node1Trans.WorldPosition = new Vector2(0, 0); - node2Trans.WorldPosition = new Vector2(1, 1); - node3Trans.WorldPosition = new Vector2(2, 2); - node4Trans.WorldPosition = new Vector2(0, 2); + XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); + XformSystem.SetWorldPosition(node4Trans, new Vector2(0, 2)); - node2Trans.AttachParent(node1Trans); - node3Trans.AttachParent(node2Trans); - node4Trans.AttachParent(node3Trans); + XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); + XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); + XformSystem.SetParent(node4, node4Trans, node3, parentXform: node3Trans); //Act node1Trans.LocalRotation = new Angle(MathHelper.Pi / 2); //Assert - var result = node4Trans.WorldPosition; + var result = XformSystem.GetWorldPosition(node4Trans); Assert.Multiple(() => { @@ -285,25 +285,25 @@ public void ParentLocalPositionRoundingErrorTest() var node2Trans = EntityManager.GetComponent(node2); var node3Trans = EntityManager.GetComponent(node3); - node1Trans.WorldPosition = new Vector2(0, 0); - node2Trans.WorldPosition = new Vector2(1, 1); - node3Trans.WorldPosition = new Vector2(2, 2); + XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); - node2Trans.AttachParent(node1Trans); - node3Trans.AttachParent(node2Trans); + XformSystem.SetParent(node1, node1Trans, node2, parentXform: node2Trans); + XformSystem.SetParent(node2, node2Trans, node3, parentXform: node3Trans); // Act - var oldWpos = node3Trans.WorldPosition; + var oldWpos = XformSystem.GetWorldPosition(node3Trans); for (var i = 0; i < 10000; i++) { var dx = i % 2 == 0 ? 5 : -5; - node1Trans.LocalPosition += new Vector2(dx, dx); - node2Trans.LocalPosition += new Vector2(dx, dx); - node3Trans.LocalPosition += new Vector2(dx, dx); + XformSystem.SetLocalPosition(node1, node1Trans.LocalPosition + new Vector2(dx, dx), node1Trans); + XformSystem.SetLocalPosition(node2, node2Trans.LocalPosition + new Vector2(dx, dx), node2Trans); + XformSystem.SetLocalPosition(node3, node3Trans.LocalPosition + new Vector2(dx, dx), node3Trans); } - var newWpos = node3Trans.WorldPosition; + var newWpos = XformSystem.GetWorldPosition(node3Trans); // Assert Assert.Multiple(() => @@ -330,15 +330,15 @@ public void ParentRotationRoundingErrorTest() var node2Trans = EntityManager.GetComponent(node2); var node3Trans = EntityManager.GetComponent(node3); - node1Trans.WorldPosition = new Vector2(0, 0); - node2Trans.WorldPosition = new Vector2(1, 1); - node3Trans.WorldPosition = new Vector2(2, 2); + XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); - node2Trans.AttachParent(node1Trans); - node3Trans.AttachParent(node2Trans); + XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); + XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); // Act - var oldWpos = node3Trans.WorldPosition; + var oldWpos = XformSystem.GetWorldPosition(node3Trans); for (var i = 0; i < 100; i++) { @@ -347,7 +347,7 @@ public void ParentRotationRoundingErrorTest() node3Trans.LocalRotation += new Angle(MathHelper.Pi); } - var newWpos = node3Trans.WorldPosition; + var newWpos = XformSystem.GetWorldPosition(node3Trans); //NOTE: Yes, this does cause a non-zero error @@ -355,8 +355,8 @@ public void ParentRotationRoundingErrorTest() Assert.Multiple(() => { - Assert.That(MathHelper.CloseToPercent(oldWpos.X, newWpos.Y)); - Assert.That(MathHelper.CloseToPercent(oldWpos.Y, newWpos.Y)); + Assert.That(MathHelper.CloseToPercent(oldWpos.X, newWpos.Y, 0.0001f)); + Assert.That(MathHelper.CloseToPercent(oldWpos.Y, newWpos.Y, 0.0001f)); }); } @@ -379,21 +379,21 @@ public void TreeComposeWorldMatricesTest() var node3Trans = EntityManager.GetComponent(node3); var node4Trans = EntityManager.GetComponent(node4); - node1Trans.WorldPosition = new Vector2(0, 0); - node2Trans.WorldPosition = new Vector2(1, 1); - node3Trans.WorldPosition = new Vector2(2, 2); - node4Trans.WorldPosition = new Vector2(0, 2); + XformSystem.SetWorldPosition(node1Trans, new Vector2(0, 0)); + XformSystem.SetWorldPosition(node2Trans, new Vector2(1, 1)); + XformSystem.SetWorldPosition(node3Trans, new Vector2(2, 2)); + XformSystem.SetWorldPosition(node4Trans, new Vector2(0, 2)); - node2Trans.AttachParent(node1Trans); - node3Trans.AttachParent(node2Trans); - node4Trans.AttachParent(node3Trans); + XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); + XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); + XformSystem.SetParent(node4, node4Trans, node3, parentXform: node3Trans); //Act node1Trans.LocalRotation = new Angle(MathHelper.Pi / 6.37); - node1Trans.WorldPosition = new Vector2(1, 1); + XformSystem.SetWorldPosition(node1Trans, new Vector2(1, 1)); - var worldMat = node4Trans.WorldMatrix; - var invWorldMat = node4Trans.InvWorldMatrix; + var worldMat = XformSystem.GetWorldMatrix(node4Trans); + var invWorldMat = XformSystem.GetInvWorldMatrix(node4Trans); Matrix3.Multiply(in worldMat, in invWorldMat, out var leftVerifyMatrix); Matrix3.Multiply(in invWorldMat, in worldMat, out var rightVerifyMatrix); @@ -425,8 +425,8 @@ public void WorldRotationTest() var node2Trans = EntityManager.GetComponent(node2); var node3Trans = EntityManager.GetComponent(node3); - node2Trans.AttachParent(node1Trans); - node3Trans.AttachParent(node2Trans); + XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); + XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); node1Trans.LocalRotation = Angle.FromDegrees(0); node2Trans.LocalRotation = Angle.FromDegrees(45); @@ -436,7 +436,7 @@ public void WorldRotationTest() node1Trans.LocalRotation = Angle.FromDegrees(135); // Assert (135 + 45 + 45 = 225) - var result = node3Trans.WorldRotation; + var result = XformSystem.GetWorldRotation(node3Trans); Assert.That(result, new ApproxEqualityConstraint(Angle.FromDegrees(225))); } @@ -454,14 +454,14 @@ public void MatrixUpdateTest() var node2Trans = EntityManager.GetComponent(node2); var node3Trans = EntityManager.GetComponent(node3); - node2Trans.AttachParent(node1Trans); - node3Trans.AttachParent(node2Trans); + XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans); + XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans); - node3Trans.LocalPosition = new Vector2(5, 5); - node2Trans.LocalPosition = new Vector2(5, 5); - node1Trans.LocalPosition = new Vector2(5, 5); + XformSystem.SetLocalPosition(node3, new Vector2(5, 5), node3Trans); + XformSystem.SetLocalPosition(node2, new Vector2(5, 5), node2Trans); + XformSystem.SetLocalPosition(node1, new Vector2(5, 5), node1Trans); - Assert.That(node3Trans.WorldPosition, new ApproxEqualityConstraint(new Vector2(15, 15))); + Assert.That(XformSystem.GetWorldPosition(node3Trans), new ApproxEqualityConstraint(new Vector2(15, 15))); } /* diff --git a/Robust.UnitTesting/Server/GameStates/DefaultEntityTest.cs b/Robust.UnitTesting/Server/GameStates/DefaultEntityTest.cs index bfded2e559c..5c4186abd83 100644 --- a/Robust.UnitTesting/Server/GameStates/DefaultEntityTest.cs +++ b/Robust.UnitTesting/Server/GameStates/DefaultEntityTest.cs @@ -1,7 +1,6 @@ using System.Linq; using System.Threading.Tasks; using NUnit.Framework; -using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared; using Robust.Shared.Configuration; @@ -81,7 +80,7 @@ await server.WaitPost(() => coords = new(map, default); var playerUid = sEntMan.SpawnEntity(null, coords); player = sEntMan.GetNetEntity(playerUid); - sEntMan.System().Attach(playerUid, session); + server.PlayerMan.SetAttachedEntity(session, playerUid); }); for (int i = 0; i < 10; i++) diff --git a/Robust.UnitTesting/Server/GameStates/PvsReEntryTest.cs b/Robust.UnitTesting/Server/GameStates/PvsReEntryTest.cs index 8a4abaf809c..c5c3534f461 100644 --- a/Robust.UnitTesting/Server/GameStates/PvsReEntryTest.cs +++ b/Robust.UnitTesting/Server/GameStates/PvsReEntryTest.cs @@ -4,15 +4,13 @@ using NUnit.Framework; using Robust.Client.GameStates; using Robust.Client.Timing; -using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Network; +using Robust.Shared.Player; using Robust.Shared.Timing; -using cIPlayerManager = Robust.Client.Player.IPlayerManager; -using sIPlayerManager = Robust.Server.Player.IPlayerManager; namespace Robust.UnitTesting.Server.GameStates; @@ -33,13 +31,13 @@ public async Task TestLossyReEntry() var mapMan = server.ResolveDependency(); var sEntMan = server.ResolveDependency(); var confMan = server.ResolveDependency(); - var sPlayerMan = server.ResolveDependency(); + var sPlayerMan = server.ResolveDependency(); var xforms = sEntMan.System(); var stateMan = (ClientGameStateManager) client.ResolveDependency(); var cEntMan = client.ResolveDependency(); var netMan = client.ResolveDependency(); - var cPlayerMan = client.ResolveDependency(); + var cPlayerMan = client.ResolveDependency(); Assert.DoesNotThrow(() => client.SetConnectTarget(server)); client.Post(() => netMan.ClientConnect(null!, 0, null!)); @@ -87,7 +85,7 @@ await server.WaitPost(() => // Attach player. var session = sPlayerMan.Sessions.First(); - sEntMan.System().Attach(playerUid, session); + server.PlayerMan.SetAttachedEntity(session, playerUid); sPlayerMan.JoinGame(session); }); @@ -106,7 +104,7 @@ await client.WaitPost(() => { Assert.That(cEntMan.TryGetEntityData(entity, out _, out meta)); Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid)); - Assert.That(cPlayerMan.LocalPlayer?.ControlledEntity, Is.EqualTo(cPlayerUid)); + Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid)); Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None)); Assert.That(stateMan.IsQueuedForDetach(entity), Is.False); }); diff --git a/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs b/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs index 9c14b159707..84fa99d82a2 100644 --- a/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs +++ b/Robust.UnitTesting/Server/GameStates/PvsSystemTests.cs @@ -2,15 +2,13 @@ using System.Numerics; using System.Threading.Tasks; using NUnit.Framework; -using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Network; -using cIPlayerManager = Robust.Client.Player.IPlayerManager; -using sIPlayerManager = Robust.Server.Player.IPlayerManager; +using Robust.Shared.Player; namespace Robust.UnitTesting.Server.GameStates; @@ -30,12 +28,12 @@ public async Task TestMultipleIndexChange() var mapMan = server.ResolveDependency(); var sEntMan = server.ResolveDependency(); var confMan = server.ResolveDependency(); - var sPlayerMan = server.ResolveDependency(); + var sPlayerMan = server.ResolveDependency(); var xforms = sEntMan.System(); var cEntMan = client.ResolveDependency(); var netMan = client.ResolveDependency(); - var cPlayerMan = client.ResolveDependency(); + var cPlayerMan = client.ResolveDependency(); Assert.DoesNotThrow(() => client.SetConnectTarget(server)); client.Post(() => netMan.ClientConnect(null!, 0, null!)); @@ -76,7 +74,7 @@ await server.WaitPost(() => // Attach player. var session = sPlayerMan.Sessions.First(); - sEntMan.System().Attach(player, session); + server.PlayerMan.SetAttachedEntity(session, player); sPlayerMan.JoinGame(session); }); @@ -89,7 +87,7 @@ await server.WaitPost(() => // Check player got properly attached await client.WaitPost(() => { - var ent = cEntMan.GetNetEntity(cPlayerMan.LocalPlayer?.ControlledEntity); + var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity); Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player))); }); diff --git a/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs b/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs index 19c2136e1dd..19ed261e8fd 100644 --- a/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs +++ b/Robust.UnitTesting/Shared/GameObjects/ContainerTests.cs @@ -72,7 +72,7 @@ await server.WaitAssertion(() => // Setup PVS sEntManager.AddComponent(entityUid); var player = sPlayerManager.Sessions.First(); - sEntManager.System().Attach(entityUid, player); + server.PlayerMan.SetAttachedEntity(player, entityUid); sPlayerManager.JoinGame(player); }); @@ -200,7 +200,7 @@ await server.WaitAssertion(() => // Setup PVS sEntManager.AddComponent(sEntityUid); var player = sPlayerManager.Sessions.First(); - sEntManager.System().Attach(sEntityUid, player); + server.PlayerMan.SetAttachedEntity(player, sEntityUid); sPlayerManager.JoinGame(player); }); diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs index b5c7e713a35..dfe3ac750fb 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs @@ -43,7 +43,9 @@ public void SubscribeCompEvent() // add a component to the system bus.OnEntityAdded(entUid); - bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), CompIdx.Index())); + + var reg = compFactory.GetRegistration(CompIdx.Index()); + bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg)); // Raise var evntArgs = new TestEvent(5); @@ -98,7 +100,9 @@ public void UnsubscribeCompEvent() // add a component to the system bus.OnEntityAdded(entUid); - bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), CompIdx.Index())); + + var reg = compFacMock.Object.GetRegistration(CompIdx.Index()); + bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg)); // Raise var evntArgs = new TestEvent(5); @@ -151,7 +155,9 @@ public void SubscribeCompLifeEvent() // add a component to the system entManMock.Raise(m => m.EntityAdded += null, entUid); - entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), CompIdx.Index())); + + var reg = compFacMock.Object.GetRegistration(); + entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg)); // Raise ((IEventBus)bus).RaiseComponentEvent(compInstance, new ComponentInit()); @@ -228,9 +234,14 @@ void HandlerB(EntityUid uid, Component comp, TestEvent ev) // add a component to the system bus.OnEntityAdded(entUid); - bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), CompIdx.Index())); - bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), CompIdx.Index())); - bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid), CompIdx.Index())); + + var regA = compFacMock.Object.GetRegistration(CompIdx.Index()); + var regB = compFacMock.Object.GetRegistration(CompIdx.Index()); + var regC = compFacMock.Object.GetRegistration(CompIdx.Index()); + + bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA)); + bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB)); + bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid), regC)); // Raise var evntArgs = new TestEvent(5); diff --git a/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs b/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs index 0aa8531ba51..a11408109f5 100644 --- a/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs +++ b/Robust.UnitTesting/Shared/GameState/ComponentStateTests.cs @@ -2,14 +2,12 @@ using System.Numerics; using System.Threading.Tasks; using NUnit.Framework; -using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Network; -using Robust.Shared.Serialization.Manager.Attributes; namespace Robust.UnitTesting.Shared.GameState; @@ -74,7 +72,7 @@ await server.WaitPost(() => // Attach player. player = server.EntMan.Spawn(); var session = server.PlayerMan.Sessions.First(); - server.System().Attach(player, session); + server.PlayerMan.SetAttachedEntity(session, player); server.PlayerMan.JoinGame(session); // Spawn test entities. @@ -210,7 +208,7 @@ await server.WaitPost(() => // Attach player. player = server.EntMan.Spawn(); var session = server.PlayerMan.Sessions.First(); - server.System().Attach(player, session); + server.PlayerMan.SetAttachedEntity(session, player); server.PlayerMan.JoinGame(session); // Spawn test entities. @@ -308,6 +306,6 @@ async Task RunTicks() [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class UnknownEntityTestComponent : Component { - [DataField, AutoNetworkedField] + [AutoNetworkedField] public EntityUid? Other; } diff --git a/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs b/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs index 210edf1861b..d73656ea04e 100644 --- a/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs +++ b/Robust.UnitTesting/Shared/GameState/DeletionNetworkingTests.cs @@ -2,15 +2,13 @@ using System.Numerics; using System.Threading.Tasks; using NUnit.Framework; -using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Network; -using cIPlayerManager = Robust.Client.Player.IPlayerManager; -using sIPlayerManager = Robust.Server.Player.IPlayerManager; +using Robust.Shared.Player; // ReSharper disable AccessToStaticMemberViaDerivedType @@ -37,8 +35,8 @@ public async Task DeletionNetworkingTest() var cEntMan = client.ResolveDependency(); var netMan = client.ResolveDependency(); var confMan = server.ResolveDependency(); - var cPlayerMan = client.ResolveDependency(); - var sPlayerMan = server.ResolveDependency(); + var cPlayerMan = client.ResolveDependency(); + var sPlayerMan = server.ResolveDependency(); var xformSys = sEntMan.EntitySysManager.GetEntitySystem(); Assert.DoesNotThrow(() => client.SetConnectTarget(server)); @@ -85,7 +83,7 @@ await server.WaitPost(() => var coords = new EntityCoordinates(grid1, new Vector2(0.5f, 0.5f)); player = sEntMan.SpawnEntity(null, coords); var session = sPlayerMan.Sessions.First(); - sEntMan.System().Attach(player, session); + server.PlayerMan.SetAttachedEntity(session, player); sPlayerMan.JoinGame(session); }); @@ -94,7 +92,7 @@ await server.WaitPost(() => // Check player got properly attached await client.WaitPost(() => { - var ent = cEntMan.GetNetEntity(cPlayerMan.LocalPlayer?.ControlledEntity); + var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity); Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player))); }); diff --git a/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs b/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs index 16046605e1e..e3a33cc6377 100644 --- a/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs +++ b/Robust.UnitTesting/Shared/Physics/BroadphaseNetworkingTest.cs @@ -2,7 +2,6 @@ using System.Numerics; using System.Threading.Tasks; using NUnit.Framework; -using Robust.Server.GameObjects; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; @@ -14,8 +13,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Systems; -using cIPlayerManager = Robust.Client.Player.IPlayerManager; -using sIPlayerManager = Robust.Server.Player.IPlayerManager; +using Robust.Shared.Player; // ReSharper disable AccessToStaticMemberViaDerivedType @@ -42,8 +40,8 @@ public async Task TestBroadphaseNetworking() var cEntMan = client.ResolveDependency(); var netMan = client.ResolveDependency(); var confMan = server.ResolveDependency(); - var cPlayerMan = client.ResolveDependency(); - var sPlayerMan = server.ResolveDependency(); + var cPlayerMan = client.ResolveDependency(); + var sPlayerMan = server.ResolveDependency(); var fixturesSystem = sEntMan.EntitySysManager.GetEntitySystem(); var physicsSystem = sEntMan.EntitySysManager.GetEntitySystem(); @@ -91,7 +89,7 @@ await server.WaitPost(() => // Attach player. var session = sPlayerMan.Sessions.First(); - sEntMan.System().Attach(player, session); + server.PlayerMan.SetAttachedEntity(session, player); sPlayerMan.JoinGame(session); }); @@ -106,7 +104,7 @@ await server.WaitPost(() => // Check player got properly attached await client.WaitPost(() => { - var ent = cEntMan.GetNetEntity(cPlayerMan.LocalPlayer?.ControlledEntity); + var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity); Assert.That(ent, Is.EqualTo(playerNet)); });