mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a604e4a04 | ||
|
|
6218ef6e3f | ||
|
|
76b46479b6 | ||
|
|
de9a8d286a | ||
|
|
a1df0fb4af | ||
|
|
e6bc5a1057 | ||
|
|
11b24579a2 | ||
|
|
685d002bb7 | ||
|
|
2e0d18aeaf | ||
|
|
06dbff0429 | ||
|
|
15958a9447 | ||
|
|
fd5a4d9b8a | ||
|
|
6d958847cb | ||
|
|
8a04a4f3a5 | ||
|
|
7104a4f459 | ||
|
|
f29949a32c | ||
|
|
3bbbabf238 | ||
|
|
d95aca3d9e | ||
|
|
e14537074e | ||
|
|
af2d01981f | ||
|
|
7df23e047c | ||
|
|
5c7ab43049 |
@@ -57,7 +57,7 @@
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.1" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,48 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 247.2.1
|
||||
|
||||
|
||||
## 247.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added functions for copying components to `IEntityManager` and `EntitySystem`.
|
||||
* Sound played from sound collections is now sent as "collection ID + index" over the network instead of the final filename.
|
||||
* This enables integration of future accessibility systems.
|
||||
* Added a new `ResolvedSoundSpecifier` to represent played sounds. Methods that previously took a filename now take a `ResolvedSoundSpecifier`, with an implicit cast from string being interpreted as a raw filename.
|
||||
* `VisibilitySystem` has been made accessible to shared as `SharedVisibilitySystem`.
|
||||
* `ScrollContainer` now has properties exposing `Value` and `ValueTarget` on its internal scroll bars.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix prototype hot reload crashing when adding a new component already exists on an entity.
|
||||
* Fix maps failing to save in some cases related to tilemap IDs.
|
||||
* Fix `Regex.Escape(string)` not being available in sandbox.
|
||||
* Prototypes that parent themselves directly won't cause the game to hang on an infinite loop anymore.
|
||||
* Fixed disconnecting during a connection attempt leaving the client stuck in a phantom state.
|
||||
|
||||
### Internal
|
||||
|
||||
* More warning cleanup.
|
||||
|
||||
## 247.1.0
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for `Color[]` shader uniforms
|
||||
* Added optional minimumDistance parameter to `SharedJointSystem.CreateDistanceJoint()`
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fixed `EntitySystem.DirtyFields()` not actually marking fields as dirty.
|
||||
|
||||
### Other
|
||||
|
||||
* Updated the Yamale map file format validator to support v7 map/grid files.
|
||||
|
||||
|
||||
## 247.0.0
|
||||
|
||||
### Breaking changes
|
||||
@@ -64,7 +106,7 @@ END TEMPLATE-->
|
||||
when warnings are logged.
|
||||
* The minimum supported map format / version has been increased from 2 to 3.
|
||||
* The server-side `MapLoaderSystem` and associated classes & structs has been moved to `Robust.Shared`, and has been significantly modified.
|
||||
* The`TryLoad` and `Save` methods have been replaced with grid, map, generic entity variants. I.e, `SaveGrid`, `SaveMap`, and `SaveEntities`.
|
||||
* The `TryLoad` and `Save` methods have been replaced with grid, map, generic entity variants. I.e, `SaveGrid`, `SaveMap`, and `SaveEntities`.
|
||||
* Most of the serialization logic and methods have been moved out of `MapLoaderSystem` and into new `EntitySerializer`
|
||||
and `EntityDeserializer` classes, which also replace the old `MapSerializationContext`.
|
||||
* The `MapLoadOptions` class has been split into `MapLoadOptions`, `SerializationOptions`, and `DeserializationOptions`
|
||||
|
||||
@@ -6,7 +6,7 @@ using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
public static class Program
|
||||
internal static class Program
|
||||
{
|
||||
// This was supposed to be the main entry for the subprocess program... It doesn't work.
|
||||
public static int Main(string[] args)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameControllerInternal _gameController = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
@@ -61,7 +63,10 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
var cachePath = "";
|
||||
if (_resourceManager.UserData is WritableDirProvider userData)
|
||||
cachePath = userData.GetFullPath(new ResPath("/cef_cache"));
|
||||
{
|
||||
var rootDir = UserDataDir.GetRootUserDataDir(_gameController);
|
||||
cachePath = Path.Combine(rootDir, "cef_cache", "0");
|
||||
}
|
||||
|
||||
var settings = new CefSettings()
|
||||
{
|
||||
|
||||
@@ -40,11 +40,7 @@ namespace Robust.Client.Animations
|
||||
var keyFrame = KeyFrames[keyFrameIndex];
|
||||
|
||||
var audioParams = keyFrame.AudioParamsFunc.Invoke();
|
||||
var audio = new SoundPathSpecifier(keyFrame.Resource)
|
||||
{
|
||||
Params = audioParams
|
||||
};
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().PlayEntity(audio, Filter.Local(), entity, true);
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>().PlayEntity(keyFrame.Specifier, Filter.Local(), entity, true, audioParams);
|
||||
}
|
||||
|
||||
return (keyFrameIndex, playingTime);
|
||||
@@ -55,7 +51,7 @@ namespace Robust.Client.Animations
|
||||
/// <summary>
|
||||
/// The RSI state to play when this keyframe gets triggered.
|
||||
/// </summary>
|
||||
public readonly string Resource;
|
||||
public readonly ResolvedSoundSpecifier Specifier;
|
||||
|
||||
/// <summary>
|
||||
/// A function that returns the audio parameter to be used.
|
||||
@@ -69,9 +65,9 @@ namespace Robust.Client.Animations
|
||||
/// </summary>
|
||||
public readonly float KeyTime;
|
||||
|
||||
public KeyFrame(string resource, float keyTime, Func<AudioParams>? audioParams = null)
|
||||
public KeyFrame(ResolvedSoundSpecifier specifier, float keyTime, Func<AudioParams>? audioParams = null)
|
||||
{
|
||||
Resource = resource;
|
||||
Specifier = specifier;
|
||||
KeyTime = keyTime;
|
||||
AudioParamsFunc = audioParams ?? (() => AudioParams.Default);
|
||||
}
|
||||
|
||||
@@ -415,6 +415,16 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return occlusion;
|
||||
}
|
||||
|
||||
private bool TryGetAudio(ResolvedSoundSpecifier specifier, [NotNullWhen(true)] out AudioResource? audio)
|
||||
{
|
||||
var filename = GetAudioPath(specifier);
|
||||
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 TryGetAudio(string filename, [NotNullWhen(true)] out AudioResource? audio)
|
||||
{
|
||||
if (_resourceCache.TryGetResource(new ResPath(filename), out audio))
|
||||
@@ -433,15 +443,15 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityCoordinates coordinates,
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityCoordinates coordinates,
|
||||
AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayStatic(filename, Filter.Local(), coordinates, true, audioParams);
|
||||
return PlayStatic(specifier, Filter.Local(), coordinates, true, audioParams);
|
||||
}
|
||||
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityUid uid, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityUid uid, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayEntity(filename, Filter.Local(), uid, true, audioParams);
|
||||
return PlayEntity(specifier, Filter.Local(), uid, true, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -477,21 +487,21 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
private (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, AudioParams? audioParams = null, bool recordReplay = true)
|
||||
private (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, AudioParams? audioParams = null, bool recordReplay = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (recordReplay && _replayRecording.IsRecording)
|
||||
{
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioGlobalMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Specifier = specifier,
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
|
||||
return TryGetAudio(filename, out var audio) ? PlayGlobal(audio, audioParams) : default;
|
||||
return TryGetAudio(specifier, out var audio) ? PlayGlobal(audio, specifier, audioParams) : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -499,9 +509,9 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// </summary>
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public (EntityUid Entity, AudioComponent Component)? PlayGlobal(AudioStream stream, AudioParams? audioParams = null)
|
||||
public (EntityUid Entity, AudioComponent Component)? PlayGlobal(AudioStream stream, ResolvedSoundSpecifier? specifier, AudioParams? audioParams = null)
|
||||
{
|
||||
var (entity, component) = CreateAndStartPlayingStream(audioParams, stream);
|
||||
var (entity, component) = CreateAndStartPlayingStream(audioParams, specifier, stream);
|
||||
component.Global = true;
|
||||
component.Source.Global = true;
|
||||
DirtyField(entity, component, nameof(AudioComponent.Global));
|
||||
@@ -513,22 +523,22 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
private (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true)
|
||||
private (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, EntityUid entity, AudioParams? audioParams = null, bool recordReplay = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (recordReplay && _replayRecording.IsRecording)
|
||||
{
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioEntityMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Specifier = specifier,
|
||||
NetEntity = GetNetEntity(entity),
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
|
||||
return TryGetAudio(filename, out var audio) ? PlayEntity(audio, entity, audioParams) : default;
|
||||
return TryGetAudio(specifier, out var audio) ? PlayEntity(audio, entity, specifier, audioParams) : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -537,7 +547,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="entity">The entity "emitting" the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public (EntityUid Entity, AudioComponent Component)? PlayEntity(AudioStream stream, EntityUid entity, AudioParams? audioParams = null)
|
||||
public (EntityUid Entity, AudioComponent Component)? PlayEntity(AudioStream stream, EntityUid entity, ResolvedSoundSpecifier? specifier, AudioParams? audioParams = null)
|
||||
{
|
||||
if (TerminatingOrDeleted(entity))
|
||||
{
|
||||
@@ -545,7 +555,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
}
|
||||
|
||||
var playing = CreateAndStartPlayingStream(audioParams, stream);
|
||||
var playing = CreateAndStartPlayingStream(audioParams, specifier, stream);
|
||||
_xformSys.SetCoordinates(playing.Entity, new EntityCoordinates(entity, Vector2.Zero));
|
||||
|
||||
return playing;
|
||||
@@ -557,22 +567,22 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
private (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true)
|
||||
private (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, EntityCoordinates coordinates, AudioParams? audioParams = null, bool recordReplay = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (recordReplay && _replayRecording.IsRecording)
|
||||
{
|
||||
_replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage
|
||||
{
|
||||
FileName = filename,
|
||||
Specifier = specifier,
|
||||
Coordinates = GetNetCoordinates(coordinates),
|
||||
AudioParams = audioParams ?? AudioParams.Default
|
||||
});
|
||||
}
|
||||
|
||||
return TryGetAudio(filename, out var audio) ? PlayStatic(audio, coordinates, audioParams) : default;
|
||||
return TryGetAudio(specifier, out var audio) ? PlayStatic(audio, coordinates, specifier, audioParams) : default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -581,7 +591,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
/// <param name="stream">The audio stream to play.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
/// <param name="audioParams"></param>
|
||||
public (EntityUid Entity, AudioComponent Component)? PlayStatic(AudioStream stream, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
public (EntityUid Entity, AudioComponent Component)? PlayStatic(AudioStream stream, EntityCoordinates coordinates, ResolvedSoundSpecifier? specifier, AudioParams? audioParams = null)
|
||||
{
|
||||
if (TerminatingOrDeleted(coordinates.EntityId))
|
||||
{
|
||||
@@ -589,33 +599,33 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
}
|
||||
|
||||
var playing = CreateAndStartPlayingStream(audioParams, stream);
|
||||
var playing = CreateAndStartPlayingStream(audioParams, specifier, stream);
|
||||
_xformSys.SetCoordinates(playing.Entity, coordinates);
|
||||
return playing;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayGlobal(filename, audioParams);
|
||||
return PlayGlobal(specifier, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityUid entity, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayEntity(filename, entity, audioParams);
|
||||
return PlayEntity(specifier, entity, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayStatic(filename, coordinates, audioParams);
|
||||
return PlayStatic(specifier, coordinates, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, ICommonSession recipient, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayGlobal(filename, audioParams);
|
||||
return PlayGlobal(specifier, audioParams);
|
||||
}
|
||||
|
||||
public override void LoadStream<T>(Entity<AudioComponent> entity, T stream)
|
||||
@@ -629,39 +639,39 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, EntityUid recipient, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, EntityUid recipient, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayGlobal(filename, audioParams);
|
||||
return PlayGlobal(specifier, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayEntity(filename, uid, audioParams);
|
||||
return PlayEntity(specifier, uid, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayEntity(filename, uid, audioParams);
|
||||
return PlayEntity(specifier, uid, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayStatic(filename, coordinates, audioParams);
|
||||
return PlayStatic(specifier, coordinates, audioParams);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
{
|
||||
return PlayStatic(filename, coordinates, audioParams);
|
||||
return PlayStatic(specifier, coordinates, audioParams);
|
||||
}
|
||||
|
||||
private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, AudioStream stream)
|
||||
private (EntityUid Entity, AudioComponent Component) CreateAndStartPlayingStream(AudioParams? audioParams, ResolvedSoundSpecifier? specifier, AudioStream stream)
|
||||
{
|
||||
var audioP = audioParams ?? AudioParams.Default;
|
||||
var entity = SetupAudio(null, audioP, initialize: false, length: stream.Length);
|
||||
var entity = SetupAudio(specifier, audioP, initialize: false, length: stream.Length);
|
||||
LoadStream(entity, stream);
|
||||
EntityManager.InitializeAndStartEntity(entity);
|
||||
var comp = entity.Comp;
|
||||
@@ -694,17 +704,17 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
|
||||
private void OnEntityCoordinates(PlayAudioPositionalMessage ev)
|
||||
{
|
||||
PlayStatic(ev.FileName, GetCoordinates(ev.Coordinates), ev.AudioParams, false);
|
||||
PlayStatic(ev.Specifier, GetCoordinates(ev.Coordinates), ev.AudioParams, false);
|
||||
}
|
||||
|
||||
private void OnEntityAudio(PlayAudioEntityMessage ev)
|
||||
{
|
||||
PlayEntity(ev.FileName, GetEntity(ev.NetEntity), ev.AudioParams, false);
|
||||
PlayEntity(ev.Specifier, GetEntity(ev.NetEntity), ev.AudioParams, false);
|
||||
}
|
||||
|
||||
private void OnGlobalAudio(PlayAudioGlobalMessage ev)
|
||||
{
|
||||
PlayGlobal(ev.FileName, ev.AudioParams, false);
|
||||
PlayGlobal(ev.Specifier, ev.AudioParams, false);
|
||||
}
|
||||
|
||||
protected override TimeSpan GetAudioLengthImpl(string filename)
|
||||
|
||||
@@ -115,10 +115,6 @@ namespace Robust.Client
|
||||
/// <inheritdoc />
|
||||
public void DisconnectFromServer(string reason)
|
||||
{
|
||||
DebugTools.Assert(RunLevel > ClientRunLevel.Initialize);
|
||||
DebugTools.Assert(_net.IsConnected);
|
||||
// run level changed in OnNetDisconnect()
|
||||
// are both of these *really* needed?
|
||||
_net.ClientDisconnect(reason);
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +382,7 @@ namespace Robust.Client
|
||||
|
||||
_prof.Initialize();
|
||||
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null);
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null, hideUserDataDir: true);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed class VisibilitySystem : SharedVisibilitySystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -523,6 +523,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case Color color:
|
||||
program.SetUniform(name, color);
|
||||
break;
|
||||
case Color[] colorArr:
|
||||
program.SetUniform(name, colorArr);
|
||||
break;
|
||||
case int i:
|
||||
program.SetUniform(name, i);
|
||||
break;
|
||||
|
||||
@@ -485,6 +485,13 @@ namespace Robust.Client.Graphics.Clyde
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, Color[] value)
|
||||
{
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.ParametersDirty = true;
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, int value)
|
||||
{
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
|
||||
@@ -369,6 +369,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, Color[] value)
|
||||
{
|
||||
}
|
||||
|
||||
private protected override void SetParameterImpl(string name, int value)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetUniformDirect(int slot, in Color color, bool convertToLinear=true)
|
||||
private void SetUniformDirect(int slot, in Color color, bool convertToLinear = true)
|
||||
{
|
||||
var converted = color;
|
||||
if (convertToLinear)
|
||||
@@ -349,6 +349,39 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniform(string uniformName, Color[] colors)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
SetUniformDirect(uniformId, colors);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void SetUniformDirect(int slot, Color[] colors, bool convertToLinear = true)
|
||||
{
|
||||
scoped Span<Color> colorsToPass;
|
||||
if (convertToLinear)
|
||||
{
|
||||
colorsToPass = stackalloc Color[colors.Length];
|
||||
for (int i = 0; i < colors.Length; i++)
|
||||
{
|
||||
colorsToPass[i] = Color.FromSrgb(colors[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
colorsToPass = colors;
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (Color* ptr = &colorsToPass[0])
|
||||
{
|
||||
GL.Uniform4(slot, colorsToPass.Length, (float*)ptr);
|
||||
_clyde.CheckGlError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUniform(string uniformName, in Vector3 vector)
|
||||
{
|
||||
var uniformId = GetUniform(uniformName);
|
||||
|
||||
@@ -221,11 +221,12 @@ namespace Robust.Client.Graphics
|
||||
|
||||
public static bool TypeSupportsArrays(this ShaderDataType type)
|
||||
{
|
||||
// TODO: add support for int, and vec3/4 arrays
|
||||
// TODO: add support for int, and vec3 arrays
|
||||
return
|
||||
(type == ShaderDataType.Float) ||
|
||||
(type == ShaderDataType.Vec2) ||
|
||||
(type == ShaderDataType.Bool);
|
||||
(type == ShaderDataType.Bool) ||
|
||||
(type == ShaderDataType.Vec4);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "StringLiteralTypo")]
|
||||
|
||||
@@ -113,6 +113,13 @@ namespace Robust.Client.Graphics
|
||||
SetParameterImpl(name, value);
|
||||
}
|
||||
|
||||
public void SetParameter(string name, Color[] value)
|
||||
{
|
||||
EnsureAlive();
|
||||
EnsureMutable();
|
||||
SetParameterImpl(name, value);
|
||||
}
|
||||
|
||||
public void SetParameter(string name, Vector4 value)
|
||||
{
|
||||
EnsureAlive();
|
||||
@@ -223,6 +230,7 @@ namespace Robust.Client.Graphics
|
||||
private protected abstract void SetParameterImpl(string name, Vector3 value);
|
||||
private protected abstract void SetParameterImpl(string name, Vector4 value);
|
||||
private protected abstract void SetParameterImpl(string name, Color value);
|
||||
private protected abstract void SetParameterImpl(string name, Color[] value);
|
||||
private protected abstract void SetParameterImpl(string name, int value);
|
||||
private protected abstract void SetParameterImpl(string name, Vector2i value);
|
||||
private protected abstract void SetParameterImpl(string name, bool value);
|
||||
|
||||
@@ -144,7 +144,7 @@ namespace Robust.Client.Placement
|
||||
if (mousePos.MapId == MapId.Nullspace)
|
||||
yield break;
|
||||
|
||||
var (_, (x, y)) = EntityCoordinates.FromMap(pManager.StartPoint.EntityId, mousePos, transformSys, pManager.EntityManager) - pManager.StartPoint;
|
||||
var (_, (x, y)) = transformSys.ToCoordinates(pManager.StartPoint.EntityId, mousePos) - pManager.StartPoint;
|
||||
float iterations;
|
||||
Vector2 distance;
|
||||
if (Math.Abs(x) > Math.Abs(y))
|
||||
@@ -176,7 +176,7 @@ namespace Robust.Client.Placement
|
||||
if (mousePos.MapId == MapId.Nullspace)
|
||||
yield break;
|
||||
|
||||
var placementdiff = EntityCoordinates.FromMap(pManager.StartPoint.EntityId, mousePos, transformSys, pManager.EntityManager) - pManager.StartPoint;
|
||||
var placementdiff = transformSys.ToCoordinates(pManager.StartPoint.EntityId, mousePos) - pManager.StartPoint;
|
||||
|
||||
var xSign = Math.Sign(placementdiff.X);
|
||||
var ySign = Math.Sign(placementdiff.Y);
|
||||
@@ -264,13 +264,15 @@ namespace Robust.Client.Placement
|
||||
protected EntityCoordinates ScreenToCursorGrid(ScreenCoordinates coords)
|
||||
{
|
||||
var mapCoords = pManager.EyeManager.PixelToMap(coords.Position);
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
|
||||
if (!pManager.MapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid))
|
||||
{
|
||||
return EntityCoordinates.FromMap(pManager.MapManager, mapCoords);
|
||||
|
||||
return transformSys.ToCoordinates(mapCoords);
|
||||
}
|
||||
|
||||
var transformSys = pManager.EntityManager.System<SharedTransformSystem>();
|
||||
return EntityCoordinates.FromMap(gridUid, mapCoords, transformSys, pManager.EntityManager);
|
||||
return transformSys.ToCoordinates(gridUid, mapCoords);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,30 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public int ScrollSpeedX { get; set; } = 50;
|
||||
public int ScrollSpeedY { get; set; } = 50;
|
||||
|
||||
public float VScroll
|
||||
{
|
||||
get => _vScrollBar.Value;
|
||||
set => _vScrollBar.Value = value;
|
||||
}
|
||||
|
||||
public float VScrollTarget
|
||||
{
|
||||
get => _vScrollBar.ValueTarget;
|
||||
set => _vScrollBar.ValueTarget = value;
|
||||
}
|
||||
|
||||
public float HScroll
|
||||
{
|
||||
get => _hScrollBar.Value;
|
||||
set => _hScrollBar.Value = value;
|
||||
}
|
||||
|
||||
public float HScrollTarget
|
||||
{
|
||||
get => _hScrollBar.ValueTarget;
|
||||
set => _hScrollBar.ValueTarget = value;
|
||||
}
|
||||
|
||||
private bool _reserveScrollbarSpace;
|
||||
public bool ReserveScrollbarSpace
|
||||
{
|
||||
|
||||
@@ -81,27 +81,27 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? specifier, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
var entity = SetupAudio(specifier, audioParams);
|
||||
AddAudioFilter(entity, entity.Comp, playerFilter);
|
||||
entity.Comp.Global = true;
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(uid))
|
||||
return null;
|
||||
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
var entity = SetupAudio(specifier, audioParams);
|
||||
// Move it after setting it up
|
||||
XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero));
|
||||
|
||||
@@ -115,24 +115,24 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityUid uid, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityUid uid, AudioParams? audioParams = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(uid))
|
||||
return null;
|
||||
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
var entity = SetupAudio(specifier, audioParams);
|
||||
XformSystem.SetCoordinates(entity, new EntityCoordinates(uid, Vector2.Zero));
|
||||
|
||||
return (entity, entity.Comp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? specifier, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(coordinates.EntityId))
|
||||
@@ -144,7 +144,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
if (!coordinates.IsValid(EntityManager))
|
||||
return null;
|
||||
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
var entity = SetupAudio(specifier, audioParams);
|
||||
XformSystem.SetCoordinates(entity, coordinates);
|
||||
AddAudioFilter(entity, entity.Comp, playerFilter);
|
||||
|
||||
@@ -152,10 +152,10 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(string? filename, EntityCoordinates coordinates,
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? specifier, EntityCoordinates coordinates,
|
||||
AudioParams? audioParams = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
if (specifier is null)
|
||||
return null;
|
||||
|
||||
if (TerminatingOrDeleted(coordinates.EntityId))
|
||||
@@ -168,7 +168,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
|
||||
// TODO: Transform TryFindGridAt mess + optimisation required.
|
||||
var entity = SetupAudio(filename, audioParams);
|
||||
var entity = SetupAudio(specifier, audioParams);
|
||||
XformSystem.SetCoordinates(entity, coordinates);
|
||||
|
||||
return (entity, entity.Comp);
|
||||
@@ -191,7 +191,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
if (sound == null)
|
||||
return null;
|
||||
|
||||
var audio = PlayPvs(GetSound(sound), source, audioParams ?? sound.Params);
|
||||
var audio = PlayPvs(ResolveSound(sound), source, audioParams ?? sound.Params);
|
||||
|
||||
if (audio == null)
|
||||
return null;
|
||||
@@ -206,7 +206,7 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
if (sound == null)
|
||||
return null;
|
||||
|
||||
var audio = PlayPvs(GetSound(sound), coordinates, audioParams ?? sound.Params);
|
||||
var audio = PlayPvs(ResolveSound(sound), coordinates, audioParams ?? sound.Params);
|
||||
|
||||
if (audio == null)
|
||||
return null;
|
||||
@@ -215,12 +215,12 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return audio;
|
||||
}
|
||||
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? 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)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, EntityUid recipient, AudioParams? audioParams = null)
|
||||
{
|
||||
if (TryComp(recipient, out ActorComponent? actor))
|
||||
return PlayGlobal(filename, actor.PlayerSession, audioParams);
|
||||
@@ -228,12 +228,12 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
}
|
||||
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? 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)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null)
|
||||
{
|
||||
if (TryComp(recipient, out ActorComponent? actor))
|
||||
return PlayEntity(filename, actor.PlayerSession, uid, audioParams);
|
||||
@@ -241,12 +241,12 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
return null;
|
||||
}
|
||||
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? 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)
|
||||
public override (EntityUid Entity, AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null)
|
||||
{
|
||||
if (TryComp(recipient, out ActorComponent? actor))
|
||||
return PlayStatic(filename, actor.PlayerSession, coordinates, audioParams);
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace Robust.Server
|
||||
: null;
|
||||
|
||||
// Set up the VFS
|
||||
_resources.Initialize(dataDir);
|
||||
_resources.Initialize(dataDir, hideUserDataDir: false);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions) : Options.MountOptions;
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
public sealed class VisibilitySystem : EntitySystem
|
||||
public sealed class VisibilitySystem : SharedVisibilitySystem
|
||||
{
|
||||
[Dependency] private readonly PvsSystem _pvs = default!;
|
||||
[Dependency] private readonly IViewVariablesManager _vvManager = default!;
|
||||
@@ -40,7 +40,7 @@ namespace Robust.Server.GameObjects
|
||||
EntityManager.EntityInitialized -= OnEntityInit;
|
||||
}
|
||||
|
||||
public void AddLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
public override void AddLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
ent.Comp ??= _visibilityQuery.CompOrNull(ent.Owner) ?? AddComp<VisibilityComponent>(ent.Owner);
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(ent);
|
||||
}
|
||||
|
||||
public void RemoveLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
public override void RemoveLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
if (!_visibilityQuery.Resolve(ent.Owner, ref ent.Comp, false))
|
||||
return;
|
||||
@@ -67,7 +67,7 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(ent);
|
||||
}
|
||||
|
||||
public void SetLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
public override void SetLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
ent.Comp ??= _visibilityQuery.CompOrNull(ent.Owner) ?? AddComp<VisibilityComponent>(ent.Owner);
|
||||
|
||||
@@ -90,14 +90,14 @@ namespace Robust.Server.GameObjects
|
||||
RefreshVisibility(ent.Owner, null, ent.Comp);
|
||||
}
|
||||
|
||||
public void RefreshVisibility(EntityUid uid,
|
||||
public override void RefreshVisibility(EntityUid uid,
|
||||
VisibilityComponent? visibilityComponent = null,
|
||||
MetaDataComponent? meta = null)
|
||||
{
|
||||
RefreshVisibility((uid, visibilityComponent, meta));
|
||||
}
|
||||
|
||||
public void RefreshVisibility(Entity<VisibilityComponent?, MetaDataComponent?> ent)
|
||||
public override void RefreshVisibility(Entity<VisibilityComponent?, MetaDataComponent?> ent)
|
||||
{
|
||||
if (!_metaQuery.Resolve(ent, ref ent.Comp2, false))
|
||||
return;
|
||||
|
||||
90
Robust.Shared/Audio/ResolvedSoundSpecifier.cs
Normal file
90
Robust.Shared/Audio/ResolvedSoundSpecifier.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Robust.Shared.Audio;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a path to a sound resource, either as a literal path or as a collection ID and index.
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolvedPathSpecifier"/>
|
||||
/// <seealso cref="ResolvedCollectionSpecifier"/>
|
||||
[Serializable, NetSerializable]
|
||||
public abstract partial class ResolvedSoundSpecifier {
|
||||
[Obsolete("String literals for sounds are deprecated, use a SoundSpecifier or ResolvedSoundSpecifier as appropriate instead")]
|
||||
public static implicit operator ResolvedSoundSpecifier(string s) => new ResolvedPathSpecifier(s);
|
||||
[Obsolete("String literals for sounds are deprecated, use a SoundSpecifier or ResolvedSoundSpecifier as appropriate instead")]
|
||||
public static implicit operator ResolvedSoundSpecifier(ResPath s) => new ResolvedPathSpecifier(s);
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether <c>s</c> is null, or if it contains an empty path/collection ID.
|
||||
/// </summary>
|
||||
public static bool IsNullOrEmpty(ResolvedSoundSpecifier? s) {
|
||||
return s switch {
|
||||
null => true,
|
||||
ResolvedPathSpecifier path => path.Path.ToString() == "",
|
||||
ResolvedCollectionSpecifier collection => string.IsNullOrEmpty(collection.Collection),
|
||||
_ => throw new ArgumentOutOfRangeException("s", s, "argument is not a ResolvedPathSpecifier or a ResolvedCollectionSpecifier"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a path to a sound resource as a literal path.
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolvedCollectionSpecifier"/>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ResolvedPathSpecifier : ResolvedSoundSpecifier {
|
||||
/// <summary>
|
||||
/// The resource path of the sound.
|
||||
/// </summary>
|
||||
public ResPath Path { get; private set; }
|
||||
|
||||
override public string ToString() =>
|
||||
$"ResolvedPathSpecifier({Path})";
|
||||
|
||||
[UsedImplicitly]
|
||||
private ResolvedPathSpecifier()
|
||||
{
|
||||
}
|
||||
public ResolvedPathSpecifier(ResPath path)
|
||||
{
|
||||
Path = path;
|
||||
}
|
||||
public ResolvedPathSpecifier(string path) : this(new ResPath(path))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a path to a sound resource as a collection ID and index.
|
||||
/// </summary>
|
||||
/// <seealso cref="ResolvedPathSpecifier"/>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ResolvedCollectionSpecifier : ResolvedSoundSpecifier {
|
||||
/// <summary>
|
||||
/// The ID of the <see cref="SoundCollectionPrototype">sound collection</see> to look up.
|
||||
/// </summary>
|
||||
public ProtoId<SoundCollectionPrototype>? Collection { get; private set; }
|
||||
/// <summary>
|
||||
/// The index of the file in the associated sound collection to play.
|
||||
/// </summary>
|
||||
public int Index { get; private set; }
|
||||
|
||||
override public string ToString() =>
|
||||
$"ResolvedCollectionSpecifier({Collection}, {Index})";
|
||||
|
||||
[UsedImplicitly]
|
||||
private ResolvedCollectionSpecifier()
|
||||
{
|
||||
}
|
||||
|
||||
public ResolvedCollectionSpecifier(string collection, int index)
|
||||
{
|
||||
Collection = collection;
|
||||
Index = index;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,9 @@ public sealed partial class SoundPathSpecifier : SoundSpecifier
|
||||
[DataField(Node, customTypeSerializer: typeof(ResPathSerializer), required: true)]
|
||||
public ResPath Path { get; private set; }
|
||||
|
||||
override public string ToString() =>
|
||||
$"SoundPathSpecifier({Path})";
|
||||
|
||||
[UsedImplicitly]
|
||||
private SoundPathSpecifier()
|
||||
{
|
||||
@@ -52,6 +55,9 @@ public sealed partial class SoundCollectionSpecifier : SoundSpecifier
|
||||
[DataField(Node, customTypeSerializer: typeof(PrototypeIdSerializer<SoundCollectionPrototype>), required: true)]
|
||||
public string? Collection { get; private set; }
|
||||
|
||||
override public string ToString() =>
|
||||
$"SoundCollectionSpecifier({Collection})";
|
||||
|
||||
[UsedImplicitly]
|
||||
public SoundCollectionSpecifier() { }
|
||||
|
||||
|
||||
@@ -283,33 +283,60 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the filepath to a sound file.
|
||||
/// Resolve a sound specifier so it can be consistently played back on all clients.
|
||||
/// </summary>
|
||||
public string GetSound(SoundSpecifier specifier)
|
||||
public ResolvedSoundSpecifier ResolveSound(SoundSpecifier specifier)
|
||||
{
|
||||
switch (specifier)
|
||||
{
|
||||
case SoundPathSpecifier path:
|
||||
return path.Path == default ? string.Empty : path.Path.ToString();
|
||||
return new ResolvedPathSpecifier(path.Path == default ? string.Empty : path.Path.ToString());
|
||||
|
||||
case SoundCollectionSpecifier collection:
|
||||
{
|
||||
if (collection.Collection == null)
|
||||
return string.Empty;
|
||||
return new ResolvedPathSpecifier(string.Empty);
|
||||
|
||||
var soundCollection = ProtoMan.Index<SoundCollectionPrototype>(collection.Collection);
|
||||
return RandMan.Pick(soundCollection.PickFiles).ToString();
|
||||
var index = RandMan.Next(soundCollection.PickFiles.Count);
|
||||
return new ResolvedCollectionSpecifier(collection.Collection, index);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
return new ResolvedPathSpecifier(string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the filepath to a sound file.
|
||||
/// </summary>
|
||||
[Obsolete("Use ResolveSound() and pass around resolved sound specifiers instead.")]
|
||||
public string GetSound(SoundSpecifier specifier)
|
||||
{
|
||||
var resolved = ResolveSound(specifier);
|
||||
return GetAudioPath(resolved);
|
||||
}
|
||||
|
||||
#region AudioParams
|
||||
|
||||
protected Entity<AudioComponent> SetupAudio(string? fileName, AudioParams? audioParams, bool initialize = true, TimeSpan? length = null)
|
||||
[return: NotNullIfNotNull(nameof(specifier))]
|
||||
public string? GetAudioPath(ResolvedSoundSpecifier? specifier)
|
||||
{
|
||||
return specifier switch {
|
||||
ResolvedPathSpecifier path =>
|
||||
path.Path.ToString(),
|
||||
ResolvedCollectionSpecifier collection =>
|
||||
collection.Collection is null ?
|
||||
string.Empty :
|
||||
ProtoMan.Index<SoundCollectionPrototype>(collection.Collection).PickFiles[collection.Index].ToString(),
|
||||
null => null,
|
||||
_ => throw new ArgumentOutOfRangeException("specifier", specifier, "argument is not a ResolvedPathSpecifier or a ResolvedCollectionSpecifier"),
|
||||
};
|
||||
}
|
||||
|
||||
protected Entity<AudioComponent> SetupAudio(ResolvedSoundSpecifier? specifier, AudioParams? audioParams, bool initialize = true, TimeSpan? length = null)
|
||||
{
|
||||
var uid = EntityManager.CreateEntityUninitialized("Audio", MapCoordinates.Nullspace);
|
||||
var fileName = GetAudioPath(specifier);
|
||||
DebugTools.Assert(!string.IsNullOrEmpty(fileName) || length is not null);
|
||||
MetadataSys.SetEntityName(uid, $"Audio ({fileName})", raiseEvents: false);
|
||||
audioParams ??= AudioParams.Default;
|
||||
@@ -395,8 +422,9 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Gets the timespan of the specified audio.
|
||||
/// </summary>
|
||||
public TimeSpan GetAudioLength(string filename)
|
||||
public TimeSpan GetAudioLength(ResolvedSoundSpecifier specifier)
|
||||
{
|
||||
var filename = GetAudioPath(specifier) ?? string.Empty;
|
||||
if (!filename.StartsWith("/"))
|
||||
throw new ArgumentException("Path must be rooted");
|
||||
|
||||
@@ -429,7 +457,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="playerFilter">The set of players that will hear the sound.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, Filter playerFilter, bool recordReplay, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file globally, without position.
|
||||
@@ -438,7 +466,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="playerFilter">The set of players that will hear the sound.</param>
|
||||
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, audioParams ?? sound.Params);
|
||||
return sound == null ? null : PlayGlobal(ResolveSound(sound), playerFilter, recordReplay, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -446,7 +474,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string? filename, ICommonSession recipient, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, ICommonSession recipient, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file globally, without position.
|
||||
@@ -455,7 +483,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
public (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(SoundSpecifier? sound, ICommonSession recipient, AudioParams? audioParams = null)
|
||||
{
|
||||
return sound == null ? null : PlayGlobal(GetSound(sound), recipient, audioParams ?? sound.Params);
|
||||
return sound == null ? null : PlayGlobal(ResolveSound(sound), recipient, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
public abstract void LoadStream<T>(Entity<AudioComponent> entity, T stream);
|
||||
@@ -465,7 +493,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(string? filename, EntityUid recipient, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(ResolvedSoundSpecifier? filename, EntityUid recipient, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file globally, without position.
|
||||
@@ -474,7 +502,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
public (EntityUid Entity, Components.AudioComponent Component)? PlayGlobal(SoundSpecifier? sound, EntityUid recipient, AudioParams? audioParams = null)
|
||||
{
|
||||
return sound == null ? null : PlayGlobal(GetSound(sound), recipient, audioParams ?? sound.Params);
|
||||
return sound == null ? null : PlayGlobal(ResolveSound(sound), recipient, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -483,7 +511,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="playerFilter">The set of players that will hear the sound.</param>
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string? filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, Filter playerFilter, EntityUid uid, bool recordReplay, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file following an entity.
|
||||
@@ -491,7 +519,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, ICommonSession recipient, EntityUid uid, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file following an entity.
|
||||
@@ -499,7 +527,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(string? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayEntity(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityUid uid, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file following an entity.
|
||||
@@ -509,7 +537,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayEntity(ResolveSound(sound), playerFilter, uid, recordReplay, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -520,7 +548,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayEntity(ResolveSound(sound), recipient, uid, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -531,7 +559,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayEntity(ResolveSound(sound), recipient, uid, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -541,7 +569,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayPvs(ResolveSound(sound), uid, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -551,7 +579,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="coordinates">The EntityCoordinates to attach the audio source to.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayPvs(ResolveSound(sound), coordinates, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -559,7 +587,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="sound">The sound specifier that points the audio file(s) that should be played.</param>
|
||||
/// <param name="coordinates">The EntityCoordinates to attach the audio source to.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(string? filename,
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? filename,
|
||||
EntityCoordinates coordinates, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -567,7 +595,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="uid">The UID of the entity "emitting" the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(string? filename, EntityUid uid,
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayPvs(ResolvedSoundSpecifier? filename, EntityUid uid,
|
||||
AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
@@ -604,7 +632,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="playerFilter">The set of players that will hear the sound.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, Filter playerFilter, EntityCoordinates coordinates, bool recordReplay, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file at a static position.
|
||||
@@ -612,7 +640,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, ICommonSession recipient, EntityCoordinates coordinates, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file at a static position.
|
||||
@@ -620,7 +648,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
||||
/// <param name="recipient">The player that will hear the sound.</param>
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(string? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null);
|
||||
public abstract (EntityUid Entity, Components.AudioComponent Component)? PlayStatic(ResolvedSoundSpecifier? filename, EntityUid recipient, EntityCoordinates coordinates, AudioParams? audioParams = null);
|
||||
|
||||
/// <summary>
|
||||
/// Play an audio file at a static position.
|
||||
@@ -630,7 +658,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
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, audioParams);
|
||||
return sound == null ? null : PlayStatic(ResolveSound(sound), playerFilter, coordinates, recordReplay, audioParams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -641,7 +669,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayStatic(ResolveSound(sound), recipient, coordinates, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -652,7 +680,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <param name="coordinates">The coordinates at which to play the audio.</param>
|
||||
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);
|
||||
return sound == null ? null : PlayStatic(ResolveSound(sound), recipient, coordinates, audioParams ?? sound.Params);
|
||||
}
|
||||
|
||||
// These are just here for replays now.
|
||||
@@ -665,7 +693,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
[NetSerializable, Serializable]
|
||||
protected abstract class AudioMessage : EntityEventArgs
|
||||
{
|
||||
public string FileName = string.Empty;
|
||||
public ResolvedSoundSpecifier Specifier = new ResolvedPathSpecifier(string.Empty);
|
||||
public AudioParams AudioParams;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
internal string GetPath(ResPath relPath)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(_directory.FullName, relPath.ToRelativeSystemPath()));
|
||||
return PathHelpers.SafeGetResourcePath(_directory.FullName, relPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -14,7 +14,11 @@ namespace Robust.Shared.ContentPack
|
||||
/// The directory to use for user data.
|
||||
/// If null, a virtual temporary file system is used instead.
|
||||
/// </param>
|
||||
void Initialize(string? userData);
|
||||
/// <param name="hideUserDataDir">
|
||||
/// If true, <see cref="IWritableDirProvider.RootDir"/> will be hidden on
|
||||
/// <see cref="IResourceManager.UserData"/>.
|
||||
/// </param>
|
||||
void Initialize(string? userData, bool hideUserDataDir);
|
||||
|
||||
/// <summary>
|
||||
/// Mounts a single stream as a content file. Useful for unit testing.
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
/// <summary>
|
||||
/// The root path of this provider.
|
||||
/// Can be null if it's a virtual provider.
|
||||
/// Can be null if it's a virtual provider or the path is protected (e.g. on the client).
|
||||
/// </summary>
|
||||
string? RootDir { get; }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
{
|
||||
@@ -63,5 +64,27 @@ namespace Robust.Shared.ContentPack
|
||||
!OperatingSystem.IsWindows()
|
||||
&& !OperatingSystem.IsMacOS();
|
||||
|
||||
|
||||
internal static string SafeGetResourcePath(string baseDir, ResPath path)
|
||||
{
|
||||
var relSysPath = path.ToRelativeSystemPath();
|
||||
if (relSysPath.Contains("\\..") || relSysPath.Contains("/.."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
var retPath = Path.GetFullPath(Path.Join(baseDir, relSysPath));
|
||||
// better safe than sorry check
|
||||
if (!retPath.StartsWith(baseDir))
|
||||
{
|
||||
// Allow path to match if it's just missing the directory separator at the end.
|
||||
if (retPath != baseDir.TrimEnd('\\'))
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
return retPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,13 +41,13 @@ namespace Robust.Shared.ContentPack
|
||||
public IWritableDirProvider UserData { get; private set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Initialize(string? userData)
|
||||
public virtual void Initialize(string? userData, bool hideRootDir)
|
||||
{
|
||||
Sawmill = _logManager.GetSawmill("res");
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
UserData = new WritableDirProvider(Directory.CreateDirectory(userData));
|
||||
UserData = new WritableDirProvider(Directory.CreateDirectory(userData), hideRootDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -379,7 +379,13 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
if (root is DirLoader loader)
|
||||
{
|
||||
yield return new ResPath(loader.GetPath(new ResPath(@"/")));
|
||||
var rootDir = loader.GetPath(new ResPath(@"/"));
|
||||
|
||||
// TODO: GET RID OF THIS.
|
||||
// This code shouldn't be passing OS disk paths through ResPath.
|
||||
rootDir = rootDir.Replace(Path.DirectorySeparatorChar, '/');
|
||||
|
||||
yield return new ResPath(rootDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -696,7 +696,7 @@ Types:
|
||||
- "bool IsMatch(string, string, System.Text.RegularExpressions.RegexOptions, System.TimeSpan)"
|
||||
- "int GroupNumberFromName(string)"
|
||||
- "int[] GetGroupNumbers()"
|
||||
- "string Escape()"
|
||||
- "string Escape(string)"
|
||||
- "string GroupNameFromNumber(int)"
|
||||
- "string Replace(string, string)"
|
||||
- "string Replace(string, string, int)"
|
||||
|
||||
@@ -10,17 +10,22 @@ namespace Robust.Shared.ContentPack
|
||||
/// <inheritdoc />
|
||||
internal sealed class WritableDirProvider : IWritableDirProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
private readonly bool _hideRootDir;
|
||||
|
||||
public string RootDir { get; }
|
||||
|
||||
string? IWritableDirProvider.RootDir => _hideRootDir ? null : RootDir;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of <see cref="WritableDirProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="rootDir">Root file system directory to allow writing.</param>
|
||||
public WritableDirProvider(DirectoryInfo rootDir)
|
||||
/// <param name="hideRootDir">If true, <see cref="IWritableDirProvider.RootDir"/> is reported as null.</param>
|
||||
public WritableDirProvider(DirectoryInfo rootDir, bool hideRootDir)
|
||||
{
|
||||
// FullName does not have a trailing separator, and we MUST have a separator.
|
||||
RootDir = rootDir.FullName + Path.DirectorySeparatorChar.ToString();
|
||||
_hideRootDir = hideRootDir;
|
||||
}
|
||||
|
||||
#region File Access
|
||||
@@ -119,7 +124,7 @@ namespace Robust.Shared.ContentPack
|
||||
throw new FileNotFoundException();
|
||||
|
||||
var dirInfo = new DirectoryInfo(GetFullPath(path));
|
||||
return new WritableDirProvider(dirInfo);
|
||||
return new WritableDirProvider(dirInfo, _hideRootDir);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -180,20 +185,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
path = path.Clean();
|
||||
|
||||
return GetFullPath(RootDir, path);
|
||||
}
|
||||
|
||||
private static string GetFullPath(string root, ResPath path)
|
||||
{
|
||||
var relPath = path.ToRelativeSystemPath();
|
||||
if (relPath.Contains("\\..") || relPath.Contains("/.."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
return Path.GetFullPath(Path.Combine(root, relPath));
|
||||
return PathHelpers.SafeGetResourcePath(RootDir, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,10 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
foreach (var (origId, prototypeId) in savedMap)
|
||||
{
|
||||
if (_tileDef.TryGetDefinition(prototypeId, out var definition))
|
||||
{
|
||||
_tileMap.TryAdd(definition.TileId, origId);
|
||||
_yamlTileIds.Add(origId); // Make sure we record the IDs we're using so when we need to reserve new ones we can
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +596,7 @@ public sealed class EntitySerializer : ISerializationContext,
|
||||
public MappingDataNode Write()
|
||||
{
|
||||
DebugTools.AssertEqual(Maps.ToHashSet().Count, Maps.Count, "Duplicate maps?");
|
||||
DebugTools.AssertEqual(Grids.ToHashSet().Count, Grids.Count, "Duplicate frids?");
|
||||
DebugTools.AssertEqual(Grids.ToHashSet().Count, Grids.Count, "Duplicate grids?");
|
||||
DebugTools.AssertEqual(Orphans.ToHashSet().Count, Orphans.Count, "Duplicate orphans?");
|
||||
DebugTools.AssertEqual(Nullspace.ToHashSet().Count, Nullspace.Count, "Duplicate nullspace?");
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
|
||||
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(VisibilitySystem))]
|
||||
[Access(typeof(SharedVisibilitySystem))]
|
||||
public sealed partial class VisibilityComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -1075,6 +1075,97 @@ namespace Robust.Shared.GameObjects
|
||||
return TryGetComponent(uid.Value, netId, out component, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryCopyComponent<T>(EntityUid source, EntityUid target, ref T? sourceComponent, [NotNullWhen(true)] out T? targetComp, MetaDataComponent? meta = null) where T : IComponent
|
||||
{
|
||||
if (!MetaQuery.Resolve(target, ref meta))
|
||||
{
|
||||
targetComp = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sourceComponent == null && !TryGetComponent(source, out sourceComponent))
|
||||
{
|
||||
targetComp = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
targetComp = CopyComponentInternal(source, target, sourceComponent, meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryCopyComponents(
|
||||
EntityUid source,
|
||||
EntityUid target,
|
||||
MetaDataComponent? meta = null,
|
||||
params Type[] sourceComponents)
|
||||
{
|
||||
if (!MetaQuery.TryGetComponent(source, out meta))
|
||||
return false;
|
||||
|
||||
var allCopied = true;
|
||||
|
||||
foreach (var type in sourceComponents)
|
||||
{
|
||||
if (!TryGetComponent(source, type, out var srcComp))
|
||||
{
|
||||
allCopied = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
CopyComponent(source, target, srcComp, meta: meta);
|
||||
}
|
||||
|
||||
return allCopied;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IComponent CopyComponent(EntityUid source, EntityUid target, IComponent sourceComponent, MetaDataComponent? meta = null)
|
||||
{
|
||||
if (!MetaQuery.Resolve(target, ref meta))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return CopyComponentInternal(source, target, sourceComponent, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public T CopyComponent<T>(EntityUid source, EntityUid target, T sourceComponent,MetaDataComponent? meta = null) where T : IComponent
|
||||
{
|
||||
if (!MetaQuery.Resolve(target, ref meta))
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return CopyComponentInternal(source, target, sourceComponent, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void CopyComponents(EntityUid source, EntityUid target, MetaDataComponent? meta = null, params IComponent[] sourceComponents)
|
||||
{
|
||||
if (!MetaQuery.Resolve(target, ref meta))
|
||||
return;
|
||||
|
||||
foreach (var comp in sourceComponents)
|
||||
{
|
||||
CopyComponentInternal(source, target, comp, meta);
|
||||
}
|
||||
}
|
||||
|
||||
private T CopyComponentInternal<T>(EntityUid source, EntityUid target, T sourceComponent, MetaDataComponent meta) where T : IComponent
|
||||
{
|
||||
var compReg = ComponentFactory.GetRegistration(sourceComponent.GetType());
|
||||
var component = (T)ComponentFactory.GetComponent(compReg);
|
||||
|
||||
_serManager.CopyTo(sourceComponent, ref component, notNullableOverride: true);
|
||||
component.Owner = target;
|
||||
|
||||
AddComponentInternal(target, component, compReg, true, false, meta);
|
||||
return component;
|
||||
}
|
||||
|
||||
public EntityQuery<TComp1> GetEntityQuery<TComp1>() where TComp1 : IComponent
|
||||
{
|
||||
var comps = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
|
||||
|
||||
@@ -175,7 +175,7 @@ public partial class EntitySystem
|
||||
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
|
||||
where T : IComponentDelta
|
||||
{
|
||||
EntityManager.DirtyFields(uid, comp, meta);
|
||||
EntityManager.DirtyFields(uid, comp, meta, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -565,6 +565,52 @@ public partial class EntitySystem
|
||||
|
||||
#endregion
|
||||
|
||||
#region Component Copy
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TryCopyComponent"/>
|
||||
protected bool TryCopyComponent<T>(
|
||||
EntityUid source,
|
||||
EntityUid target,
|
||||
ref T? sourceComponent,
|
||||
[NotNullWhen(true)] out T? targetComp,
|
||||
MetaDataComponent? meta = null) where T : IComponent
|
||||
{
|
||||
return EntityManager.TryCopyComponent(source, target, ref sourceComponent, out targetComp, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.TryCopyComponents"/>
|
||||
protected bool TryCopyComponents(
|
||||
EntityUid source,
|
||||
EntityUid target,
|
||||
MetaDataComponent? meta = null,
|
||||
params Type[] sourceComponents)
|
||||
{
|
||||
return EntityManager.TryCopyComponents(source, target, meta, sourceComponents);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.CopyComponent"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected IComponent CopyComp(EntityUid source, EntityUid target, IComponent sourceComponent, MetaDataComponent? meta = null)
|
||||
{
|
||||
return EntityManager.CopyComponent(source, target, sourceComponent, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.CopyComponent{T}"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected T CopyComp<T>(EntityUid source, EntityUid target, T sourceComponent, MetaDataComponent? meta = null) where T : IComponent
|
||||
{
|
||||
return EntityManager.CopyComponent(source, target, sourceComponent, meta);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.CopyComponents"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void CopyComps(EntityUid source, EntityUid target, MetaDataComponent? meta = null, params IComponent[] sourceComponents)
|
||||
{
|
||||
EntityManager.CopyComponents(source, target, meta, sourceComponents);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Component Has
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -358,6 +358,51 @@ namespace Robust.Shared.GameObjects
|
||||
/// <returns>If the component existed in the entity.</returns>
|
||||
bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, ushort netId, [NotNullWhen(true)] out IComponent? component, MetaDataComponent? meta = null);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to run <see cref="CopyComponents"/> without throwing if the component doesn't exist.
|
||||
/// </summary>
|
||||
bool TryCopyComponent<T>(
|
||||
EntityUid source,
|
||||
EntityUid target,
|
||||
ref T? sourceComponent,
|
||||
[NotNullWhen(true)] out T? targetComp,
|
||||
MetaDataComponent? meta = null) where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Tries to run <see cref="CopyComponents"/> without throwing if the components don't exist.
|
||||
/// </summary>
|
||||
bool TryCopyComponents(EntityUid source, EntityUid target, MetaDataComponent? meta = null, params Type[] sourceComponents);
|
||||
|
||||
/// <summary>
|
||||
/// Copy a single component from source to target entity.
|
||||
/// </summary>
|
||||
/// <param name="source">The source entity to copy from.</param>
|
||||
/// <param name="target">The target entity to copy to.</param>
|
||||
/// <param name="sourceComponent">The source component instance to copy.</param>
|
||||
/// <param name="component">The copied component if successful.</param>
|
||||
/// <param name="meta">Optional metadata of the target entity.</param>
|
||||
IComponent CopyComponent(EntityUid source, EntityUid target, IComponent sourceComponent, MetaDataComponent? meta = null);
|
||||
|
||||
/// <summary>
|
||||
/// Copy a single component from source to target entity.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of component to copy.</typeparam>
|
||||
/// <param name="source">The source entity to copy from.</param>
|
||||
/// <param name="target">The target entity to copy to.</param>
|
||||
/// <param name="sourceComponent">The source component instance to copy.</param>
|
||||
/// <param name="component">The copied component if successful.</param>
|
||||
/// <param name="meta">Optional metadata of the target entity.</param>
|
||||
T CopyComponent<T>(EntityUid source, EntityUid target, T sourceComponent, MetaDataComponent? meta = null) where T : IComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Copy multiple components from source to target entity using existing component instances.
|
||||
/// </summary>
|
||||
/// <param name="source">The source entity to copy from.</param>
|
||||
/// <param name="target">The target entity to copy to.</param>
|
||||
/// <param name="meta">Optional metadata of the target entity.</param>
|
||||
/// <param name="sourceComponents">Array of component instances to copy.</param>
|
||||
void CopyComponents(EntityUid source, EntityUid target, MetaDataComponent? meta = null, params IComponent[] sourceComponents);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a cached struct enumerator with the specified component.
|
||||
/// </summary>
|
||||
|
||||
@@ -72,7 +72,9 @@ internal sealed class PrototypeReloadSystem : EntitySystem
|
||||
{
|
||||
var data = newPrototype.Components[name];
|
||||
var component = _componentFactory.GetComponent(name);
|
||||
EntityManager.AddComponent(entity, component);
|
||||
|
||||
if (!EntityManager.HasComponent(entity, component.GetType()))
|
||||
EntityManager.AddComponent(entity, component);
|
||||
}
|
||||
|
||||
// Update entity metadata
|
||||
|
||||
26
Robust.Shared/GameObjects/Systems/SharedVisibilitySystem.cs
Normal file
26
Robust.Shared/GameObjects/Systems/SharedVisibilitySystem.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public abstract class SharedVisibilitySystem : EntitySystem
|
||||
{
|
||||
public virtual void AddLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RemoveLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void SetLayer(Entity<VisibilityComponent?> ent, ushort layer, bool refresh = true)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RefreshVisibility(EntityUid uid,
|
||||
VisibilityComponent? visibilityComponent = null,
|
||||
MetaDataComponent? meta = null)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void RefreshVisibility(Entity<VisibilityComponent?, MetaDataComponent?> ent)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@@ -11,7 +10,6 @@ using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network.Messages.Handshake;
|
||||
using Robust.Shared.Utility;
|
||||
using SpaceWizards.Sodium;
|
||||
@@ -98,7 +96,7 @@ namespace Robust.Shared.Network
|
||||
{
|
||||
await CCDoHandshake(winningPeer, winningConnection, userNameRequest, mainCancelToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
winningPeer.Peer.Shutdown("Cancelled");
|
||||
_toCleanNetPeers.Add(winningPeer.Peer);
|
||||
@@ -120,7 +118,10 @@ namespace Robust.Shared.Network
|
||||
_logger.Debug("Handshake completed, connection established.");
|
||||
}
|
||||
|
||||
private async Task CCDoHandshake(NetPeerData peer, NetConnection connection, string userNameRequest,
|
||||
private async Task CCDoHandshake(
|
||||
NetPeerData peer,
|
||||
NetConnection connection,
|
||||
string userNameRequest,
|
||||
CancellationToken cancel)
|
||||
{
|
||||
var encrypt = _config.GetCVar(CVars.NetEncrypt);
|
||||
@@ -289,37 +290,51 @@ namespace Robust.Shared.Network
|
||||
private async Task<(NetPeerData winningPeer, NetConnection winningConnection)?>
|
||||
CCHappyEyeballs(int port, IPAddress first, IPAddress? second, CancellationToken mainCancelToken)
|
||||
{
|
||||
NetPeerData CreatePeerForIp(IPAddress address)
|
||||
// Try to establish a connection with an IP address and wait for it to either connect or fail
|
||||
// Returns a disposable wrapper around the peer/connection because ParallelTask
|
||||
async Task<ConnectionAttempt> AttemptConnection(IPAddress address, CancellationToken cancel)
|
||||
{
|
||||
var config = _getBaseNetPeerConfig();
|
||||
if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
config.LocalAddress = IPAddress.IPv6Any;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.LocalAddress = IPAddress.Any;
|
||||
}
|
||||
config.LocalAddress = address.AddressFamily == AddressFamily.InterNetworkV6
|
||||
? IPAddress.IPv6Any
|
||||
: IPAddress.Any;
|
||||
|
||||
var peer = new NetPeer(config);
|
||||
peer.Start();
|
||||
var data = new NetPeerData(peer);
|
||||
_netPeers.Add(data);
|
||||
return data;
|
||||
var peerData = new NetPeerData(peer);
|
||||
_netPeers.Add(peerData);
|
||||
|
||||
var connection = peer.Connect(new IPEndPoint(address, port));
|
||||
|
||||
try
|
||||
{
|
||||
// We need AwaitNonInitStatusChange to properly handle connection state transitions
|
||||
var reason = await AwaitNonInitStatusChange(connection, cancel);
|
||||
|
||||
if (connection.Status != NetConnectionStatus.Connected)
|
||||
{
|
||||
// Connection failed, clean up and yeet an exception
|
||||
peer.Shutdown(reason);
|
||||
_toCleanNetPeers.Add(peer);
|
||||
throw new Exception($"Connection failed: {reason}");
|
||||
}
|
||||
|
||||
return new ConnectionAttempt(peerData, connection, this);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Something went wrong!
|
||||
peer.Shutdown("Connection attempt failed");
|
||||
_toCleanNetPeers.Add(peer);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// Create first peer.
|
||||
var firstPeer = CreatePeerForIp(first);
|
||||
var firstConnection = firstPeer.Peer.Connect(new IPEndPoint(first, port));
|
||||
NetPeerData? secondPeer = null;
|
||||
NetConnection? secondConnection = null;
|
||||
string? secondReason = null;
|
||||
|
||||
// Waits for a connection's status to change from InitiatedConnect to anything else
|
||||
async Task<string> AwaitNonInitStatusChange(NetConnection connection, CancellationToken cancellationToken)
|
||||
{
|
||||
NetConnectionStatus status;
|
||||
string reason;
|
||||
|
||||
NetConnectionStatus status;
|
||||
do
|
||||
{
|
||||
reason = await AwaitStatusChange(connection, cancellationToken);
|
||||
@@ -329,124 +344,37 @@ namespace Robust.Shared.Network
|
||||
return reason;
|
||||
}
|
||||
|
||||
async Task ConnectSecondDelayed(CancellationToken cancellationToken)
|
||||
{
|
||||
DebugTools.AssertNotNull(second);
|
||||
// Connecting via second peer is delayed by 25ms to give an advantage to IPv6, if it works.
|
||||
var delay = TimeSpan.FromSeconds(_config.GetCVar(CVars.NetHappyEyeballsDelay));
|
||||
await Task.Delay(delay, cancellationToken);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
secondPeer = CreatePeerForIp(second);
|
||||
secondConnection = secondPeer.Peer.Connect(new IPEndPoint(second, port));
|
||||
|
||||
secondReason = await AwaitNonInitStatusChange(secondConnection, cancellationToken);
|
||||
}
|
||||
|
||||
NetPeerData? winningPeer;
|
||||
NetConnection? winningConnection;
|
||||
string? firstReason = null;
|
||||
try
|
||||
{
|
||||
if (second != null)
|
||||
{
|
||||
// We have two addresses to try.
|
||||
var cancellation = CancellationTokenSource.CreateLinkedTokenSource(mainCancelToken);
|
||||
var firstPeerChanged = AwaitNonInitStatusChange(firstConnection, cancellation.Token);
|
||||
var secondPeerChanged = ConnectSecondDelayed(cancellation.Token);
|
||||
// Create list of IPs to try
|
||||
var addresses = second != null
|
||||
? new[] { first, second }
|
||||
: new[] { first };
|
||||
|
||||
var firstChange = await Task.WhenAny(firstPeerChanged, secondPeerChanged);
|
||||
// Use ParallelTask to handle the connection attempts
|
||||
var delay = TimeSpan.FromSeconds(_config.GetCVar(CVars.NetHappyEyeballsDelay));
|
||||
var (result, _) = await HappyEyeballsHttp.ParallelTask(
|
||||
addresses.Length,
|
||||
(i, token) => AttemptConnection(addresses[i], token),
|
||||
delay,
|
||||
mainCancelToken);
|
||||
|
||||
if (firstChange == firstPeerChanged)
|
||||
{
|
||||
_logger.Debug("First peer status changed.");
|
||||
// First peer responded first.
|
||||
if (firstConnection.Status == NetConnectionStatus.Connected)
|
||||
{
|
||||
// First peer won!
|
||||
_logger.Debug("First peer succeeded.");
|
||||
cancellation.Cancel();
|
||||
if (secondPeer != null)
|
||||
{
|
||||
secondPeer.Peer.Shutdown("First connection attempt won.");
|
||||
_toCleanNetPeers.Add(secondPeer.Peer);
|
||||
}
|
||||
|
||||
winningPeer = firstPeer;
|
||||
winningConnection = firstConnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
// First peer failed, try the second one I guess.
|
||||
_logger.Debug("First peer failed.");
|
||||
firstPeer.Peer.Shutdown("You failed.");
|
||||
_toCleanNetPeers.Add(firstPeer.Peer);
|
||||
firstReason = await firstPeerChanged;
|
||||
await secondPeerChanged;
|
||||
winningPeer = secondPeer;
|
||||
winningConnection = secondConnection;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (secondConnection!.Status == NetConnectionStatus.Connected)
|
||||
{
|
||||
// Second peer won!
|
||||
_logger.Debug("Second peer succeeded.");
|
||||
cancellation.Cancel();
|
||||
firstPeer.Peer.Shutdown("Second connection attempt won.");
|
||||
_toCleanNetPeers.Add(firstPeer.Peer);
|
||||
winningPeer = secondPeer;
|
||||
winningConnection = secondConnection;
|
||||
}
|
||||
else
|
||||
{
|
||||
// First peer failed, try the second one I guess.
|
||||
_logger.Debug("Second peer failed.");
|
||||
secondPeer!.Peer.Shutdown("You failed.");
|
||||
_toCleanNetPeers.Add(secondPeer.Peer);
|
||||
firstReason = await firstPeerChanged;
|
||||
winningPeer = firstPeer;
|
||||
winningConnection = firstConnection;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only one address to try. Pretty straight forward.
|
||||
firstReason = await AwaitNonInitStatusChange(firstConnection, mainCancelToken);
|
||||
winningPeer = firstPeer;
|
||||
winningConnection = firstConnection;
|
||||
}
|
||||
return (result.Peer, result.Connection);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
firstPeer.Peer.Shutdown("Cancelled");
|
||||
_toCleanNetPeers.Add(firstPeer.Peer);
|
||||
if (secondPeer != null)
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
secondPeer.Peer.Shutdown("Cancelled");
|
||||
_toCleanNetPeers.Add(secondPeer.Peer);
|
||||
}
|
||||
|
||||
// Connection attempt was cancelled, nothing to see here
|
||||
OnConnectFailed("Connection attempt cancelled.");
|
||||
return null;
|
||||
}
|
||||
|
||||
// winningPeer can still be failed at this point.
|
||||
// If it is, neither succeeded. RIP.
|
||||
if (winningConnection!.Status != NetConnectionStatus.Connected)
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
winningPeer!.Peer.Shutdown("You failed");
|
||||
_toCleanNetPeers.Add(winningPeer.Peer);
|
||||
OnConnectFailed((secondReason ?? firstReason)!);
|
||||
// ParallelTask throws AggregateException with all connection failures
|
||||
// We just take the first one
|
||||
var message = ae.InnerExceptions.First().Message;
|
||||
OnConnectFailed(message);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (winningPeer!, winningConnection);
|
||||
}
|
||||
|
||||
private Task<string> AwaitStatusChange(NetConnection connection, CancellationToken cancellationToken = default)
|
||||
@@ -471,7 +399,8 @@ namespace Robust.Shared.Network
|
||||
return tcs.Task;
|
||||
}
|
||||
|
||||
private Task<NetIncomingMessage> AwaitData(NetConnection connection,
|
||||
private Task<NetIncomingMessage> AwaitData(
|
||||
NetConnection connection,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_awaitingData.ContainsKey(connection))
|
||||
@@ -529,5 +458,17 @@ namespace Robust.Shared.Network
|
||||
}
|
||||
|
||||
private sealed record JoinRequest(string Hash, string? Hwid);
|
||||
|
||||
private sealed class ConnectionAttempt(NetPeerData peer, NetConnection connection, NetManager netManager) : IDisposable
|
||||
{
|
||||
public NetPeerData Peer { get; } = peer;
|
||||
public NetConnection Connection { get; } = connection;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Peer.Peer.Shutdown("Disposing unused connection attempt");
|
||||
netManager._toCleanNetPeers.Add(Peer.Peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,6 +587,14 @@ namespace Robust.Shared.Network
|
||||
public void ClientDisconnect(string reason)
|
||||
{
|
||||
DebugTools.Assert(IsClient, "Should never be called on the server.");
|
||||
|
||||
// First handle any in-progress connection attempt
|
||||
if (ClientConnectState != ClientConnectionState.NotConnecting)
|
||||
{
|
||||
_cancelConnectTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
// Then handle existing connection if any
|
||||
if (ServerChannel != null)
|
||||
{
|
||||
Disconnect?.Invoke(this, new NetDisconnectedArgs(ServerChannel, reason));
|
||||
|
||||
@@ -236,7 +236,8 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
Vector2? anchorB = null,
|
||||
string? id = null,
|
||||
TransformComponent? xformA = null,
|
||||
TransformComponent? xformB = null)
|
||||
TransformComponent? xformB = null,
|
||||
int? minimumDistance = null)
|
||||
{
|
||||
if (!Resolve(bodyA, ref xformA) || !Resolve(bodyB, ref xformB))
|
||||
{
|
||||
@@ -246,9 +247,13 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
anchorA ??= Vector2.Zero;
|
||||
anchorB ??= Vector2.Zero;
|
||||
|
||||
var length = Vector2.Transform(anchorA.Value, xformA.WorldMatrix) - Vector2.Transform(anchorB.Value, xformB.WorldMatrix);
|
||||
var vecA = Vector2.Transform(anchorA.Value, xformA.WorldMatrix);
|
||||
var vecB = Vector2.Transform(anchorB.Value, xformB.WorldMatrix);
|
||||
var length = (vecA - vecB).Length();
|
||||
if (minimumDistance != null)
|
||||
length = Math.Max(minimumDistance.Value, length);
|
||||
|
||||
var joint = new DistanceJoint(bodyA, bodyB, anchorA.Value, anchorB.Value, length.Length());
|
||||
var joint = new DistanceJoint(bodyA, bodyB, anchorA.Value, anchorB.Value, length);
|
||||
id ??= GetJointId(joint);
|
||||
joint.ID = id;
|
||||
AddJoint(joint);
|
||||
|
||||
@@ -38,6 +38,9 @@ public sealed class MultiRootInheritanceGraph<T> where T : notnull
|
||||
//check for circular inheritance
|
||||
foreach (var parent in parents)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(parent, id))
|
||||
throw new InvalidOperationException($"Self Inheritance detected for id \"{id}\"!");
|
||||
|
||||
var parentsL1 = GetParents(parent);
|
||||
if(parentsL1 == null) continue;
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
Assert.That(sContainerSys.Insert(itemUid, container));
|
||||
|
||||
// Modify visibility layer so that the item does not get sent ot the player
|
||||
sEntManager.System<VisibilitySystem>().AddLayer(itemUid, 10 );
|
||||
sEntManager.System<SharedVisibilitySystem>().AddLayer(itemUid, 10 );
|
||||
});
|
||||
|
||||
// Needs minimum 4 to sync to client because buffer size is 3
|
||||
@@ -119,7 +119,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// Modify visibility layer so it now gets sent to the client
|
||||
sEntManager.System<VisibilitySystem>().RemoveLayer(itemUid, 10 );
|
||||
sEntManager.System<SharedVisibilitySystem>().RemoveLayer(itemUid, 10 );
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
@@ -219,7 +219,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
sContainerSys.Insert(sItemUid, container);
|
||||
|
||||
// Modify visibility layer so that the item does not get sent ot the player
|
||||
sEntManager.System<VisibilitySystem>().AddLayer(sItemUid, 10 );
|
||||
sEntManager.System<SharedVisibilitySystem>().AddLayer(sItemUid, 10 );
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
|
||||
128
Robust.UnitTesting/Shared/GameObjects/EntityManagerCopyTests.cs
Normal file
128
Robust.UnitTesting/Shared/GameObjects/EntityManagerCopyTests.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects;
|
||||
|
||||
[TestFixture]
|
||||
public sealed partial class EntityManagerCopyTests
|
||||
{
|
||||
[Test]
|
||||
public void CopyComponentGeneric()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
var targetComp = entManager.CopyComponent(original, target, comp);
|
||||
|
||||
Assert.That(targetComp!.Owner == target);
|
||||
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyComponentNonGeneric()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
var targetComp = entManager.CopyComponent(original, target, (IComponent) comp);
|
||||
|
||||
Assert.That(targetComp!.Owner == target);
|
||||
Assert.That(((AComponent) targetComp).Value, Is.EqualTo(comp.Value));
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyComponentMultiple()
|
||||
{
|
||||
var instant = RobustServerSimulation.NewSimulation();
|
||||
instant.RegisterComponents(fac =>
|
||||
{
|
||||
fac.RegisterClass<AComponent>();
|
||||
fac.RegisterClass<BComponent>();
|
||||
});
|
||||
|
||||
var sim = instant.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
var comp = entManager.AddComponent<AComponent>(original);
|
||||
var comp2 = entManager.AddComponent<BComponent>(original);
|
||||
|
||||
Assert.That(comp.Value, Is.EqualTo(false));
|
||||
comp.Value = true;
|
||||
|
||||
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(!entManager.HasComponent<AComponent>(target));
|
||||
|
||||
entManager.CopyComponents(original, target, null, comp, comp2);
|
||||
var targetComp = entManager.GetComponent<AComponent>(target);
|
||||
var targetComp2 = entManager.GetComponent<BComponent>(target);
|
||||
|
||||
Assert.That(targetComp!.Owner == target);
|
||||
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
|
||||
|
||||
Assert.That(targetComp2!.Owner == target);
|
||||
Assert.That(targetComp2.Value, Is.EqualTo(comp2.Value));
|
||||
|
||||
Assert.That(!ReferenceEquals(comp, targetComp));
|
||||
Assert.That(!ReferenceEquals(comp2, targetComp2));
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
private sealed partial class AComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public bool Value = false;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
private sealed partial class BComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public bool Value = false;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.UnitTesting.Server;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
[TestFixture, Parallelizable]
|
||||
sealed class EntityManagerTests
|
||||
sealed partial class EntityManagerTests
|
||||
{
|
||||
private static ISimulation SimulationFactory()
|
||||
{
|
||||
|
||||
@@ -104,4 +104,11 @@ public sealed class MultiRootGraphTest
|
||||
Assert.Throws<InvalidOperationException>(() => graph.Add(Id3, new[] { Id1 }));
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsOwnParentTest()
|
||||
{
|
||||
var graph = new MultiRootInheritanceGraph<string>();
|
||||
Assert.Throws<InvalidOperationException>(() => graph.Add(Id1, new []{ Id1 }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.UnitTesting.Shared.Resources
|
||||
_testDir = Directory.CreateDirectory(_testDirPath);
|
||||
var subDir = Path.Combine(_testDirPath, "writable");
|
||||
|
||||
_dirProvider = new WritableDirProvider(Directory.CreateDirectory(subDir));
|
||||
_dirProvider = new WritableDirProvider(Directory.CreateDirectory(subDir), hideRootDir: false);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
# schema file for Yamale
|
||||
meta:
|
||||
format: int()
|
||||
postmapinit: bool()
|
||||
postmapinit: bool(required=False)
|
||||
time: str(required=False) # timestamp() expects yyyy-mm-dd
|
||||
category: enum("Unknown", "Entity", "Grid", "Map", "Save", required=False) # FileCategory enum
|
||||
engineVersion: str(required=False)
|
||||
entityCount: int(required=False)
|
||||
forkId: str(required=False)
|
||||
forkVersion: str(required=False)
|
||||
tilemap: map(str(), key=int())
|
||||
orphans: list(int(), required=False)
|
||||
nullspace: list(int(), required=False)
|
||||
maps: list(int(), required=False)
|
||||
grids: list(int(), required=False)
|
||||
entities: list(include('proto'), min=1)
|
||||
---
|
||||
proto:
|
||||
@@ -14,64 +24,3 @@ entity:
|
||||
components: list(comp())
|
||||
missingComponents: list(str(), required=False)
|
||||
|
||||
# Example
|
||||
# meta:
|
||||
# format: 3
|
||||
# name: DemoStation
|
||||
# author: Space-Wizards
|
||||
# postmapinit: false
|
||||
# tilemap:
|
||||
# 0: space
|
||||
# 1: floor_asteroid_coarse_sand0
|
||||
# 2: floor_asteroid_coarse_sand1
|
||||
# 3: floor_asteroid_coarse_sand2
|
||||
# 4: floor_asteroid_coarse_sand_dug
|
||||
# 5: floor_asteroid_sand
|
||||
# 6: floor_asteroid_tile
|
||||
# 7: floor_blue
|
||||
# 8: floor_dark
|
||||
# 9: floor_elevator_shaft
|
||||
# 10: floor_freezer
|
||||
# 11: floor_glass
|
||||
# 12: floor_gold
|
||||
# 13: floor_green_circuit
|
||||
# 14: floor_hydro
|
||||
# 15: floor_lino
|
||||
# 16: floor_mono
|
||||
# 17: floor_reinforced
|
||||
# 18: floor_rglass
|
||||
# 19: floor_rock_vault
|
||||
# 20: floor_showroom
|
||||
# 21: floor_snow
|
||||
# 22: floor_steel
|
||||
# 23: floor_steel_dirty
|
||||
# 24: floor_techmaint
|
||||
# 25: floor_warning1
|
||||
# 26: floor_warning2
|
||||
# 27: floor_white
|
||||
# 28: floor_white_warning1
|
||||
# 29: floor_white_warning2
|
||||
# 30: floor_wood
|
||||
# 31: lattice
|
||||
# 32: plating
|
||||
# 33: plating
|
||||
# entities:
|
||||
# - uid: 0
|
||||
# components:
|
||||
# - parent: null
|
||||
# type: Transform
|
||||
# - index: 0
|
||||
# chunks:
|
||||
# - ind: "-1,-1"
|
||||
# tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgAAAA==
|
||||
# type: MapGrid
|
||||
# - linearDamping: 0.05
|
||||
# fixtures: []
|
||||
# bodyType: Dynamic
|
||||
# type: Physics
|
||||
# - uid: 1
|
||||
# type: SpawnPointLatejoin
|
||||
# components:
|
||||
# - parent: 0
|
||||
# pos: 0,0
|
||||
# type: Transform
|
||||
|
||||
Reference in New Issue
Block a user