mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
21e1b75f5d | ||
|
|
1f6ddd96a6 | ||
|
|
b4d2cd26aa | ||
|
|
ee6aee57bd | ||
|
|
ac1705e41f | ||
|
|
196a6047f1 | ||
|
|
b98b36a461 | ||
|
|
9980a98a94 | ||
|
|
5a1a1d0420 | ||
|
|
1c99678fe9 | ||
|
|
4049f10faa | ||
|
|
331c0bd43f | ||
|
|
604cd2f93d | ||
|
|
a87db2e57c | ||
|
|
4c7f0a8d6b | ||
|
|
9eaf52886a | ||
|
|
fce6f6c714 | ||
|
|
c04d51d489 | ||
|
|
d310871aa6 | ||
|
|
5fa422865f | ||
|
|
c137823355 | ||
|
|
0a0026b9ae | ||
|
|
97a2a5cfae | ||
|
|
a266e21d9e | ||
|
|
ad8bbe6401 | ||
|
|
71ce4749fe | ||
|
|
1e33c3c843 | ||
|
|
b3f3ca9725 | ||
|
|
33c37393ba | ||
|
|
ae36529744 | ||
|
|
ed2be48864 | ||
|
|
7ad5ce302e | ||
|
|
647f1a6808 |
@@ -6,6 +6,8 @@ using System.Threading;
|
||||
using NFluidsynth;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -61,6 +63,7 @@ namespace Robust.Client.Audio.Midi
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfgMan = default!;
|
||||
|
||||
private SharedBroadPhaseSystem _broadPhaseSystem = default!;
|
||||
|
||||
@@ -94,7 +97,7 @@ namespace Robust.Client.Audio.Midi
|
||||
if (MathHelper.CloseTo(_volume, value))
|
||||
return;
|
||||
|
||||
_volume = value;
|
||||
_cfgMan.SetCVar(CVars.MidiVolume, value);
|
||||
_volumeDirty = true;
|
||||
}
|
||||
}
|
||||
@@ -131,6 +134,12 @@ namespace Robust.Client.Audio.Midi
|
||||
{
|
||||
if (FluidsynthInitialized || _failedInitialize) return;
|
||||
|
||||
_cfgMan.OnValueChanged(CVars.MidiVolume, value =>
|
||||
{
|
||||
_volume = value;
|
||||
_volumeDirty = true;
|
||||
}, true);
|
||||
|
||||
_midiSawmill = Logger.GetSawmill("midi");
|
||||
_sawmill = Logger.GetSawmill("midi.fluidsynth");
|
||||
_loggerDelegate = LoggerDelegate;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using MidiEvent = NFluidsynth.MidiEvent;
|
||||
@@ -203,7 +204,9 @@ namespace Robust.Client.Audio.Midi
|
||||
private const int SampleRate = 44100;
|
||||
private const int Buffers = SampleRate / 2205;
|
||||
private readonly object _playerStateLock = new();
|
||||
private bool _debugEvents = false;
|
||||
private SequencerClientId _synthRegister;
|
||||
private SequencerClientId _debugRegister;
|
||||
public IClydeBufferedAudioSource Source { get; set; }
|
||||
IClydeBufferedAudioSource IMidiRenderer.Source => Source;
|
||||
|
||||
@@ -313,6 +316,7 @@ namespace Robust.Client.Audio.Midi
|
||||
_soundFontLoader = soundFontLoader;
|
||||
_synth = new Synth(_settings);
|
||||
_sequencer = new Sequencer(false);
|
||||
_debugRegister = _sequencer.RegisterClient("honk", DumpSequencerEvent);
|
||||
_synthRegister = _sequencer.RegisterFluidsynth(_synth);
|
||||
|
||||
_synth.AddSoundFontLoader(soundFontLoader);
|
||||
@@ -322,6 +326,27 @@ namespace Robust.Client.Audio.Midi
|
||||
Source.StartPlaying();
|
||||
}
|
||||
|
||||
private void DumpSequencerEvent(uint time, SequencerEvent @event)
|
||||
{
|
||||
// ReSharper disable once UseStringInterpolation
|
||||
_midiSawmill.Debug(string.Format(
|
||||
"{0:D8}: {1} chan:{2:D2} key:{3:D5} bank:{4:D2} ctrl:{5:D5} dur:{6:D5} pitch:{7:D5} prog:{8:D3} val:{9:D5} vel:{10:D5}",
|
||||
time,
|
||||
@event.Type.ToString().PadLeft(22),
|
||||
@event.Channel,
|
||||
@event.Key,
|
||||
@event.Bank,
|
||||
@event.Control,
|
||||
@event.Duration,
|
||||
@event.Pitch,
|
||||
@event.Program,
|
||||
@event.Value,
|
||||
@event.Velocity));
|
||||
|
||||
@event.Dest = _synthRegister;
|
||||
_sequencer.SendNow(@event);
|
||||
}
|
||||
|
||||
public bool OpenInput()
|
||||
{
|
||||
if (Disposed)
|
||||
@@ -428,9 +453,6 @@ namespace Robust.Client.Audio.Midi
|
||||
{
|
||||
if (Disposed) return;
|
||||
|
||||
// SSE needs this.
|
||||
DebugTools.Assert(length % 4 == 0, "Sample length must be multiple of 4");
|
||||
|
||||
var buffersProcessed = Source.GetNumberOfBuffersProcessed();
|
||||
if(buffersProcessed == Buffers) _midiSawmill.Warning("MIDI buffer overflow!");
|
||||
if (buffersProcessed == 0) return;
|
||||
@@ -445,36 +467,16 @@ namespace Robust.Client.Audio.Midi
|
||||
Source.GetBuffersProcessed(buffers);
|
||||
|
||||
lock (_playerStateLock)
|
||||
{
|
||||
// _sequencer.Process(10);
|
||||
_synth?.WriteSampleFloat(length * buffers.Length, audio, 0, Mono ? 1 : 2,
|
||||
audio, Mono ? length * buffers.Length : 1, Mono ? 1 : 2);
|
||||
|
||||
}
|
||||
if (Mono) // Turn audio to mono
|
||||
{
|
||||
var l = length * buffers.Length;
|
||||
|
||||
if (Sse.IsSupported)
|
||||
{
|
||||
fixed (float* ptr = audio)
|
||||
{
|
||||
for (var j = 0; j < l; j += 4)
|
||||
{
|
||||
var k = j + l;
|
||||
|
||||
var jV = Sse.LoadVector128(ptr + j);
|
||||
var kV = Sse.LoadVector128(ptr + k);
|
||||
|
||||
Sse.Store(j + ptr, Sse.Add(jV, kV));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var j = 0; j < l; j++)
|
||||
{
|
||||
var k = j + l;
|
||||
audio[j] = ((audio[k] + audio[j]));
|
||||
}
|
||||
}
|
||||
NumericsHelpers.Add(audio[..l], audio[l..]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < buffers.Length; i++)
|
||||
@@ -532,16 +534,16 @@ namespace Robust.Client.Audio.Midi
|
||||
lock(_playerStateLock)
|
||||
switch (midiEvent.Type)
|
||||
{
|
||||
// Note On 0x80
|
||||
case 144:
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
|
||||
break;
|
||||
|
||||
// Note Off - 0x90
|
||||
// Note Off - 0x80
|
||||
case 128:
|
||||
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
|
||||
break;
|
||||
|
||||
// Note On 0x90
|
||||
case 144:
|
||||
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
|
||||
break;
|
||||
|
||||
// After Touch - 0xA
|
||||
case 160:
|
||||
_synth.KeyPressure(midiEvent.Channel, midiEvent.Key, midiEvent.Value);
|
||||
@@ -576,6 +578,12 @@ namespace Robust.Client.Audio.Midi
|
||||
case 81:
|
||||
// System Messages - 0xF0
|
||||
case 240:
|
||||
switch (midiEvent.Control)
|
||||
{
|
||||
case 11:
|
||||
_synth.AllNotesOff(midiEvent.Channel);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
@@ -597,7 +605,7 @@ namespace Robust.Client.Audio.Midi
|
||||
if (Disposed) return;
|
||||
|
||||
var seqEv = (SequencerEvent) midiEvent;
|
||||
seqEv.Dest = _synthRegister;
|
||||
seqEv.Dest = _debugEvents ? _debugRegister : _synthRegister;
|
||||
_sequencer.SendAt(seqEv, time, absolute);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Robust.Client
|
||||
public bool Launcher { get; }
|
||||
public string? Username { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> CVars { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
|
||||
|
||||
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
|
||||
// Also I don't like spending 100ms parsing command line args. Do you?
|
||||
@@ -31,6 +32,7 @@ namespace Robust.Client
|
||||
var launcher = false;
|
||||
string? username = null;
|
||||
var cvars = new List<(string, string)>();
|
||||
var logLevels = new List<(string, string)>();
|
||||
var mountOptions = new MountOptions();
|
||||
|
||||
using var enumerator = args.GetEnumerator();
|
||||
@@ -124,6 +126,26 @@ namespace Robust.Client
|
||||
|
||||
mountOptions.DirMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--loglevel")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing loglevel sawmill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var loglevel = enumerator.Current;
|
||||
DebugTools.AssertNotNull(loglevel);
|
||||
var pos = loglevel.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in loglevel.");
|
||||
return false;
|
||||
}
|
||||
|
||||
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
|
||||
}
|
||||
else if (arg == "--help")
|
||||
{
|
||||
PrintHelp();
|
||||
@@ -142,6 +164,7 @@ namespace Robust.Client
|
||||
launcher,
|
||||
username,
|
||||
cvars,
|
||||
logLevels,
|
||||
connectAddress,
|
||||
ss14Address,
|
||||
mountOptions);
|
||||
@@ -162,6 +185,7 @@ Options:
|
||||
--launcher Run in launcher mode (no main menu, auto connect).
|
||||
--username Override username.
|
||||
--cvar Specifies an additional cvar overriding the config file. Syntax is <key>=<value>
|
||||
--loglevel Specifies an additional sawmill log level overriding the default values. Syntax is <key>=<value>
|
||||
--mount-dir Resource directory to mount.
|
||||
--mount-zip Resource zip to mount.
|
||||
--help Display this help text and exit.
|
||||
@@ -175,6 +199,7 @@ Options:
|
||||
bool launcher,
|
||||
string? username,
|
||||
IReadOnlyCollection<(string key, string value)> cVars,
|
||||
IReadOnlyCollection<(string key, string value)> logLevels,
|
||||
string connectAddress, string? ss14Address,
|
||||
MountOptions mountOptions)
|
||||
{
|
||||
@@ -184,6 +209,7 @@ Options:
|
||||
Launcher = launcher;
|
||||
Username = username;
|
||||
CVars = cVars;
|
||||
LogLevels = logLevels;
|
||||
ConnectAddress = connectAddress;
|
||||
Ss14Address = ss14Address;
|
||||
MountOptions = mountOptions;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Robust.Client.Console
|
||||
Message = message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="IClientConsoleHost" />
|
||||
internal class ClientConsoleHost : ConsoleHost, IClientConsoleHost
|
||||
{
|
||||
@@ -45,9 +45,9 @@ namespace Robust.Client.Console
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(HandleConCmdReg);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(HandleConCmdAck);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
|
||||
|
||||
Reset();
|
||||
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
|
||||
|
||||
@@ -17,11 +17,11 @@ namespace Robust.Client.Console
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>(MsgScriptStop.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>(MsgScriptEval.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>(MsgScriptStart.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>(MsgScriptResponse.NAME, ReceiveScriptResponse);
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>(MsgScriptStartAck.NAME, ReceiveScriptStartAckResponse);
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>();
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>();
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>();
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>(ReceiveScriptResponse);
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>(ReceiveScriptStartAckResponse);
|
||||
}
|
||||
|
||||
private void ReceiveScriptStartAckResponse(MsgScriptStartAck message)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
@@ -23,7 +23,7 @@ namespace Robust.Client.Debugging
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_entityManager, _eyeManager));
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _eyeManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -171,7 +171,7 @@ namespace Robust.Client.Debugging
|
||||
// all entities have a TransformComponent
|
||||
var transform = physBody.Owner.Transform;
|
||||
|
||||
var worldBox = physBody.GetWorldAABB(_mapManager);
|
||||
var worldBox = physBody.GetWorldAABB();
|
||||
if (worldBox.IsEmpty()) continue;
|
||||
|
||||
foreach (var fixture in physBody.Fixtures)
|
||||
@@ -270,14 +270,14 @@ namespace Robust.Client.Debugging
|
||||
|
||||
private sealed class EntityPositionOverlay : Overlay
|
||||
{
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IEntityLookup _lookup;
|
||||
private readonly IEyeManager _eyeManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager)
|
||||
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_lookup = lookup;
|
||||
_eyeManager = eyeManager;
|
||||
}
|
||||
|
||||
@@ -286,18 +286,17 @@ namespace Robust.Client.Debugging
|
||||
const float stubLength = 0.25f;
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
foreach (var entity in _entityManager.GetEntities())
|
||||
var viewport = _eyeManager.GetWorldViewport();
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
|
||||
{
|
||||
var transform = entity.Transform;
|
||||
if (transform.MapID != _eyeManager.CurrentMap ||
|
||||
!_eyeManager.GetWorldViewport().Contains(transform.WorldPosition))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var center = transform.WorldPosition;
|
||||
var xLine = transform.WorldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = transform.WorldRotation.RotateVec(Vector2.UnitY);
|
||||
var worldRotation = transform.WorldRotation;
|
||||
|
||||
var xLine = worldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = worldRotation.RotateVec(Vector2.UnitY);
|
||||
|
||||
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
|
||||
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
{
|
||||
@@ -54,7 +54,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgRay>(MsgRay.NAME, HandleDrawRay);
|
||||
_net.RegisterNetMessage<MsgRay>(HandleDrawRay);
|
||||
}
|
||||
|
||||
private void HandleDrawRay(MsgRay msg)
|
||||
|
||||
@@ -202,6 +202,26 @@ namespace Robust.Client
|
||||
|
||||
SetupLogging(_logManager, logHandlerFactory ?? (() => new ConsoleLogHandler()));
|
||||
|
||||
if (_commandLineArgs != null)
|
||||
{
|
||||
foreach (var (sawmill, level) in _commandLineArgs.LogLevels)
|
||||
{
|
||||
LogLevel? logLevel;
|
||||
if (level == "null")
|
||||
logLevel = null;
|
||||
else
|
||||
{
|
||||
if (!Enum.TryParse<LogLevel>(level, out var result))
|
||||
{
|
||||
System.Console.WriteLine($"LogLevel {level} does not exist!");
|
||||
continue;
|
||||
}
|
||||
logLevel = result;
|
||||
}
|
||||
_logManager.GetSawmill(sawmill).Level = logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out user data directory.
|
||||
var userDataDir = GetUserDataDir();
|
||||
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Prometheus;
|
||||
using Robust.Client.GameStates;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -67,7 +62,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void SetupNetworking()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
|
||||
}
|
||||
|
||||
public override void TickUpdate(float frameTime, Histogram? histogram)
|
||||
|
||||
@@ -1359,18 +1359,6 @@ namespace Robust.Client.GameObjects
|
||||
return texture;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
var map = Owner.Transform.MapID;
|
||||
if (map != MapId.Nullspace)
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
|
||||
new RenderTreeRemoveSpriteEvent(this, map));
|
||||
}
|
||||
}
|
||||
|
||||
public void FrameUpdate(float delta)
|
||||
{
|
||||
foreach (var t in Layers)
|
||||
|
||||
@@ -254,7 +254,7 @@ namespace Robust.Client.GameObjects
|
||||
if (!source.SetPosition(entity.Transform.WorldPosition))
|
||||
{
|
||||
source.Dispose();
|
||||
Logger.Warning("Can't play positional audio, can't set position.");
|
||||
Logger.Warning($"Can't play positional audio \"{stream.Name}\", can't set position.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Robust.Client.GameObjects
|
||||
if (!source.SetPosition(coordinates.ToMapPos(EntityManager)))
|
||||
{
|
||||
source.Dispose();
|
||||
Logger.Warning("Can't play positional audio, can't set position.");
|
||||
Logger.Warning("Can't play positional audio \"{stream.Name}\", can't set position.");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
#if DEBUG
|
||||
using System;
|
||||
using System.Text;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
internal sealed class DebugGridTileLookupSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
set
|
||||
{
|
||||
if (_enabled == value) return;
|
||||
_enabled = value;
|
||||
|
||||
if (_enabled)
|
||||
{
|
||||
_label.Visible = true;
|
||||
LastTile = default;
|
||||
}
|
||||
else
|
||||
{
|
||||
_label.Text = null;
|
||||
_label.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _enabled;
|
||||
|
||||
private (GridId Grid, Vector2i Indices) LastTile;
|
||||
|
||||
// Label and shit that follows cursor
|
||||
private Label _label = new()
|
||||
{
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeNetworkEvent<SendGridTileLookupMessage>(HandleSentEntities);
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_label);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
UnsubscribeNetworkEvent<SendGridTileLookupMessage>();
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.RemoveChild(_label);
|
||||
}
|
||||
|
||||
private void RequestEntities(GridId gridId, Vector2i indices)
|
||||
{
|
||||
if (gridId == GridId.Invalid) return;
|
||||
RaiseNetworkEvent(new RequestGridTileLookupMessage(gridId, indices));
|
||||
}
|
||||
|
||||
private void HandleSentEntities(SendGridTileLookupMessage message)
|
||||
{
|
||||
if (!Enabled) return;
|
||||
var text = new StringBuilder();
|
||||
text.AppendLine($"GridId: {LastTile.Grid}, Tile: {LastTile.Indices}");
|
||||
|
||||
for (var i = 0; i < message.Entities.Count; i++)
|
||||
{
|
||||
var uid = message.Entities[i];
|
||||
|
||||
if (!EntityManager.TryGetEntity(uid, out var entity)) continue;
|
||||
|
||||
text.AppendLine(entity.ToString());
|
||||
}
|
||||
|
||||
_label.Text = text.ToString();
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
if (!Enabled) return;
|
||||
|
||||
var mousePos = _inputManager.MouseScreenPosition;
|
||||
var worldPos = _eyeManager.ScreenToMap(mousePos);
|
||||
|
||||
GridId gridId;
|
||||
Vector2i tile;
|
||||
|
||||
if (_mapManager.TryFindGridAt(worldPos, out var grid))
|
||||
{
|
||||
gridId = grid.Index;
|
||||
tile = grid.WorldToTile(worldPos.Position);
|
||||
}
|
||||
else
|
||||
{
|
||||
gridId = GridId.Invalid;
|
||||
tile = new Vector2i((int) MathF.Floor(worldPos.Position.X), (int) MathF.Floor(worldPos.Position.Y));
|
||||
}
|
||||
|
||||
LayoutContainer.SetPosition(_label, mousePos.Position);
|
||||
|
||||
if ((gridId, tile).Equals(LastTile)) return;
|
||||
|
||||
_label.Text = null;
|
||||
LastTile = (gridId, tile);
|
||||
RequestEntities(gridId, tile);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class RequestTileEntities : IConsoleCommand
|
||||
{
|
||||
public string Command => "tilelookup";
|
||||
public string Description => "Used for debugging GridTileLookupSystem";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DebugGridTileLookupSystem>().Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -50,10 +50,13 @@ namespace Robust.Client.GameObjects
|
||||
_mapManager.OnGridCreated += MapManagerOnGridCreated;
|
||||
_mapManager.OnGridRemoved += MapManagerOnGridRemoved;
|
||||
|
||||
// Due to how recursion works, this must be done.
|
||||
SubscribeLocalEvent<MoveEvent>(AnythingMoved);
|
||||
|
||||
SubscribeLocalEvent<SpriteComponent, EntMapIdChangedMessage>(SpriteMapChanged);
|
||||
SubscribeLocalEvent<SpriteComponent, MoveEvent>(SpriteMoved);
|
||||
SubscribeLocalEvent<SpriteComponent, EntParentChangedMessage>(SpriteParentChanged);
|
||||
SubscribeLocalEvent<SpriteComponent, RenderTreeRemoveSpriteEvent>(RemoveSprite);
|
||||
SubscribeLocalEvent<SpriteComponent, ComponentRemove>(RemoveSprite);
|
||||
|
||||
SubscribeLocalEvent<PointLightComponent, EntMapIdChangedMessage>(LightMapChanged);
|
||||
SubscribeLocalEvent<PointLightComponent, MoveEvent>(LightMoved);
|
||||
@@ -62,6 +65,26 @@ namespace Robust.Client.GameObjects
|
||||
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
|
||||
}
|
||||
|
||||
private void AnythingMoved(MoveEvent args)
|
||||
{
|
||||
AnythingMovedSubHandler(args.Sender.Transform);
|
||||
}
|
||||
|
||||
private void AnythingMovedSubHandler(ITransformComponent sender)
|
||||
{
|
||||
// This recursive search is needed, as MoveEvent is defined to not care about indirect events like children.
|
||||
// WHATEVER YOU DO, DON'T REPLACE THIS WITH SPAMMING EVENTS UNLESS YOU HAVE A GUARANTEE IT WON'T LAG THE GC.
|
||||
// (Struct-based events ok though)
|
||||
if (sender.Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
QueueSpriteUpdate(sprite);
|
||||
if (sender.Owner.TryGetComponent(out PointLightComponent? light))
|
||||
QueueLightUpdate(light);
|
||||
foreach (ITransformComponent child in sender.Children)
|
||||
{
|
||||
AnythingMovedSubHandler(child);
|
||||
}
|
||||
}
|
||||
|
||||
// For the RemoveX methods
|
||||
// If the Transform is removed BEFORE the Sprite/Light,
|
||||
// then the MapIdChanged code will handle and remove it (because MapId gets set to nullspace).
|
||||
@@ -83,7 +106,7 @@ namespace Robust.Client.GameObjects
|
||||
QueueSpriteUpdate(component);
|
||||
}
|
||||
|
||||
private void RemoveSprite(EntityUid uid, SpriteComponent component, RenderTreeRemoveSpriteEvent args)
|
||||
private void RemoveSprite(EntityUid uid, SpriteComponent component, ComponentRemove args)
|
||||
{
|
||||
ClearSprite(component);
|
||||
}
|
||||
@@ -380,18 +403,6 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
internal class RenderTreeRemoveSpriteEvent : EntityEventArgs
|
||||
{
|
||||
public RenderTreeRemoveSpriteEvent(SpriteComponent sprite, MapId map)
|
||||
{
|
||||
Sprite = sprite;
|
||||
Map = map;
|
||||
}
|
||||
|
||||
public SpriteComponent Sprite { get; }
|
||||
public MapId Map { get; }
|
||||
}
|
||||
|
||||
internal class RenderTreeRemoveLightEvent : EntityEventArgs
|
||||
{
|
||||
public RenderTreeRemoveLightEvent(PointLightComponent light, MapId map)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// ReSharper disable once RedundantUsingDirective
|
||||
// Used in EXCEPTION_TOLERANCE preprocessor
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@@ -5,19 +7,18 @@ using System.Linq;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Map;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Timing;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -80,8 +81,8 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
_processor = new GameStateProcessor(_timing);
|
||||
|
||||
_network.RegisterNetMessage<MsgState>(MsgState.NAME, HandleStateMessage);
|
||||
_network.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME);
|
||||
_network.RegisterNetMessage<MsgState>(HandleStateMessage);
|
||||
_network.RegisterNetMessage<MsgStateAck>();
|
||||
_client.RunLevelChanged += RunLevelChanged;
|
||||
|
||||
_config.OnValueChanged(CVars.NetInterp, b => _processor.Interpolation = b, true);
|
||||
|
||||
@@ -44,8 +44,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
internal bool IsEfxSupported;
|
||||
|
||||
private ISawmill _openALSawmill = default!;
|
||||
|
||||
private void _initializeAudio()
|
||||
{
|
||||
_openALSawmill = Logger.GetSawmill("clyde.oal");
|
||||
|
||||
_audioOpenDevice();
|
||||
|
||||
// Create OpenAL context.
|
||||
@@ -74,9 +78,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_alContextExtensions.Add(extension);
|
||||
}
|
||||
|
||||
Logger.DebugS("clyde.oal", "OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
|
||||
Logger.DebugS("clyde.oal", "OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
|
||||
Logger.DebugS("clyde.oal", "OpenAL Version: {0}", AL.Get(ALGetString.Version));
|
||||
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
|
||||
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
|
||||
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
|
||||
}
|
||||
|
||||
private void _audioOpenDevice()
|
||||
@@ -89,7 +93,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_openALDevice = ALC.OpenDevice(preferredDevice);
|
||||
if (_openALDevice == IntPtr.Zero)
|
||||
{
|
||||
Logger.WarningS("clyde.oal", "Unable to open preferred audio device '{0}': {1}. Falling back default.",
|
||||
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
|
||||
preferredDevice, ALC.GetError(ALDevice.Null));
|
||||
|
||||
_openALDevice = ALC.OpenDevice(null);
|
||||
@@ -153,7 +157,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Clear out finalized audio sources.
|
||||
while (_sourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
Logger.DebugS("clyde.oal", "Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
@@ -163,7 +167,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Clear out finalized buffered audio sources.
|
||||
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
|
||||
{
|
||||
Logger.DebugS("clyde.oal", "Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
|
||||
if (IsEfxSupported) RemoveEfx(handles);
|
||||
AL.DeleteSource(handles.sourceHandle);
|
||||
_checkAlError();
|
||||
@@ -211,24 +215,24 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return audioSource;
|
||||
}
|
||||
|
||||
private static void _checkAlcError(ALDevice device,
|
||||
private void _checkAlcError(ALDevice device,
|
||||
[CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = ALC.GetError(device);
|
||||
if (error != AlcError.NoError)
|
||||
{
|
||||
Logger.ErrorS("clyde.oal", "[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
|
||||
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _checkAlError([CallerMemberName] string callerMember = "",
|
||||
private void _checkAlError([CallerMemberName] string callerMember = "",
|
||||
[CallerLineNumber] int callerLineNumber = -1)
|
||||
{
|
||||
var error = AL.GetError();
|
||||
if (error != ALError.NoError)
|
||||
{
|
||||
Logger.ErrorS("clyde.oal", "[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
|
||||
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,6 +334,35 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return new AudioStream(handle, length, wav.NumChannels, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
var fmt = channels switch
|
||||
{
|
||||
1 => ALFormat.Mono16,
|
||||
2 => ALFormat.Stereo16,
|
||||
_ => throw new ArgumentOutOfRangeException(
|
||||
nameof(channels), "Only stereo and mono is currently supported")
|
||||
};
|
||||
|
||||
var buffer = AL.GenBuffer();
|
||||
_checkAlError();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (short* ptr = samples)
|
||||
{
|
||||
AL.BufferData(buffer, fmt, (IntPtr) ptr, samples.Length * sizeof(short), sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
|
||||
var handle = new ClydeHandle(_audioSampleBuffers.Count);
|
||||
var length = TimeSpan.FromSeconds((double) samples.Length / channels / sampleRate);
|
||||
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
|
||||
return new AudioStream(handle, length, channels, name);
|
||||
}
|
||||
|
||||
private sealed class LoadedAudioSample
|
||||
{
|
||||
public readonly int BufferHandle;
|
||||
@@ -381,14 +414,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.SourcePlay(SourceHandle);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void StopPlaying()
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.SourceStop(SourceHandle);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public bool IsPlaying
|
||||
@@ -407,14 +440,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.GetSource(SourceHandle, ALSourceb.Looping, out var ret);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
return ret;
|
||||
}
|
||||
set
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourceb.Looping, value);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +455,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourceb.SourceRelative, true);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolume(float decibels)
|
||||
@@ -436,7 +469,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
_gain = MathF.Pow(10, decibels / 10);
|
||||
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
{
|
||||
_checkDisposed();
|
||||
var priorOcclusion = 1f;
|
||||
if (!IsEfxSupported)
|
||||
{
|
||||
AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain);
|
||||
priorOcclusion = priorGain / _gain;
|
||||
}
|
||||
_gain = scale;
|
||||
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
@@ -453,7 +500,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
gain *= gain * gain;
|
||||
AL.Source(SourceHandle, ALSourcef.Gain, _gain * gain);
|
||||
}
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
private void SetOcclusionEfx(float gain, float cutoff)
|
||||
@@ -473,7 +520,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourcef.SecOffset, seconds);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public bool SetPosition(Vector2 position)
|
||||
@@ -499,7 +546,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
#endif
|
||||
|
||||
AL.Source(SourceHandle, ALSource3f.Position, x, y, 0);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -526,14 +573,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
AL.Source(SourceHandle, ALSource3f.Velocity, x, y, 0);
|
||||
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
{
|
||||
_checkDisposed();
|
||||
AL.Source(SourceHandle, ALSourcef.Pitch, pitch);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
~AudioSource()
|
||||
@@ -559,7 +606,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
if (FilterHandle != 0) EFX.DeleteFilter(FilterHandle);
|
||||
AL.DeleteSource(SourceHandle);
|
||||
_master._audioSources.Remove(SourceHandle);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
SourceHandle = -1;
|
||||
@@ -609,7 +656,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.SourcePlay(stackalloc int[] {SourceHandle!.Value});
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void StopPlaying()
|
||||
@@ -617,7 +664,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.SourceStop(SourceHandle!.Value);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public bool IsPlaying
|
||||
@@ -643,7 +690,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_mono = false;
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSourceb.SourceRelative, true);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetLooping()
|
||||
@@ -662,7 +709,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
_gain = MathF.Pow(10, decibels / 10);
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
{
|
||||
_checkDisposed();
|
||||
var priorOcclusion = 1f;
|
||||
if (!IsEfxSupported)
|
||||
{
|
||||
AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain);
|
||||
priorOcclusion = priorGain / _gain;
|
||||
}
|
||||
_gain = scale;
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
@@ -680,7 +741,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Gain, gain * _gain);
|
||||
}
|
||||
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
private void SetOcclusionEfx(float gain, float cutoff)
|
||||
@@ -700,7 +761,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.SecOffset, seconds);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public bool SetPosition(Vector2 position)
|
||||
@@ -717,7 +778,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_mono = true;
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSource3f.Position, x, y, 0);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -744,7 +805,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
AL.Source(SourceHandle!.Value, ALSource3f.Velocity, x, y, 0);
|
||||
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
public void SetPitch(float pitch)
|
||||
@@ -752,7 +813,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
_checkDisposed();
|
||||
// ReSharper disable once PossibleInvalidOperationException
|
||||
AL.Source(SourceHandle!.Value, ALSourcef.Pitch, pitch);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
~BufferedAudioSource()
|
||||
@@ -783,7 +844,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
AL.DeleteSource(SourceHandle.Value);
|
||||
AL.DeleteBuffers(BufferHandles);
|
||||
_master._bufferedAudioSources.Remove(SourceHandle.Value);
|
||||
_checkAlError();
|
||||
_master._checkAlError();
|
||||
}
|
||||
|
||||
SourceHandle = null;
|
||||
|
||||
@@ -248,6 +248,12 @@ namespace Robust.Client.Graphics.Clyde
|
||||
return new(default, default, 1, name);
|
||||
}
|
||||
|
||||
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
|
||||
{
|
||||
// TODO: Might wanna actually load this so the length gets reported correctly.
|
||||
return new(default, default, channels, name);
|
||||
}
|
||||
|
||||
public IClydeAudioSource CreateAudioSource(AudioStream stream)
|
||||
{
|
||||
return DummyAudioSource.Instance;
|
||||
@@ -323,6 +329,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetVolumeDirect(float scale)
|
||||
{
|
||||
// Nada.
|
||||
}
|
||||
|
||||
public void SetOcclusion(float blocks)
|
||||
{
|
||||
// Nada.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Robust.Client.Audio;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
@@ -8,6 +9,7 @@ namespace Robust.Client.Graphics
|
||||
// AUDIO SYSTEM DOWN BELOW.
|
||||
AudioStream LoadAudioOggVorbis(Stream stream, string? name = null);
|
||||
AudioStream LoadAudioWav(Stream stream, string? name = null);
|
||||
AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null);
|
||||
|
||||
void SetMasterVolume(float newVolume);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Robust.Client.Graphics
|
||||
void SetPitch(float pitch);
|
||||
void SetGlobal();
|
||||
void SetVolume(float decibels);
|
||||
void SetVolumeDirect(float decibels);
|
||||
void SetOcclusion(float blocks);
|
||||
void SetPlaybackPosition(float seconds);
|
||||
void SetVelocity(Vector2 velocity);
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace Robust.Client.Placement
|
||||
{
|
||||
_drawingShader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
|
||||
NetworkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandlePlacementMessage);
|
||||
NetworkManager.RegisterNetMessage<MsgPlacement>(HandlePlacementMessage);
|
||||
|
||||
_modeDictionary.Clear();
|
||||
foreach (var type in ReflectionManager.GetAllChildren<PlacementMode>())
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -82,8 +81,8 @@ namespace Robust.Client.Player
|
||||
{
|
||||
_client.RunLevelChanged += OnRunLevelChanged;
|
||||
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME);
|
||||
_network.RegisterNetMessage<MsgPlayerList>(MsgPlayerList.NAME, HandlePlayerList);
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>();
|
||||
_network.RegisterNetMessage<MsgPlayerList>(HandlePlayerList);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Robust.Client.Prototypes
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, accept: NetMessageAccept.Server);
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(accept: NetMessageAccept.Server);
|
||||
|
||||
_clyde.OnWindowFocused += WindowFocusedChanged;
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
return;
|
||||
}
|
||||
|
||||
_label.Text = string.Join("\n", _inputManager.DownKeyFunctions);
|
||||
var functionsText = string.Join("\n", _inputManager.DownKeyFunctions);
|
||||
_label.Text = $"Context: {_inputManager.Contexts.ActiveContext.Name}\n{functionsText}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using NumberType = Robust.Client.ViewVariables.Editors.VVPropEditorNumeric.NumberType;
|
||||
using static Robust.Client.ViewVariables.Editors.VVPropEditorNumeric;
|
||||
|
||||
namespace Robust.Client.ViewVariables
|
||||
{
|
||||
@@ -43,17 +43,13 @@ namespace Robust.Client.ViewVariables
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>(MsgViewVariablesOpenSession.NAME,
|
||||
_netMessageOpenSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>(MsgViewVariablesRemoteData.NAME,
|
||||
_netMessageRemoteData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(MsgViewVariablesCloseSession.NAME,
|
||||
_netMessageCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>(MsgViewVariablesDenySession.NAME,
|
||||
_netMessageDenySession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>(MsgViewVariablesModifyRemote.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>(MsgViewVariablesReqSession.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>(MsgViewVariablesReqData.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>(_netMessageOpenSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>(_netMessageRemoteData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(_netMessageCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>(_netMessageDenySession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>();
|
||||
}
|
||||
|
||||
public VVPropEditor PropertyFor(Type? type)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime;
|
||||
@@ -217,6 +217,26 @@ namespace Robust.Server
|
||||
_log.RootSawmill.AddHandler(_logHandler!);
|
||||
}
|
||||
|
||||
if (_commandLineArgs != null)
|
||||
{
|
||||
foreach (var (sawmill, level) in _commandLineArgs.LogLevels)
|
||||
{
|
||||
LogLevel? logLevel;
|
||||
if (level == "null")
|
||||
logLevel = null;
|
||||
else
|
||||
{
|
||||
if (!Enum.TryParse<LogLevel>(level, out var result))
|
||||
{
|
||||
System.Console.WriteLine($"LogLevel {level} does not exist!");
|
||||
continue;
|
||||
}
|
||||
logLevel = result;
|
||||
}
|
||||
_log.GetSawmill(sawmill).Level = logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
SelfLog.Enable(s => { System.Console.WriteLine("SERILOG ERROR: {0}", s); });
|
||||
|
||||
if (!SetupLoki())
|
||||
@@ -330,6 +350,7 @@ namespace Robust.Server
|
||||
|
||||
_watchdogApi.Initialize();
|
||||
|
||||
AddFinalStringsToSerializer();
|
||||
_stringSerializer.LockStrings();
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _config.GetCVar(CVars.SysWinTickPeriod) >= 0)
|
||||
@@ -342,6 +363,16 @@ namespace Robust.Server
|
||||
return false;
|
||||
}
|
||||
|
||||
private void AddFinalStringsToSerializer()
|
||||
{
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
foreach (var regType in factory.AllRegisteredTypes)
|
||||
{
|
||||
var reg = factory.GetRegistration(regType);
|
||||
_stringSerializer.AddString(reg.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private bool SetupLoki()
|
||||
{
|
||||
var enabled = _config.GetCVar(CVars.LokiEnabled);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -12,6 +12,7 @@ namespace Robust.Server
|
||||
public string? ConfigFile { get; }
|
||||
public string? DataDir { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> CVars { get; }
|
||||
public IReadOnlyCollection<(string key, string value)> LogLevels { get; }
|
||||
|
||||
// Manual parser because C# has no good command line parsing libraries. Also dependencies bad.
|
||||
// Also I don't like spending 100ms parsing command line args. Do you?
|
||||
@@ -21,6 +22,7 @@ namespace Robust.Server
|
||||
string? configFile = null;
|
||||
string? dataDir = null;
|
||||
var cvars = new List<(string, string)>();
|
||||
var logLevels = new List<(string, string)>();
|
||||
var mountOptions = new MountOptions();
|
||||
|
||||
using var enumerator = args.GetEnumerator();
|
||||
@@ -93,13 +95,33 @@ namespace Robust.Server
|
||||
|
||||
mountOptions.DirMounts.Add(enumerator.Current);
|
||||
}
|
||||
else if (arg == "--loglevel")
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
C.WriteLine("Missing loglevel sawmill.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var loglevel = enumerator.Current;
|
||||
DebugTools.AssertNotNull(loglevel);
|
||||
var pos = loglevel.IndexOf('=');
|
||||
|
||||
if (pos == -1)
|
||||
{
|
||||
C.WriteLine("Expected = in loglevel.");
|
||||
return false;
|
||||
}
|
||||
|
||||
logLevels.Add((loglevel[..pos], loglevel[(pos + 1)..]));
|
||||
}
|
||||
else
|
||||
{
|
||||
C.WriteLine("Unknown argument: {0}", arg);
|
||||
}
|
||||
}
|
||||
|
||||
parsed = new CommandLineArgs(configFile, dataDir, cvars, mountOptions);
|
||||
parsed = new CommandLineArgs(configFile, dataDir, cvars, logLevels, mountOptions);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -110,17 +132,19 @@ Options:
|
||||
--config-file Path to the config file to read from.
|
||||
--data-dir Path to the data directory to read/write from/to.
|
||||
--cvar Specifies an additional cvar overriding the config file. Syntax is <key>=<value>
|
||||
--loglevel Specifies an additional sawmill log level overriding the default values. Syntax is <key>=<value>
|
||||
--mount-dir Resource directory to mount.
|
||||
--mount-zip Resource zip to mount.
|
||||
--help Display this help text and exit.
|
||||
");
|
||||
}
|
||||
|
||||
private CommandLineArgs(string? configFile, string? dataDir, IReadOnlyCollection<(string, string)> cVars, MountOptions mountOptions)
|
||||
private CommandLineArgs(string? configFile, string? dataDir, IReadOnlyCollection<(string, string)> cVars, IReadOnlyCollection<(string, string)> logLevels, MountOptions mountOptions)
|
||||
{
|
||||
ConfigFile = configFile;
|
||||
DataDir = dataDir;
|
||||
CVars = cVars;
|
||||
LogLevels = logLevels;
|
||||
MountOptions = mountOptions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,15 +62,14 @@ namespace Robust.Server.Console
|
||||
var sudoShell = new SudoShell(this, localShell, shell);
|
||||
ExecuteInShell(sudoShell, argStr.Substring("sudo ".Length));
|
||||
});
|
||||
|
||||
|
||||
LoadConsoleCommands();
|
||||
|
||||
// setup networking with clients
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
|
||||
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
|
||||
NetManager.RegisterNetMessage<MsgConCmdAck>();
|
||||
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
|
||||
message => HandleRegistrationRequest(message.MsgChannel));
|
||||
NetManager.RegisterNetMessage<MsgConCmdReg>(message => HandleRegistrationRequest(message.MsgChannel));
|
||||
}
|
||||
|
||||
private void ExecuteInShell(IConsoleShell shell, string command)
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Robust.Server.Debugging
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgRay>(MsgRay.NAME);
|
||||
_net.RegisterNetMessage<MsgRay>();
|
||||
// TODO _physics.DebugDrawRay += data => PhysicsOnDebugDrawRay(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,18 +9,8 @@ namespace Robust.Server.GameObjects
|
||||
{
|
||||
public override string Name => "Actor";
|
||||
|
||||
[ViewVariables] public IPlayerSession PlayerSession { get; internal set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
// Warning: careful here, Detach removes this component, make sure this is after the base shutdown
|
||||
// to prevent infinite recursion
|
||||
// ReSharper disable once ConstantConditionalAccessQualifier
|
||||
PlayerSession?.DetachFromEntity();
|
||||
}
|
||||
[ViewVariables]
|
||||
public IPlayerSession PlayerSession { get; internal set; } = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -52,26 +42,4 @@ namespace Robust.Server.GameObjects
|
||||
OldPlayer = oldPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerAttachSystemMessage : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachSystemMessage(IEntity entity, IPlayerSession newPlayer)
|
||||
{
|
||||
Entity = entity;
|
||||
NewPlayer = newPlayer;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
public IPlayerSession NewPlayer { get; }
|
||||
}
|
||||
|
||||
public class PlayerDetachedSystemMessage : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedSystemMessage(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
}
|
||||
}
|
||||
|
||||
173
Robust.Server/GameObjects/EntitySystems/ActorSystem.cs
Normal file
173
Robust.Server/GameObjects/EntitySystems/ActorSystem.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// System that handles players being attached/detached from entities.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class ActorSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AttachPlayerEvent>(OnActorPlayerAttach);
|
||||
SubscribeLocalEvent<ActorComponent, DetachPlayerEvent>(OnActorPlayerDetach);
|
||||
SubscribeLocalEvent<ActorComponent, ComponentShutdown>(OnActorShutdown);
|
||||
}
|
||||
|
||||
private void OnActorPlayerAttach(AttachPlayerEvent args)
|
||||
{
|
||||
// Cannot attach to a deleted entity.
|
||||
if (args.Entity.Deleted)
|
||||
{
|
||||
args.Result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var uid = args.Entity.Uid;
|
||||
|
||||
// Check if there was a player attached to the entity already...
|
||||
if (ComponentManager.TryGetComponent(uid, out ActorComponent actor))
|
||||
{
|
||||
// If we're not forcing the attach, this fails.
|
||||
if (!args.Force)
|
||||
{
|
||||
args.Result = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the event's force-kicked session before detaching it.
|
||||
args.ForceKicked = actor.PlayerSession;
|
||||
|
||||
// This detach cannot fail, as a player is attached to this entity.
|
||||
// It's important to note that detaching the player removes the component.
|
||||
RaiseLocalEvent(uid, new DetachPlayerEvent());
|
||||
}
|
||||
|
||||
// We add the actor component.
|
||||
actor = ComponentManager.AddComponent<ActorComponent>(args.Entity);
|
||||
actor.PlayerSession = args.Player;
|
||||
args.Player.SetAttachedEntity(args.Entity);
|
||||
args.Result = true;
|
||||
|
||||
// TODO: Remove component message.
|
||||
args.Entity.SendMessage(actor, new PlayerAttachedMsg(args.Player));
|
||||
|
||||
// The player is fully attached now, raise an event!
|
||||
RaiseLocalEvent(uid, new PlayerAttachedEvent(args.Entity, args.Player));
|
||||
}
|
||||
|
||||
private void OnActorPlayerDetach(EntityUid uid, ActorComponent component, DetachPlayerEvent args)
|
||||
{
|
||||
// Removing the component will call shutdown, and our subscription will handle the rest of the detach logic.
|
||||
ComponentManager.RemoveComponent<ActorComponent>(uid);
|
||||
args.Result = true;
|
||||
}
|
||||
|
||||
private void OnActorShutdown(EntityUid uid, ActorComponent component, ComponentShutdown args)
|
||||
{
|
||||
component.PlayerSession.SetAttachedEntity(null);
|
||||
|
||||
var entity = EntityManager.GetEntity(uid);
|
||||
|
||||
// TODO: Remove component message.
|
||||
entity.SendMessage(component, new PlayerDetachedMsg(component.PlayerSession));
|
||||
|
||||
// The player is fully detached now that the component has shut down.
|
||||
RaiseLocalEvent(uid, new PlayerDetachedEvent(entity, component.PlayerSession));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise this broadcast event to attach a player to an entity, optionally detaching the player attached to it.
|
||||
/// </summary>
|
||||
public class AttachPlayerEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Player to attach to the entity.
|
||||
/// Input parameter.
|
||||
/// </summary>
|
||||
public IPlayerSession Player { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity to attach the player to.
|
||||
/// Input parameter.
|
||||
/// </summary>
|
||||
public IEntity Entity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to force-attach the player,
|
||||
/// detaching any players attached to it if any.
|
||||
/// Input parameter.
|
||||
/// </summary>
|
||||
public bool Force { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If the attach was forced and there was a player attached to the entity before, this will be it.
|
||||
/// Output parameter.
|
||||
/// </summary>
|
||||
public IPlayerSession? ForceKicked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the player was attached correctly.
|
||||
/// False if not forcing and the entity already had a player attached to it.
|
||||
/// Output parameter.
|
||||
/// </summary>
|
||||
public bool Result { get; set; } = false;
|
||||
|
||||
public AttachPlayerEvent(IEntity entity, IPlayerSession player, bool force = false)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
Force = force;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raise this directed event to detach a player from an entity.
|
||||
/// </summary>
|
||||
public class DetachPlayerEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the player was detached correctly.
|
||||
/// Fails if no player was attached to the entity.
|
||||
/// Output parameter.
|
||||
/// </summary>
|
||||
public bool Result { get; set; } = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for when a player has been attached to an entity.
|
||||
/// </summary>
|
||||
public class PlayerAttachedEvent : EntityEventArgs
|
||||
{
|
||||
public IEntity Entity { get; }
|
||||
public IPlayerSession Player { get; }
|
||||
|
||||
public PlayerAttachedEvent(IEntity entity, IPlayerSession player)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event for when a player has been detached from an entity.
|
||||
/// </summary>
|
||||
public class PlayerDetachedEvent : EntityEventArgs
|
||||
{
|
||||
public IEntity Entity { get; }
|
||||
public IPlayerSession Player { get; }
|
||||
|
||||
public PlayerDetachedEvent(IEntity entity, IPlayerSession player)
|
||||
{
|
||||
Entity = entity;
|
||||
Player = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Robust.Server.GameObjects
|
||||
[UsedImplicitly]
|
||||
public sealed class GridTileLookupSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private readonly Dictionary<GridId, Dictionary<Vector2i, GridTileLookupChunk>> _graph =
|
||||
@@ -198,16 +199,16 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private Box2 GetEntityBox(IEntity entity)
|
||||
{
|
||||
// Need to clip the aabb as anything with an edge intersecting another tile might be picked up, such as walls.
|
||||
if (entity.TryGetComponent(out IPhysBody? physics))
|
||||
return new Box2(physics.GetWorldAABB().BottomLeft + 0.01f, physics.GetWorldAABB().TopRight - 0.01f);
|
||||
|
||||
// Don't want to accidentally get neighboring tiles unless we're near an edge
|
||||
return Box2.CenteredAround(entity.Transform.Coordinates.ToMapPos(EntityManager), Vector2.One / 2);
|
||||
var aabb = _lookup.GetWorldAabbFromEntity(entity);
|
||||
return aabb.IsEmpty() ? aabb : aabb.Enlarged(-0.1f);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
#if DEBUG
|
||||
SubscribeNetworkEvent<RequestGridTileLookupMessage>(HandleRequest);
|
||||
#endif
|
||||
SubscribeLocalEvent<MoveEvent>(HandleEntityMove);
|
||||
SubscribeLocalEvent<EntityInitializedMessage>(HandleEntityInitialized);
|
||||
SubscribeLocalEvent<EntityDeletedMessage>(HandleEntityDeleted);
|
||||
@@ -225,6 +226,14 @@ namespace Robust.Server.GameObjects
|
||||
_mapManager.TileChanged -= HandleTileChanged;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private void HandleRequest(RequestGridTileLookupMessage message, EntitySessionEventArgs args)
|
||||
{
|
||||
var entities = GetEntitiesIntersecting(message.GridId, message.Indices).Select(e => e.Uid).ToList();
|
||||
RaiseNetworkEvent(new SendGridTileLookupMessage(message.GridId, message.Indices, entities), args.SenderSession.ConnectedClient);
|
||||
}
|
||||
#endif
|
||||
|
||||
private void HandleEntityInitialized(EntityInitializedMessage message)
|
||||
{
|
||||
HandleEntityAdd(message.Entity);
|
||||
|
||||
@@ -9,7 +9,6 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -108,7 +107,7 @@ namespace Robust.Server.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void SetupNetworking()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
|
||||
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -63,8 +62,8 @@ namespace Robust.Server.GameStates
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgState>(MsgState.NAME);
|
||||
_networkManager.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME, HandleStateAck);
|
||||
_networkManager.RegisterNetMessage<MsgState>();
|
||||
_networkManager.RegisterNetMessage<MsgStateAck>(HandleStateAck);
|
||||
|
||||
_networkManager.Connected += HandleClientConnected;
|
||||
_networkManager.Disconnect += HandleClientDisconnect;
|
||||
@@ -211,7 +210,10 @@ namespace Robust.Server.GameStates
|
||||
_logger.Log(LogLevel.Error, e, string.Empty);
|
||||
}
|
||||
|
||||
return (new MsgState(session.ConnectedClient), null);
|
||||
var msg = _networkManager.CreateNetMessage<MsgState>();
|
||||
msg.MsgChannel = session.ConnectedClient;
|
||||
|
||||
return (msg, null);
|
||||
}
|
||||
|
||||
var mailBag = _playerManager.GetAllPlayers()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
@@ -34,7 +34,7 @@ namespace Robust.Server.Placement
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandleNetMessage);
|
||||
_networkManager.RegisterNetMessage<MsgPlacement>(HandleNetMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,8 +20,8 @@ namespace Robust.Server.Player
|
||||
/// NOTE: The content pack almost certainly has an alternative for this.
|
||||
/// Do not call this directly for most content code.
|
||||
/// </summary>
|
||||
/// <param name="a">The entity to attach to.</param>
|
||||
void AttachToEntity(IEntity? a);
|
||||
/// <param name="entity">The entity to attach to.</param>
|
||||
void AttachToEntity(IEntity? entity);
|
||||
|
||||
/// <summary>
|
||||
/// Detaches this player from an entity.
|
||||
@@ -36,5 +36,12 @@ namespace Robust.Server.Player
|
||||
/// Persistent data for this player.
|
||||
/// </summary>
|
||||
IPlayerData Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to set <see cref="ICommonSession.AttachedEntity"/> and update the player's status.
|
||||
/// Do NOT use this unless you know what you're doing, you probably want <see cref="AttachToEntity"/>
|
||||
/// and <see cref="DetachFromEntity"/> instead.
|
||||
/// </summary>
|
||||
internal void SetAttachedEntity(IEntity? entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ namespace Robust.Server.Player
|
||||
|
||||
MaxPlayers = maxPlayers;
|
||||
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME, HandlePlayerListReq);
|
||||
_network.RegisterNetMessage<MsgPlayerList>(MsgPlayerList.NAME);
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>(HandlePlayerListReq);
|
||||
_network.RegisterNetMessage<MsgPlayerList>();
|
||||
|
||||
_network.Connecting += OnConnecting;
|
||||
_network.Connected += NewSession;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Logger = Robust.Shared.Log.Logger;
|
||||
|
||||
namespace Robust.Server.Player
|
||||
{
|
||||
@@ -36,7 +37,7 @@ namespace Robust.Server.Player
|
||||
|
||||
[ViewVariables] public INetChannel ConnectedClient { get; }
|
||||
|
||||
[ViewVariables] public IEntity? AttachedEntity { get; private set; }
|
||||
[ViewVariables] public IEntity? AttachedEntity { get; set; }
|
||||
|
||||
[ViewVariables] public EntityUid? AttachedEntityUid => AttachedEntity?.Uid;
|
||||
|
||||
@@ -109,21 +110,21 @@ namespace Robust.Server.Player
|
||||
public event EventHandler<SessionStatusEventArgs>? PlayerStatusChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AttachToEntity(IEntity? a)
|
||||
public void AttachToEntity(IEntity? entity)
|
||||
{
|
||||
DetachFromEntity();
|
||||
|
||||
if (a == null)
|
||||
{
|
||||
if (entity == null)
|
||||
return;
|
||||
}
|
||||
|
||||
var actorComponent = a.AddComponent<ActorComponent>();
|
||||
actorComponent.PlayerSession = this;
|
||||
AttachedEntity = a;
|
||||
a.SendMessage(actorComponent, new PlayerAttachedMsg(this));
|
||||
a.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerAttachSystemMessage(a, this));
|
||||
UpdatePlayerState();
|
||||
// This event needs to be broadcast.
|
||||
var attachPlayer = new AttachPlayerEvent(entity, this);
|
||||
entity.EntityManager.EventBus.RaiseLocalEvent(entity.Uid, attachPlayer);
|
||||
|
||||
if (!attachPlayer.Result)
|
||||
{
|
||||
Logger.Warning($"Couldn't attach player \"{this}\" to entity \"{entity}\"! Did it have a player already attached to it?");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -132,22 +133,12 @@ namespace Robust.Server.Player
|
||||
if (AttachedEntity == null)
|
||||
return;
|
||||
|
||||
if (AttachedEntity.Deleted)
|
||||
{
|
||||
throw new InvalidOperationException("Tried to detach player, but my entity does not exist!");
|
||||
}
|
||||
var detachPlayer = new DetachPlayerEvent();
|
||||
AttachedEntity.EntityManager.EventBus.RaiseLocalEvent(AttachedEntity.Uid, detachPlayer, false);
|
||||
|
||||
if (AttachedEntity.TryGetComponent<ActorComponent>(out var actor))
|
||||
if (!detachPlayer.Result)
|
||||
{
|
||||
AttachedEntity.SendMessage(actor, new PlayerDetachedMsg(this));
|
||||
AttachedEntity.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerDetachedSystemMessage(AttachedEntity));
|
||||
AttachedEntity.RemoveComponent<ActorComponent>();
|
||||
AttachedEntity = null;
|
||||
UpdatePlayerState();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Tried to detach player, but entity does not have ActorComponent!");
|
||||
Logger.Warning($"Couldn't detach player \"{this}\" to entity \"{AttachedEntity}\"! Is it missing an ActorComponent?");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +181,13 @@ namespace Robust.Server.Player
|
||||
|
||||
public LoginType AuthType => ConnectedClient.AuthType;
|
||||
|
||||
/// <inheritdoc />
|
||||
void IPlayerSession.SetAttachedEntity(IEntity? entity)
|
||||
{
|
||||
AttachedEntity = entity;
|
||||
UpdatePlayerState();
|
||||
}
|
||||
|
||||
private void UpdatePlayerState()
|
||||
{
|
||||
PlayerState.Status = Status;
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.Server.Prototypes
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, HandleReloadPrototypes, NetMessageAccept.Server);
|
||||
_netManager.RegisterNetMessage<MsgReloadPrototypes>(HandleReloadPrototypes, NetMessageAccept.Server);
|
||||
}
|
||||
|
||||
private void HandleReloadPrototypes(MsgReloadPrototypes msg)
|
||||
|
||||
@@ -36,11 +36,11 @@ namespace Robust.Server.Scripting
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>(MsgScriptStop.NAME, ReceiveScriptEnd);
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>(MsgScriptEval.NAME, ReceiveScriptEval);
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>(MsgScriptStart.NAME, ReceiveScriptStart);
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>(MsgScriptResponse.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>(MsgScriptStartAck.NAME);
|
||||
_netManager.RegisterNetMessage<MsgScriptStop>(ReceiveScriptEnd);
|
||||
_netManager.RegisterNetMessage<MsgScriptEval>(ReceiveScriptEval);
|
||||
_netManager.RegisterNetMessage<MsgScriptStart>(ReceiveScriptStart);
|
||||
_netManager.RegisterNetMessage<MsgScriptResponse>();
|
||||
_netManager.RegisterNetMessage<MsgScriptStartAck>();
|
||||
|
||||
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using DenyReason = Robust.Shared.Network.Messages.MsgViewVariablesDenySession.DenyReason;
|
||||
using static Robust.Shared.Network.Messages.MsgViewVariablesDenySession;
|
||||
|
||||
namespace Robust.Server.ViewVariables
|
||||
{
|
||||
@@ -34,16 +34,13 @@ namespace Robust.Server.ViewVariables
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>(MsgViewVariablesReqSession.NAME,
|
||||
_msgReqSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>(MsgViewVariablesReqData.NAME, _msgReqData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>(MsgViewVariablesModifyRemote.NAME,
|
||||
_msgModifyRemote);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(MsgViewVariablesCloseSession.NAME,
|
||||
_msgCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>(MsgViewVariablesDenySession.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>(MsgViewVariablesOpenSession.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>(MsgViewVariablesRemoteData.NAME);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqSession>(_msgReqSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesReqData>(_msgReqData);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesModifyRemote>(_msgModifyRemote);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesCloseSession>(_msgCloseSession);
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesDenySession>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesOpenSession>();
|
||||
_netManager.RegisterNetMessage<MsgViewVariablesRemoteData>();
|
||||
}
|
||||
|
||||
private void _msgCloseSession(MsgViewVariablesCloseSession message)
|
||||
|
||||
@@ -463,5 +463,12 @@ namespace Robust.Shared
|
||||
/// </summary>
|
||||
public static readonly CVarDef<int> DebugTargetFps =
|
||||
CVarDef.Create("debug.target_fps", 60, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
|
||||
/*
|
||||
* MIDI
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<float> MidiVolume =
|
||||
CVarDef.Create("midi.volume", 0f, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Robust.Shared.Configuration
|
||||
_netManager.Disconnect += PeerDisconnected;
|
||||
}
|
||||
|
||||
_netManager.RegisterNetMessage<MsgConVars>(MsgConVars.NAME, HandleNetVarMessage);
|
||||
_netManager.RegisterNetMessage<MsgConVars>(HandleNetVarMessage);
|
||||
}
|
||||
|
||||
private void PeerConnected(object? sender, NetChannelArgs e)
|
||||
|
||||
@@ -197,6 +197,27 @@ namespace Robust.Shared.Containers
|
||||
return userContainer == otherContainer;
|
||||
}
|
||||
|
||||
public static bool IsInSameOrParentContainer(this IEntity user, IEntity other)
|
||||
{
|
||||
DebugTools.AssertNotNull(user);
|
||||
DebugTools.AssertNotNull(other);
|
||||
|
||||
var isUserContained = TryGetContainer(user, out var userContainer);
|
||||
var isOtherContained = TryGetContainer(other, out var otherContainer);
|
||||
|
||||
// Both entities are not in a container
|
||||
if (!isUserContained && !isOtherContained) return true;
|
||||
|
||||
// One contains the other
|
||||
if (userContainer?.Owner == other || otherContainer?.Owner == user) return true;
|
||||
|
||||
// Both entities are in different contained states
|
||||
if (isUserContained != isOtherContained) return false;
|
||||
|
||||
// Both entities are in the same container
|
||||
return userContainer == otherContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut method to make creation of containers easier.
|
||||
/// Creates a new container on the entity and gives it back to you.
|
||||
|
||||
@@ -106,6 +106,12 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
var asmName = reader.GetString(reader.GetAssemblyDefinition().Name);
|
||||
|
||||
if (peReader.PEHeaders.CorHeader?.ManagedNativeHeaderDirectory is {Size: not 0})
|
||||
{
|
||||
_sawmill.Error($"Assembly {asmName} contains native code.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (VerifyIL)
|
||||
{
|
||||
if (!DoVerifyIL(asmName, resolver, peReader, reader))
|
||||
@@ -404,7 +410,8 @@ namespace Robust.Shared.ContentPack
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTypeAccessAllowed(SandboxConfig sandboxConfig, MTypeReferenced type, [NotNullWhen(true)] out TypeConfig? cfg)
|
||||
private bool IsTypeAccessAllowed(SandboxConfig sandboxConfig, MTypeReferenced type,
|
||||
[NotNullWhen(true)] out TypeConfig? cfg)
|
||||
{
|
||||
if (type.Namespace == null)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,8 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
public IComponentFactory ComponentFactory => _componentFactory;
|
||||
|
||||
private const int TypeCapacity = 32;
|
||||
private const int ComponentCollectionCapacity = 1024;
|
||||
private const int EntityCapacity = 1024;
|
||||
|
||||
@@ -30,12 +30,6 @@ namespace Robust.Shared.GameObjects
|
||||
void CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold);
|
||||
}
|
||||
|
||||
public interface ICollideSpecial
|
||||
{
|
||||
[Obsolete("Use PreventCollideEvent instead")]
|
||||
bool PreventCollide(IPhysBody collidedwith);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum BodyStatus: byte
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
@@ -163,11 +164,11 @@ namespace Robust.Shared.GameObjects
|
||||
if (value)
|
||||
{
|
||||
_sleepTime = 0.0f;
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsWakeMessage(this));
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsWakeMessage(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsSleepMessage(this));
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsSleepMessage(this));
|
||||
ResetDynamics();
|
||||
_sleepTime = 0.0f;
|
||||
}
|
||||
@@ -261,10 +262,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
fixture.Body = this;
|
||||
fixture.ComputeProperties();
|
||||
if (string.IsNullOrEmpty(fixture.Name))
|
||||
{
|
||||
fixture.Name = GetFixtureName(fixture);
|
||||
}
|
||||
fixture.ID = GetFixtureName(fixture);
|
||||
}
|
||||
|
||||
ResetMassData();
|
||||
@@ -304,60 +302,76 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// We will pray that this deferred joint is handled properly.
|
||||
|
||||
// TODO: Crude as FUCK diffing here as well, fine for now.
|
||||
/*
|
||||
* -- Joints --
|
||||
*/
|
||||
|
||||
var existingJoints = new List<Joint>();
|
||||
// TODO: Iterating like this is inefficient and bloated as fuck but on the other hand the linked-list is very convenient
|
||||
// for bodies with a large number of fixtures / joints.
|
||||
// Probably store them in Dictionaries but still store the linked-list stuff on the fixture / joint itself.
|
||||
var existingJoints = Joints.ToList();
|
||||
var toAddJoints = new List<Joint>();
|
||||
var toRemoveJoints = new List<Joint>();
|
||||
|
||||
for (var je = JointEdges; je != null; je = je.Next)
|
||||
foreach (var newJoint in newState.Joints)
|
||||
{
|
||||
existingJoints.Add(je.Joint);
|
||||
}
|
||||
var jointFound = false;
|
||||
|
||||
var jointsDiff = true;
|
||||
|
||||
if (existingJoints.Count == newState.Joints.Count)
|
||||
{
|
||||
var anyDiff = false;
|
||||
for (var i = 0; i < existingJoints.Count; i++)
|
||||
foreach (var joint in existingJoints)
|
||||
{
|
||||
var existing = existingJoints[i];
|
||||
var newJoint = newState.Joints[i];
|
||||
|
||||
if (!existing.Equals(newJoint))
|
||||
if (joint.ID.Equals(newJoint.ID))
|
||||
{
|
||||
anyDiff = true;
|
||||
if (!newJoint.Equals(joint) && TrySetupNetworkedJoint(newJoint))
|
||||
{
|
||||
toAddJoints.Add(newJoint);
|
||||
toRemoveJoints.Add(joint);
|
||||
}
|
||||
|
||||
jointFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyDiff)
|
||||
if (!jointFound && TrySetupNetworkedJoint(newJoint))
|
||||
{
|
||||
jointsDiff = false;
|
||||
toAddJoints.Add(newJoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (jointsDiff)
|
||||
foreach (var joint in existingJoints)
|
||||
{
|
||||
ClearJoints();
|
||||
var jointFound = false;
|
||||
|
||||
foreach (var joint in newState.Joints)
|
||||
foreach (var newJoint in newState.Joints)
|
||||
{
|
||||
joint.EdgeA = new JointEdge();
|
||||
joint.EdgeB = new JointEdge();
|
||||
// Defer joints given it relies on 2 bodies.
|
||||
AddJoint(joint);
|
||||
if (joint.ID.Equals(newJoint.ID))
|
||||
{
|
||||
jointFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (jointFound) continue;
|
||||
|
||||
toRemoveJoints.Add(joint);
|
||||
}
|
||||
|
||||
foreach (var joint in toRemoveJoints)
|
||||
{
|
||||
RemoveJoint(joint);
|
||||
}
|
||||
|
||||
foreach (var joint in toAddJoints)
|
||||
{
|
||||
AddJoint(joint);
|
||||
}
|
||||
|
||||
/*
|
||||
* -- Fixtures --
|
||||
*/
|
||||
|
||||
var toAdd = new List<Fixture>();
|
||||
var toRemove = new List<Fixture>();
|
||||
var toAddFixtures = new List<Fixture>();
|
||||
var toRemoveFixtures = new List<Fixture>();
|
||||
var computeProperties = false;
|
||||
|
||||
// Given a bunch of data isn't serialized need to sort of re-initialise it
|
||||
@@ -377,12 +391,12 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var existing in _fixtures)
|
||||
{
|
||||
if (!fixture.Name.Equals(existing.Name)) continue;
|
||||
if (!fixture.ID.Equals(existing.ID)) continue;
|
||||
|
||||
if (!fixture.Equals(existing))
|
||||
{
|
||||
toAdd.Add(fixture);
|
||||
toRemove.Add(existing);
|
||||
toAddFixtures.Add(fixture);
|
||||
toRemoveFixtures.Add(existing);
|
||||
}
|
||||
|
||||
found = true;
|
||||
@@ -391,7 +405,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (!found)
|
||||
{
|
||||
toAdd.Add(fixture);
|
||||
toAddFixtures.Add(fixture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +416,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var fixture in newFixtures)
|
||||
{
|
||||
if (fixture.Name.Equals(existing.Name))
|
||||
if (fixture.ID.Equals(existing.ID))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
@@ -411,18 +425,18 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (!found)
|
||||
{
|
||||
toRemove.Add(existing);
|
||||
toRemoveFixtures.Add(existing);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var fixture in toRemove)
|
||||
foreach (var fixture in toRemoveFixtures)
|
||||
{
|
||||
computeProperties = true;
|
||||
RemoveFixture(fixture);
|
||||
}
|
||||
|
||||
// TODO: We also still need event listeners for shapes (Probably need C# events)
|
||||
foreach (var fixture in toAdd)
|
||||
foreach (var fixture in toAddFixtures)
|
||||
{
|
||||
computeProperties = true;
|
||||
AddFixture(fixture);
|
||||
@@ -446,6 +460,29 @@ namespace Robust.Shared.GameObjects
|
||||
Predict = false;
|
||||
}
|
||||
|
||||
private bool TrySetupNetworkedJoint(Joint joint)
|
||||
{
|
||||
// This can fail if we've already deleted the entity or remove physics from it I think?
|
||||
|
||||
if (!Owner.EntityManager.TryGetEntity(joint.BodyAUid, out var entityA) ||
|
||||
!entityA.TryGetComponent(out PhysicsComponent? bodyA))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.EntityManager.TryGetEntity(joint.BodyBUid, out var entityB) ||
|
||||
!entityB.TryGetComponent(out PhysicsComponent? bodyB))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
joint.BodyA = bodyA;
|
||||
joint.BodyB = bodyB;
|
||||
joint.EdgeA = new JointEdge();
|
||||
joint.EdgeB = new JointEdge();
|
||||
return true;
|
||||
}
|
||||
|
||||
public Fixture? GetFixture(string name)
|
||||
{
|
||||
// Sooo I'd rather have fixtures as a list in serialization but there's not really an easy way to have it as a
|
||||
@@ -455,7 +492,7 @@ namespace Robust.Shared.GameObjects
|
||||
// If we really need it then you just deserialize onto a dummy field that then just never gets used again.
|
||||
foreach (var fixture in _fixtures)
|
||||
{
|
||||
if (fixture.Name.Equals(name))
|
||||
if (fixture.ID.Equals(name))
|
||||
{
|
||||
return fixture;
|
||||
}
|
||||
@@ -477,35 +514,18 @@ namespace Robust.Shared.GameObjects
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public Box2 GetWorldAABB(IMapManager? mapManager = null)
|
||||
public Box2 GetWorldAABB(Vector2? worldPosition = null, Angle? worldRotation = null)
|
||||
{
|
||||
mapManager ??= IoCManager.Resolve<IMapManager>();
|
||||
var bounds = new Box2();
|
||||
worldRotation ??= Owner.Transform.WorldRotation;
|
||||
worldPosition ??= Owner.Transform.WorldPosition;
|
||||
var bounds = new Box2(worldPosition.Value, worldPosition.Value);
|
||||
|
||||
foreach (var fixture in _fixtures)
|
||||
{
|
||||
foreach (var (gridId, proxies) in fixture.Proxies)
|
||||
{
|
||||
Vector2 offset;
|
||||
|
||||
if (gridId == GridId.Invalid)
|
||||
{
|
||||
offset = Vector2.Zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = mapManager.GetGrid(gridId).WorldPosition;
|
||||
}
|
||||
|
||||
foreach (var proxy in proxies)
|
||||
{
|
||||
var shapeBounds = proxy.AABB.Translated(offset);
|
||||
bounds = bounds.IsEmpty() ? shapeBounds : bounds.Union(shapeBounds);
|
||||
}
|
||||
}
|
||||
bounds = bounds.Union(fixture.Shape.CalculateLocalBounds(worldRotation.Value).Translated(worldPosition.Value));
|
||||
}
|
||||
|
||||
return bounds.IsEmpty() ? Box2.UnitCentered.Translated(Owner.Transform.WorldPosition) : bounds;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -546,7 +566,6 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
_canCollide = value;
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsUpdateMessage(this));
|
||||
Dirty();
|
||||
@@ -968,12 +987,46 @@ namespace Robust.Shared.GameObjects
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new FixtureUpdateMessage(this, fixture));
|
||||
}
|
||||
|
||||
internal string GetFixtureName(Fixture fixture)
|
||||
private string GetFixtureName(Fixture fixture)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(fixture.ID)) return fixture.ID;
|
||||
|
||||
// For any fixtures that aren't named in the code we will assign one.
|
||||
return $"fixture-{_fixtures.IndexOf(fixture)}";
|
||||
}
|
||||
|
||||
private string GetJointName(Joint joint)
|
||||
{
|
||||
var id = joint.ID;
|
||||
|
||||
if (!string.IsNullOrEmpty(id)) return id;
|
||||
|
||||
var jointCount = Joints.Count();
|
||||
|
||||
for (var i = 0; i < jointCount + 1; i++)
|
||||
{
|
||||
id = $"joint-{i}";
|
||||
if (GetJoint(id) != null) continue;
|
||||
return id;
|
||||
}
|
||||
|
||||
Logger.WarningS("physics", $"Unable to get a joint ID; using its hashcode instead.");
|
||||
return joint.GetHashCode().ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a joint with the specified ID.
|
||||
/// </summary>
|
||||
public Joint? GetJoint(string id)
|
||||
{
|
||||
foreach (var joint in Joints)
|
||||
{
|
||||
if (joint.ID == id) return joint;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal Transform GetTransform()
|
||||
{
|
||||
return new(Owner.Transform.WorldPosition, (float) Owner.Transform.WorldRotation.Theta);
|
||||
@@ -1121,15 +1174,10 @@ namespace Robust.Shared.GameObjects
|
||||
else
|
||||
{
|
||||
// TODO: Probably a bad idea but ehh future sloth's problem; namely that we have to duplicate code between here and CanCollide.
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsUpdateMessage(this));
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsUpdateMessage(this));
|
||||
}
|
||||
}
|
||||
|
||||
if (EntitySystem.Get<SharedPhysicsSystem>().Maps.TryGetValue(Owner.Transform.MapID, out var map))
|
||||
{
|
||||
PhysicsMap = map;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearJoints()
|
||||
@@ -1181,12 +1229,23 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void AddJoint(Joint joint)
|
||||
{
|
||||
var id = GetJointName(joint);
|
||||
|
||||
foreach (var existing in Joints)
|
||||
{
|
||||
// This can happen if a server created joint is sent and applied before the client can create it locally elsewhere
|
||||
if (existing.ID.Equals(id)) return;
|
||||
}
|
||||
|
||||
PhysicsMap.AddJoint(joint);
|
||||
joint.ID = id;
|
||||
Logger.DebugS("physics", $"Added joint id: {joint.ID} type: {joint.GetType().Name} to {Owner}");
|
||||
}
|
||||
|
||||
public void RemoveJoint(Joint joint)
|
||||
{
|
||||
PhysicsMap.RemoveJoint(joint);
|
||||
Logger.DebugS("physics", $"Removed joint id: {joint.ID} type: {joint.GetType().Name} from {Owner}");
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
@@ -1294,17 +1353,10 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (preventCollideMessage.Cancelled) return false;
|
||||
|
||||
#pragma warning disable 618
|
||||
foreach (var comp in Owner.GetAllComponents<ICollideSpecial>())
|
||||
{
|
||||
if (comp.PreventCollide(other)) return false;
|
||||
}
|
||||
preventCollideMessage = new PreventCollideEvent(other, this);
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(other.Owner.Uid, preventCollideMessage);
|
||||
|
||||
foreach (var comp in other.Owner.GetAllComponents<ICollideSpecial>())
|
||||
{
|
||||
if (comp.PreventCollide(this)) return false;
|
||||
}
|
||||
#pragma warning restore 618
|
||||
if (preventCollideMessage.Cancelled) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Set _nextRotation to null to break any active lerps if this is a client side prediction.
|
||||
_nextRotation = null;
|
||||
SetRotation(value);
|
||||
_localRotation = value;
|
||||
Dirty();
|
||||
|
||||
if (!DeferUpdates)
|
||||
@@ -258,17 +258,51 @@ namespace Robust.Shared.GameObjects
|
||||
var valid = _parent.IsValid();
|
||||
return new EntityCoordinates(valid ? _parent : Owner.Uid, valid ? LocalPosition : Vector2.Zero);
|
||||
}
|
||||
// NOTE: This setter must be callable from before initialize (inheriting from AttachParent's note)
|
||||
set
|
||||
{
|
||||
var oldPosition = Coordinates;
|
||||
_localPosition = value.Position;
|
||||
|
||||
var changedParent = false;
|
||||
|
||||
if (value.EntityId != _parent)
|
||||
{
|
||||
var newEntity = Owner.EntityManager.GetEntity(value.EntityId);
|
||||
AttachParent(newEntity);
|
||||
changedParent = true;
|
||||
var newParentEnt = Owner.EntityManager.GetEntity(value.EntityId);
|
||||
var newParent = newParentEnt.Transform;
|
||||
|
||||
DebugTools.Assert(newParent != this,
|
||||
$"Can't parent a {nameof(ITransformComponent)} to itself.");
|
||||
|
||||
// That's already our parent, don't bother attaching again.
|
||||
|
||||
var oldParent = Parent;
|
||||
var oldConcrete = (TransformComponent?) oldParent;
|
||||
var uid = Owner.Uid;
|
||||
oldConcrete?._children.Remove(uid);
|
||||
var newConcrete = (TransformComponent) newParent;
|
||||
newConcrete._children.Add(uid);
|
||||
|
||||
var oldParentOwner = oldParent?.Owner;
|
||||
var entMessage = new EntParentChangedMessage(Owner, oldParentOwner);
|
||||
var compMessage = new ParentChangedMessage(newParentEnt, oldParentOwner);
|
||||
|
||||
// offset position from world to parent
|
||||
_parent = newParentEnt.Uid;
|
||||
ChangeMapId(newConcrete.MapID);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, entMessage);
|
||||
Owner.SendMessage(this, compMessage);
|
||||
|
||||
GridID = GetGridIndex();
|
||||
}
|
||||
|
||||
// These conditions roughly emulate the effects of the code before I changed things,
|
||||
// in regards to when to rebuild matrices.
|
||||
// This may not in fact be the right thing.
|
||||
if (changedParent || !DeferUpdates)
|
||||
RebuildMatrices();
|
||||
Dirty();
|
||||
|
||||
if (!DeferUpdates)
|
||||
@@ -276,7 +310,6 @@ namespace Robust.Shared.GameObjects
|
||||
//TODO: This is a hack, look into WHY we can't call GridPosition before the comp is Running
|
||||
if (Running)
|
||||
{
|
||||
RebuildMatrices();
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new MoveEvent(Owner, oldPosition, Coordinates));
|
||||
}
|
||||
}
|
||||
@@ -304,7 +337,7 @@ namespace Robust.Shared.GameObjects
|
||||
_nextPosition = null;
|
||||
|
||||
var oldGridPos = Coordinates;
|
||||
SetPosition(value);
|
||||
_localPosition = value;
|
||||
Dirty();
|
||||
|
||||
if (!DeferUpdates)
|
||||
@@ -556,49 +589,22 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets another entity as the parent entity.
|
||||
/// Sets another entity as the parent entity, maintaining world position.
|
||||
/// </summary>
|
||||
/// <param name="newParent"></param>
|
||||
public virtual void AttachParent(ITransformComponent newParent)
|
||||
{
|
||||
//NOTE: This function must be callable from before initialize
|
||||
|
||||
// nothing to attach to.
|
||||
// don't attach to something we're already attached to
|
||||
if (ParentUid == newParent.Owner.Uid)
|
||||
return;
|
||||
|
||||
DebugTools.Assert(newParent != this,
|
||||
$"Can't parent a {nameof(ITransformComponent)} to itself.");
|
||||
|
||||
// That's already our parent, don't bother attaching again.
|
||||
var newParentEnt = newParent.Owner;
|
||||
if (newParentEnt.Uid == _parent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var oldParent = Parent;
|
||||
var oldConcrete = (TransformComponent?) oldParent;
|
||||
var uid = Owner.Uid;
|
||||
oldConcrete?._children.Remove(uid);
|
||||
var newConcrete = (TransformComponent) newParent;
|
||||
newConcrete._children.Add(uid);
|
||||
|
||||
var oldParentOwner = oldParent?.Owner;
|
||||
var entMessage = new EntParentChangedMessage(Owner, oldParentOwner);
|
||||
var compMessage = new ParentChangedMessage(newParentEnt, oldParentOwner);
|
||||
|
||||
// offset position from world to parent
|
||||
SetPosition(newParent.InvWorldMatrix.Transform(WorldPosition));
|
||||
_parent = newParentEnt.Uid;
|
||||
ChangeMapId(newConcrete.MapID);
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, entMessage);
|
||||
Owner.SendMessage(this, compMessage);
|
||||
|
||||
RebuildMatrices();
|
||||
Dirty();
|
||||
GridID = GetGridIndex();
|
||||
// offset position from world to parent, and set
|
||||
Coordinates = new EntityCoordinates(newParent.Owner.Uid, newParent.InvWorldMatrix.Transform(WorldPosition));
|
||||
}
|
||||
|
||||
internal void ChangeMapId(MapId newMapId)
|
||||
@@ -713,14 +719,14 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
if (LocalRotation != newState.Rotation)
|
||||
{
|
||||
SetRotation(newState.Rotation);
|
||||
_localRotation = newState.Rotation;
|
||||
rebuildMatrices = true;
|
||||
}
|
||||
|
||||
if (!_localPosition.EqualsApprox(newState.LocalPosition, 0.0001))
|
||||
{
|
||||
var oldPos = Coordinates;
|
||||
SetPosition(newState.LocalPosition);
|
||||
_localPosition = newState.LocalPosition;
|
||||
|
||||
var ev = new MoveEvent(Owner, oldPos, Coordinates);
|
||||
EntitySystem.Get<SharedTransformSystem>().DeferMoveEvent(ev);
|
||||
@@ -755,17 +761,6 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetPosition(Vector2 position)
|
||||
{
|
||||
// DebugTools.Assert(!float.IsNaN(position.X) && !float.IsNaN(position.Y));
|
||||
_localPosition = position;
|
||||
}
|
||||
|
||||
protected virtual void SetRotation(Angle rotation)
|
||||
{
|
||||
_localRotation = rotation;
|
||||
}
|
||||
|
||||
public Matrix3 GetLocalMatrix()
|
||||
{
|
||||
return _localMatrix;
|
||||
|
||||
@@ -99,6 +99,7 @@ namespace Robust.Shared.GameObjects
|
||||
private class EventTables : IDisposable
|
||||
{
|
||||
private IEntityManager _entMan;
|
||||
private IComponentFactory _comFac;
|
||||
|
||||
// eUid -> EventType -> { CompType1, ... CompTypeN }
|
||||
private Dictionary<EntityUid, Dictionary<Type, HashSet<Type>>> _eventTables;
|
||||
@@ -112,6 +113,7 @@ namespace Robust.Shared.GameObjects
|
||||
public EventTables(IEntityManager entMan)
|
||||
{
|
||||
_entMan = entMan;
|
||||
_comFac = entMan.ComponentManager.ComponentFactory;
|
||||
|
||||
_entMan.EntityAdded += OnEntityAdded;
|
||||
_entMan.EntityDeleted += OnEntityDeleted;
|
||||
@@ -194,18 +196,21 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
foreach (var type in GetReferences(compType))
|
||||
{
|
||||
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
{
|
||||
subscribedComps = new HashSet<Type>();
|
||||
eventTable.Add(kvSub.Key, subscribedComps);
|
||||
}
|
||||
if (!_subscriptions.TryGetValue(type, out var compSubs))
|
||||
continue;
|
||||
|
||||
subscribedComps.Add(compType);
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
{
|
||||
subscribedComps = new HashSet<Type>();
|
||||
eventTable.Add(kvSub.Key, subscribedComps);
|
||||
}
|
||||
|
||||
subscribedComps.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,15 +218,18 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
foreach (var type in GetReferences(compType))
|
||||
{
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
return;
|
||||
if (!_subscriptions.TryGetValue(type, out var compSubs))
|
||||
continue;
|
||||
|
||||
subscribedComps.Remove(compType);
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
return;
|
||||
|
||||
subscribedComps.Remove(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,19 +249,23 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
var component = _entMan.ComponentManager.GetComponent(euid, compType);
|
||||
|
||||
handler(euid, component, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchComponent(EntityUid euid, IComponent component, Type eventType, EntityEventArgs args)
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(component.GetType(), out var compSubs))
|
||||
return;
|
||||
foreach (var type in GetReferences(component.GetType()))
|
||||
{
|
||||
if (!_subscriptions.TryGetValue(type, out var compSubs))
|
||||
continue;
|
||||
|
||||
if (!compSubs.TryGetValue(eventType, out var handler))
|
||||
return;
|
||||
if (!compSubs.TryGetValue(eventType, out var handler))
|
||||
continue;
|
||||
|
||||
handler(euid, component, args);
|
||||
handler(euid, component, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearEntities()
|
||||
@@ -281,6 +293,11 @@ namespace Robust.Shared.GameObjects
|
||||
_eventTables = null!;
|
||||
_subscriptions = null!;
|
||||
}
|
||||
|
||||
private IEnumerable<Type> GetReferences(Type type)
|
||||
{
|
||||
return _comFac.GetRegistration(type).References;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -9,11 +9,23 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
#if EXCEPTION_TOLERANCE
|
||||
using Robust.Shared.Exceptions;
|
||||
#endif
|
||||
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public class EntitySystemManager : IEntitySystemManager
|
||||
{
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
[Dependency] private readonly IRuntimeLog _runtimeLog = default!;
|
||||
#endif
|
||||
|
||||
private static readonly Histogram _tickUsageHistogram = Metrics.CreateHistogram("robust_entity_systems_update_usage",
|
||||
"Amount of time spent processing each entity system", new HistogramConfiguration
|
||||
{
|
||||
@@ -21,12 +33,6 @@ namespace Robust.Shared.GameObjects
|
||||
Buckets = Histogram.ExponentialBuckets(0.000_001, 1.5, 25)
|
||||
});
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
[ViewVariables]
|
||||
private readonly List<Type> _extraLoadedTypes = new();
|
||||
|
||||
@@ -268,7 +274,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("entsys", e.ToString());
|
||||
_runtimeLog.LogException(e, "entsys");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -293,7 +299,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorS("entsys", e.ToString());
|
||||
_runtimeLog.LogException(e, "entsys");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -252,5 +252,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Culls all components from the collection that are marked as deleted. This needs to be called often.
|
||||
/// </summary>
|
||||
void CullRemovedComponents();
|
||||
|
||||
IComponentFactory ComponentFactory { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (entity.TryGetComponent<IPhysBody>(out var component))
|
||||
{
|
||||
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(_mapManager), approximate);
|
||||
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(), approximate);
|
||||
}
|
||||
|
||||
return GetEntitiesIntersecting(entity.Transform.Coordinates, approximate);
|
||||
@@ -295,7 +295,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (entity.TryGetComponent(out IPhysBody? component))
|
||||
{
|
||||
if (component.GetWorldAABB(_mapManager).Contains(mapPosition))
|
||||
if (component.GetWorldAABB().Contains(mapPosition))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -341,7 +341,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (entity.TryGetComponent<IPhysBody>(out var component))
|
||||
{
|
||||
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(_mapManager), range, approximate);
|
||||
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(), range, approximate);
|
||||
}
|
||||
|
||||
return GetEntitiesInRange(entity.Transform.Coordinates, range, approximate);
|
||||
@@ -474,13 +474,14 @@ namespace Robust.Shared.GameObjects
|
||||
public Box2 GetWorldAabbFromEntity(in IEntity ent)
|
||||
{
|
||||
if (ent.Deleted)
|
||||
return new Box2(0, 0, 0, 0);
|
||||
return new Box2(); // TODO: God this disgusts me but it's a bandaid for now, see tuple.extract
|
||||
|
||||
var worldPosition = ent.Transform.WorldPosition;
|
||||
|
||||
if (ent.TryGetComponent(out IPhysBody? collider))
|
||||
return collider.GetWorldAABB(_mapManager);
|
||||
return collider.GetWorldAABB(worldPosition);
|
||||
|
||||
var pos = ent.Transform.WorldPosition;
|
||||
return new Box2(pos, pos);
|
||||
return new Box2(worldPosition, worldPosition);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#if DEBUG
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RequestGridTileLookupMessage : EntityEventArgs
|
||||
{
|
||||
public GridId GridId;
|
||||
public Vector2i Indices;
|
||||
|
||||
public RequestGridTileLookupMessage(GridId gridId, Vector2i indices)
|
||||
{
|
||||
GridId = gridId;
|
||||
Indices = indices;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class SendGridTileLookupMessage : EntityEventArgs
|
||||
{
|
||||
public GridId GridId;
|
||||
public Vector2i Indices;
|
||||
|
||||
public List<EntityUid> Entities { get; }
|
||||
|
||||
public SendGridTileLookupMessage(GridId gridId, Vector2i indices, List<EntityUid> entities)
|
||||
{
|
||||
Entities = entities;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -33,6 +33,8 @@ namespace Robust.Shared.Input
|
||||
/// </summary>
|
||||
/// <param name="function">Function to remove.</param>
|
||||
void RemoveFunction(BoundKeyFunction function);
|
||||
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -40,20 +42,25 @@ namespace Robust.Shared.Input
|
||||
{
|
||||
private readonly List<BoundKeyFunction> _commands = new();
|
||||
private readonly IInputCmdContext? _parent;
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="InputCmdContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="parent">Parent context.</param>
|
||||
internal InputCmdContext(IInputCmdContext? parent)
|
||||
internal InputCmdContext(IInputCmdContext? parent, string name)
|
||||
{
|
||||
_parent = parent;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a instance of <see cref="InputCmdContext"/> with no parent.
|
||||
/// </summary>
|
||||
internal InputCmdContext() { }
|
||||
internal InputCmdContext(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddFunction(BoundKeyFunction function)
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace Robust.Shared.Input
|
||||
{
|
||||
var icc = _deferredContextSwitch;
|
||||
_deferredContextSwitch = null;
|
||||
_setActiveContextImmediately(icc);
|
||||
_setActiveContextImmediately( icc);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +132,7 @@ namespace Robust.Shared.Input
|
||||
/// </summary>
|
||||
public InputContextContainer()
|
||||
{
|
||||
_contexts.Add(DefaultContextName, new InputCmdContext());
|
||||
_contexts.Add(DefaultContextName, new InputCmdContext(DefaultContextName));
|
||||
SetActiveContext(DefaultContextName);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ namespace Robust.Shared.Input
|
||||
if (_contexts.ContainsKey(uniqueName))
|
||||
throw new ArgumentException($"Context with name {uniqueName} already exists.", nameof(uniqueName));
|
||||
|
||||
var newContext = new InputCmdContext(parentContext);
|
||||
var newContext = new InputCmdContext(parentContext, uniqueName);
|
||||
_contexts.Add(uniqueName, newContext);
|
||||
return newContext;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ namespace Robust.Shared.Input
|
||||
if (parent == null)
|
||||
throw new ArgumentNullException(nameof(parent));
|
||||
|
||||
var newContext = new InputCmdContext(parent);
|
||||
var newContext = new InputCmdContext(parent, uniqueName);
|
||||
_contexts.Add(uniqueName, newContext);
|
||||
return newContext;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,23 @@ namespace Robust.Shared.IoC
|
||||
_container.Value!.Register<TInterface, TImplementation>(overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register an implementation, to make it accessible to <see cref="Resolve{T}"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type that will be resolvable and implementation.</typeparam>
|
||||
/// <param name="overwrite">
|
||||
/// If true, do not throw an <see cref="InvalidOperationException"/> if an interface is already registered,
|
||||
/// replace the current implementation instead.
|
||||
/// </param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if <paramref name="overwrite"/> is false and <typeparamref name="T"/> has been registered before,
|
||||
/// or if an already instantiated interface (by <see cref="BuildGraph"/>) is attempting to be overwritten.
|
||||
/// </exception>
|
||||
public static void Register<T>(bool overwrite = false) where T : class
|
||||
{
|
||||
Register<T, T>(overwrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="Resolve{T}"/>
|
||||
/// <see cref="BuildGraph"/> MUST be called after this method to make the new interface available.
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Fluent.Net;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Localization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Localization
|
||||
{
|
||||
@@ -11,23 +14,49 @@ namespace Robust.Shared.Localization
|
||||
{
|
||||
private void AddBuiltinFunctions(MessageContext context)
|
||||
{
|
||||
// Grammatical gender
|
||||
// Grammatical gender / pronouns
|
||||
AddCtxFunction(context, "GENDER", FuncGender);
|
||||
AddCtxFunction(context, "SUBJECT", FuncSubject);
|
||||
AddCtxFunction(context, "OBJECT", FuncObject);
|
||||
AddCtxFunction(context, "POSS-ADJ", FuncPossAdj);
|
||||
AddCtxFunction(context, "POSS-PRONOUN", FuncPossPronoun);
|
||||
AddCtxFunction(context, "REFLEXIVE", FuncReflexive);
|
||||
|
||||
// Conjugation
|
||||
AddCtxFunction(context, "CONJUGATE-BE", FuncConjugateBe);
|
||||
AddCtxFunction(context, "CONJUGATE-HAVE", FuncConjugateHave);
|
||||
|
||||
// Proper nouns
|
||||
AddCtxFunction(context, "PROPER", FuncProper);
|
||||
|
||||
// Misc Attribs
|
||||
AddCtxFunction(context, "ATTRIB", args => FuncAttrib(context, args));
|
||||
|
||||
AddCtxFunction(context, "THE", FuncThe);
|
||||
|
||||
// Misc
|
||||
AddCtxFunction(context, "ATTRIB", args => FuncAttrib(context, args));
|
||||
AddCtxFunction(context, "CAPITALIZE", FuncCapitalize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the name of the entity passed in, prepended with "the" if it is not a proper noun.
|
||||
/// </summary>
|
||||
private ILocValue FuncThe(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-the", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string passed in, with the first letter capitalized.
|
||||
/// </summary>
|
||||
private ILocValue FuncCapitalize(LocArgs args)
|
||||
{
|
||||
var input = args.Args[0].Format(new LocContext());
|
||||
if (!String.IsNullOrEmpty(input))
|
||||
return new LocValueString(input.First().ToString().ToUpper() + input.Substring(1));
|
||||
else return new LocValueString("");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the gender of the entity passed in; either Male, Female, Neuter or Epicene.
|
||||
/// </summary>
|
||||
private ILocValue FuncGender(LocArgs args)
|
||||
{
|
||||
if (args.Args.Count < 1) return new LocValueString(nameof(Gender.Neuter));
|
||||
@@ -51,6 +80,62 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString(nameof(Gender.Neuter));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective subject pronoun (he, she, they, it) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncSubject(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-subject-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective object pronoun (him, her, them, it) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncObject(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-object-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective possessive adjective (his, her, their, its) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncPossAdj(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-possessive-adjective", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective possessive pronoun (his, hers, theirs, its) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncPossPronoun(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-possessive-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective reflexive pronoun (himself, herself, themselves, itself) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncReflexive(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-reflexive-pronoun", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective conjugated form of "to be" (is for male/female/neuter, are for epicene) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncConjugateBe(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-conjugate-be", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the respective conjugated form of "to have" (has for male/female/neuter, have for epicene) for the entity's gender.
|
||||
/// </summary>
|
||||
private ILocValue FuncConjugateHave(LocArgs args)
|
||||
{
|
||||
return new LocValueString(GetString("zzzz-conjugate-have", ("ent", args.Args[0])));
|
||||
}
|
||||
|
||||
private ILocValue FuncAttrib(MessageContext context, LocArgs args)
|
||||
{
|
||||
if (args.Args.Count < 2) return new LocValueString("other");
|
||||
@@ -69,6 +154,9 @@ namespace Robust.Shared.Localization
|
||||
return new LocValueString("other");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the passed in entity's name is proper or not.
|
||||
/// </summary>
|
||||
private ILocValue FuncProper(LocArgs args)
|
||||
{
|
||||
if (args.Args.Count < 1) return new LocValueString("false");
|
||||
|
||||
@@ -128,10 +128,25 @@ namespace Robust.Shared.Network
|
||||
/// The side of the network this message is accepted on.
|
||||
/// If we are not on the side specified, the receive callback will not be registered even if provided.
|
||||
/// </param>
|
||||
[Obsolete("Use the method without a name argument instead")]
|
||||
void RegisterNetMessage<T>(string name, ProcessMessage<T>? rxCallback = null,
|
||||
NetMessageAccept accept = NetMessageAccept.Both)
|
||||
where T : NetMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Registers a NetMessage to be sent or received.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type to register.</typeparam>
|
||||
/// <param name="name">String ID of the message.</param>
|
||||
/// <param name="rxCallback">Callback function to process the received message.</param>
|
||||
/// <param name="accept">
|
||||
/// The side of the network this message is accepted on.
|
||||
/// If we are not on the side specified, the receive callback will not be registered even if provided.
|
||||
/// </param>
|
||||
void RegisterNetMessage<T>(ProcessMessage<T>? rxCallback = null,
|
||||
NetMessageAccept accept = NetMessageAccept.Both)
|
||||
where T : NetMessage, new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new NetMessage to be sent.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,9 +6,9 @@ namespace Robust.Shared.Network.Messages.Handshake
|
||||
{
|
||||
internal sealed class MsgEncryptionRequest : NetMessage
|
||||
{
|
||||
public MsgEncryptionRequest() : base("", MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override string MsgName => string.Empty;
|
||||
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public byte[] VerifyToken;
|
||||
public byte[] PublicKey;
|
||||
|
||||
@@ -7,9 +7,9 @@ namespace Robust.Shared.Network.Messages.Handshake
|
||||
{
|
||||
internal sealed class MsgEncryptionResponse : NetMessage
|
||||
{
|
||||
public MsgEncryptionResponse() : base("", MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override string MsgName => string.Empty;
|
||||
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public Guid UserId;
|
||||
public byte[] SharedSecret;
|
||||
|
||||
@@ -10,9 +10,9 @@ namespace Robust.Shared.Network.Messages.Handshake
|
||||
// **NOTE**: This is a special message sent during the client<->server handshake.
|
||||
// It doesn't actually get sent normally and as such doesn't have the "normal" boilerplate.
|
||||
// It's basically just a sane way to encapsulate the message write/read logic.
|
||||
public MsgLoginStart() : base("", MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override string MsgName => string.Empty;
|
||||
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public string UserName;
|
||||
public ImmutableArray<byte> HWId;
|
||||
|
||||
@@ -7,10 +7,9 @@ namespace Robust.Shared.Network.Messages.Handshake
|
||||
internal sealed class MsgLoginSuccess : NetMessage
|
||||
{
|
||||
// Same deal as MsgLogin, helper for NetManager only.
|
||||
public override string MsgName => string.Empty;
|
||||
|
||||
public MsgLoginSuccess() : base("", MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public NetUserData UserData;
|
||||
public LoginType Type;
|
||||
|
||||
@@ -6,11 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgConCmd : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Command;
|
||||
public static readonly string NAME = nameof(MsgConCmd);
|
||||
public MsgConCmd(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public string Text { get; set; }
|
||||
|
||||
|
||||
@@ -6,11 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgConCmdAck : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public const MsgGroups GROUP = MsgGroups.String;
|
||||
public static readonly string NAME = nameof(MsgConCmdAck);
|
||||
public MsgConCmdAck(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.String;
|
||||
|
||||
public string Text { get; set; }
|
||||
public bool Error { get; set; }
|
||||
|
||||
@@ -7,11 +7,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgConCmdReg : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public const MsgGroups GROUP = MsgGroups.String;
|
||||
public static readonly string NAME = nameof(MsgConCmdReg);
|
||||
public MsgConCmdReg(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.String;
|
||||
|
||||
public Command[] Commands { get; set; }
|
||||
|
||||
|
||||
@@ -13,11 +13,7 @@ namespace Robust.Shared.Network.Messages
|
||||
private const int MaxNameSize = 4 * 32; // UTF8 Max char size is 4 bytes, 32 chars.
|
||||
private const int MaxStringValSize = 4 * 256; // UTF8 Max char size is 4 bytes, 256 chars.
|
||||
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Command;
|
||||
public static readonly string NAME = nameof(MsgConVars);
|
||||
public MsgConVars(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.String;
|
||||
|
||||
public GameTick Tick;
|
||||
public List<(string name, object value)> NetworkedVars = null!;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.GameObjects;
|
||||
using System.IO;
|
||||
using Robust.Shared.IoC;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -14,11 +14,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgEntity : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.EntityEvent;
|
||||
public static readonly string NAME = nameof(MsgEntity);
|
||||
public MsgEntity(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.EntityEvent;
|
||||
|
||||
public EntityMessageType Type { get; set; }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The client part of the string-exchange handshake, sent after the
|
||||
/// client receives the mapping hash and after the client receives a
|
||||
@@ -21,11 +20,7 @@ namespace Robust.Shared.Network.Messages
|
||||
[UsedImplicitly]
|
||||
internal class MsgMapStrClientHandshake : NetMessage
|
||||
{
|
||||
|
||||
public MsgMapStrClientHandshake(INetChannel ch)
|
||||
: base(nameof(MsgMapStrClientHandshake), MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
/// <value>
|
||||
/// <c>true</c> if the client needs a new copy of the mapping,
|
||||
@@ -38,7 +33,5 @@ namespace Robust.Shared.Network.Messages
|
||||
|
||||
public override void WriteToBuffer(NetOutgoingMessage buffer)
|
||||
=> buffer.Write(NeedsStrings);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The server part of the string-exchange handshake. Sent as the
|
||||
/// first message in the handshake. Tells the client the hash of
|
||||
@@ -16,11 +15,7 @@ namespace Robust.Shared.Network.Messages
|
||||
[UsedImplicitly]
|
||||
internal class MsgMapStrServerHandshake : NetMessage
|
||||
{
|
||||
|
||||
public MsgMapStrServerHandshake(INetChannel ch)
|
||||
: base(nameof(MsgMapStrServerHandshake), MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
/// <value>
|
||||
/// The hash of the current string mapping held by the server.
|
||||
@@ -48,7 +43,5 @@ namespace Robust.Shared.Network.Messages
|
||||
buffer.WriteVariableInt32(Hash.Length);
|
||||
buffer.Write(Hash);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,11 +15,7 @@ namespace Robust.Shared.Network.Messages
|
||||
[UsedImplicitly]
|
||||
internal class MsgMapStrStrings : NetMessage
|
||||
{
|
||||
|
||||
public MsgMapStrStrings(INetChannel ch)
|
||||
: base(nameof(MsgMapStrStrings), MsgGroups.Core)
|
||||
{
|
||||
}
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
/// <value>
|
||||
/// The raw bytes of the string mapping held by the server.
|
||||
@@ -48,7 +44,5 @@ namespace Robust.Shared.Network.Messages
|
||||
throw new InvalidOperationException("Not all of the bytes were written to the message.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,11 +11,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgPlacement : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Command;
|
||||
public static readonly string NAME = nameof(MsgPlacement);
|
||||
public MsgPlacement(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public PlacementManagerMessage PlaceType { get; set; }
|
||||
public string Align { get; set; }
|
||||
|
||||
@@ -9,11 +9,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgPlayerList : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Core;
|
||||
public static readonly string NAME = nameof(MsgPlayerList);
|
||||
public MsgPlayerList(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public byte PlyCount { get; set; }
|
||||
public List<PlayerState> Plyrs { get; set; }
|
||||
|
||||
@@ -6,11 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgPlayerListReq : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Core;
|
||||
public static readonly string NAME = nameof(MsgPlayerListReq);
|
||||
public MsgPlayerListReq(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Core;
|
||||
|
||||
public override void ReadFromBuffer(NetIncomingMessage buffer)
|
||||
{
|
||||
|
||||
@@ -7,16 +7,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgRay : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgRay);
|
||||
|
||||
public MsgRay(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public Vector2 RayOrigin { get; set; }
|
||||
public Vector2 RayHit { get; set; }
|
||||
|
||||
@@ -5,16 +5,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgReloadPrototypes : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgReloadPrototypes);
|
||||
|
||||
public MsgReloadPrototypes(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public ResourcePath[] Paths = default!;
|
||||
|
||||
|
||||
@@ -6,16 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgScriptEval : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgScriptEval);
|
||||
|
||||
public MsgScriptEval(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public int ScriptSession { get; set; }
|
||||
public string Code { get; set; }
|
||||
|
||||
@@ -10,16 +10,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgScriptResponse : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgScriptResponse);
|
||||
|
||||
public MsgScriptResponse(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public int ScriptSession { get; set; }
|
||||
public bool WasComplete { get; set; }
|
||||
|
||||
@@ -6,16 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgScriptStart : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgScriptStart);
|
||||
|
||||
public MsgScriptStart(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public int ScriptSession { get; set; }
|
||||
|
||||
|
||||
@@ -6,16 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgScriptStartAck : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgScriptStartAck);
|
||||
|
||||
public MsgScriptStartAck(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public bool WasAccepted { get; set; }
|
||||
public int ScriptSession { get; set; }
|
||||
|
||||
@@ -6,16 +6,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgScriptStop : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgScriptStop);
|
||||
|
||||
public MsgScriptStop(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
public int ScriptSession { get; set; }
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Lidgren.Network;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using System.IO;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -17,16 +17,7 @@ namespace Robust.Shared.Network.Messages
|
||||
// (due to being in many parts).
|
||||
public const int ReliableThreshold = 1300;
|
||||
|
||||
#region REQUIRED
|
||||
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Entity;
|
||||
public static readonly string NAME = nameof(MsgState);
|
||||
|
||||
public MsgState(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Entity;
|
||||
|
||||
public GameState State;
|
||||
|
||||
|
||||
@@ -7,11 +7,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgStateAck : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.Entity;
|
||||
public static readonly string NAME = nameof(MsgStateAck);
|
||||
public MsgStateAck(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Entity;
|
||||
|
||||
public GameTick Sequence { get; set; }
|
||||
|
||||
|
||||
@@ -9,13 +9,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public class MsgViewVariablesCloseSession : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesCloseSession);
|
||||
public MsgViewVariablesCloseSession(INetChannel channel) : base(NAME, GROUP) { }
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// The session ID to close, which was agreed upon in <see cref="MsgViewVariablesOpenSession.SessionId"/>.
|
||||
|
||||
@@ -9,13 +9,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public class MsgViewVariablesDenySession : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesDenySession);
|
||||
public MsgViewVariablesDenySession(INetChannel channel) : base(NAME, GROUP) { }
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// The request ID to identify WHICH request has been denied.
|
||||
|
||||
@@ -10,13 +10,7 @@ namespace Robust.Shared.Network.Messages
|
||||
{
|
||||
public class MsgViewVariablesModifyRemote : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesModifyRemote);
|
||||
public MsgViewVariablesModifyRemote(INetChannel channel) : base(NAME, GROUP) { }
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// The session ID of the session to modify.
|
||||
|
||||
@@ -9,13 +9,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public class MsgViewVariablesOpenSession : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesOpenSession);
|
||||
public MsgViewVariablesOpenSession(INetChannel channel) : base(NAME, GROUP) { }
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// The request ID to identify WHICH request has been granted.
|
||||
|
||||
@@ -13,16 +13,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public class MsgViewVariablesRemoteData : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesRemoteData);
|
||||
|
||||
public MsgViewVariablesRemoteData(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// The request ID equal to the ID sent in <see cref="RequestId"/>,
|
||||
|
||||
@@ -13,16 +13,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public class MsgViewVariablesReqData : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesReqData);
|
||||
|
||||
public MsgViewVariablesReqData(INetChannel channel) : base(NAME, GROUP)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// The request ID that will be sent in <see cref="MsgViewVariablesRemoteData"/> to
|
||||
|
||||
@@ -13,13 +13,7 @@ namespace Robust.Shared.Network.Messages
|
||||
/// </summary>
|
||||
public class MsgViewVariablesReqSession : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
|
||||
public const MsgGroups GROUP = MsgGroups.Command;
|
||||
public const string NAME = nameof(MsgViewVariablesReqSession);
|
||||
public MsgViewVariablesReqSession(INetChannel channel) : base(NAME, GROUP) { }
|
||||
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.Command;
|
||||
|
||||
/// <summary>
|
||||
/// An ID the client assigns so it knows which request was accepted/denied through
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -868,22 +867,32 @@ namespace Robust.Shared.Network
|
||||
return;
|
||||
}
|
||||
|
||||
var constructor = packetType.GetConstructor(new[] {typeof(INetChannel)})!;
|
||||
|
||||
DebugTools.AssertNotNull(constructor);
|
||||
|
||||
var dynamicMethod = new DynamicMethod($"_netMsg<>{name}", typeof(NetMessage), new[] {typeof(INetChannel)},
|
||||
packetType, false);
|
||||
|
||||
dynamicMethod.DefineParameter(1, ParameterAttributes.In, "channel");
|
||||
|
||||
var gen = dynamicMethod.GetILGenerator();
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var gen = dynamicMethod.GetILGenerator().GetRobustGen();
|
||||
|
||||
var @delegate =
|
||||
(Func<INetChannel, NetMessage>) dynamicMethod.CreateDelegate(typeof(Func<INetChannel, NetMessage>));
|
||||
// Obsolete path for content
|
||||
if (packetType.GetConstructor(new[] {typeof(INetChannel)}) is { } constructor)
|
||||
{
|
||||
gen.Emit(OpCodes.Ldarg_0);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
constructor = packetType.GetConstructor(Type.EmptyTypes)!;
|
||||
DebugTools.AssertNotNull(constructor);
|
||||
|
||||
gen.DeclareLocal(typeof(NetMessage));
|
||||
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
var @delegate = dynamicMethod.CreateDelegate<Func<INetChannel, NetMessage>>();
|
||||
|
||||
ref var entry = ref _netMsgFunctions[id];
|
||||
entry.CreateFunction = @delegate;
|
||||
@@ -919,6 +928,14 @@ namespace Robust.Shared.Network
|
||||
CacheBlankFunction(typeof(T));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RegisterNetMessage<T>(ProcessMessage<T>? rxCallback = null,
|
||||
NetMessageAccept accept = NetMessageAccept.Both)
|
||||
where T : NetMessage, new()
|
||||
{
|
||||
RegisterNetMessage(new T().MsgName, rxCallback, accept);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public T CreateNetMessage<T>()
|
||||
where T : NetMessage
|
||||
@@ -928,16 +945,25 @@ namespace Robust.Shared.Network
|
||||
|
||||
private void CacheBlankFunction(Type type)
|
||||
{
|
||||
var constructor = type.GetConstructor(new[] {typeof(INetChannel)})!;
|
||||
|
||||
DebugTools.AssertNotNull(constructor);
|
||||
|
||||
var dynamicMethod = new DynamicMethod($"_netMsg<>{type.Name}", typeof(NetMessage), Array.Empty<Type>(),
|
||||
type, false);
|
||||
var gen = dynamicMethod.GetILGenerator();
|
||||
gen.Emit(OpCodes.Ldnull);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
var gen = dynamicMethod.GetILGenerator().GetRobustGen();
|
||||
|
||||
// Obsolete path for content
|
||||
if (type.GetConstructor(new[] {typeof(INetChannel)}) is { } constructor)
|
||||
{
|
||||
gen.Emit(OpCodes.Ldnull);
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
constructor = type.GetConstructor(Type.EmptyTypes)!;
|
||||
DebugTools.AssertNotNull(constructor);
|
||||
|
||||
gen.Emit(OpCodes.Newobj, constructor);
|
||||
gen.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
var @delegate = (Func<NetMessage>) dynamicMethod.CreateDelegate(typeof(Func<NetMessage>));
|
||||
|
||||
|
||||
@@ -49,17 +49,17 @@ namespace Robust.Shared.Network
|
||||
/// <summary>
|
||||
/// String identifier of the message type.
|
||||
/// </summary>
|
||||
public string MsgName { get; }
|
||||
public virtual string MsgName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The group this message type belongs to.
|
||||
/// </summary>
|
||||
public MsgGroups MsgGroup { get; }
|
||||
public virtual MsgGroups MsgGroup { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The channel that this message came in on.
|
||||
/// </summary>
|
||||
public INetChannel MsgChannel { get; set; }
|
||||
public INetChannel MsgChannel { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The size of this packet in bytes.
|
||||
@@ -71,12 +71,18 @@ namespace Robust.Shared.Network
|
||||
/// </summary>
|
||||
/// <param name="name">String identifier of the message type.</param>
|
||||
/// <param name="group">The group this message type belongs to.</param>
|
||||
[Obsolete("Use NetMessageAttribute and no constructor instead")]
|
||||
protected NetMessage(string name, MsgGroups group)
|
||||
{
|
||||
MsgName = name;
|
||||
MsgGroup = group;
|
||||
}
|
||||
|
||||
protected NetMessage()
|
||||
{
|
||||
MsgName = GetType().Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes the NetIncomingMessage into this NetMessage class.
|
||||
/// </summary>
|
||||
|
||||
@@ -61,8 +61,7 @@ namespace Robust.Shared.Network
|
||||
|
||||
_callback = callback;
|
||||
_updateCallback = updateCallback;
|
||||
_network.RegisterNetMessage<MsgStringTableEntries>(MsgStringTableEntries.NAME, ReceiveEntries,
|
||||
NetMessageAccept.Client);
|
||||
_network.RegisterNetMessage<MsgStringTableEntries>(ReceiveEntries, NetMessageAccept.Client);
|
||||
|
||||
Reset();
|
||||
}
|
||||
@@ -113,9 +112,9 @@ namespace Robust.Shared.Network
|
||||
_initialized = false;
|
||||
|
||||
// manually register the id on the client so it can bootstrap itself with incoming table entries
|
||||
if (!TryFindStringId(MsgStringTableEntries.NAME, out _))
|
||||
if (!TryFindStringId(nameof(MsgStringTableEntries), out _))
|
||||
{
|
||||
_strings.Add(StringTablePacketId, MsgStringTableEntries.NAME);
|
||||
_strings.Add(StringTablePacketId, nameof(MsgStringTableEntries));
|
||||
|
||||
if (_network.IsClient)
|
||||
{
|
||||
@@ -123,7 +122,7 @@ namespace Robust.Shared.Network
|
||||
new MsgStringTableEntries.Entry
|
||||
{
|
||||
Id = StringTablePacketId,
|
||||
String = MsgStringTableEntries.NAME
|
||||
String = nameof(MsgStringTableEntries)
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -278,11 +277,7 @@ namespace Robust.Shared.Network
|
||||
/// </summary>
|
||||
public class MsgStringTableEntries : NetMessage
|
||||
{
|
||||
#region REQUIRED
|
||||
public static readonly MsgGroups GROUP = MsgGroups.String;
|
||||
public static readonly string NAME = nameof(MsgStringTableEntries);
|
||||
public MsgStringTableEntries(INetChannel channel) : base(NAME, GROUP) { }
|
||||
#endregion
|
||||
public override MsgGroups MsgGroup => MsgGroups.String;
|
||||
|
||||
public Entry[] Entries { get; set; } = default!;
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
public float IntersectionPercent(PhysicsComponent bodyA, PhysicsComponent bodyB)
|
||||
{
|
||||
// TODO: Use actual shapes and not just the AABB?
|
||||
return bodyA.GetWorldAABB(_mapManager).IntersectPercentage(bodyB.GetWorldAABB(_mapManager));
|
||||
return bodyA.GetWorldAABB().IntersectPercentage(bodyB.GetWorldAABB());
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@@ -159,7 +159,8 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
|
||||
if (moveEvent.Sender.Deleted || !moveEvent.Sender.TryGetComponent(out PhysicsComponent? physicsComponent)) continue;
|
||||
|
||||
SynchronizeFixtures(physicsComponent, moveEvent.NewPosition.ToMapPos(EntityManager) - moveEvent.OldPosition.ToMapPos(EntityManager), moveEvent.WorldAABB);
|
||||
var mapPosition = moveEvent.NewPosition.ToMapPos(EntityManager);
|
||||
SynchronizeFixtures(physicsComponent, mapPosition - moveEvent.OldPosition.ToMapPos(EntityManager), mapPosition, moveEvent.WorldAABB);
|
||||
}
|
||||
|
||||
while (_queuedRotateEvent.Count > 0)
|
||||
@@ -173,7 +174,7 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
if (rotateEvent.Sender.Deleted || !rotateEvent.Sender.TryGetComponent(out PhysicsComponent? physicsComponent))
|
||||
return;
|
||||
|
||||
SynchronizeFixtures(physicsComponent, Vector2.Zero, rotateEvent.WorldAABB);
|
||||
SynchronizeFixtures(physicsComponent, Vector2.Zero, null, rotateEvent.WorldAABB);
|
||||
}
|
||||
|
||||
_handledThisTick.Clear();
|
||||
@@ -523,9 +524,7 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
/// <summary>
|
||||
/// Move all of the fixtures on this body.
|
||||
/// </summary>
|
||||
/// <param name="body"></param>
|
||||
/// <param name="displacement"></param>
|
||||
private void SynchronizeFixtures(PhysicsComponent body, Vector2 displacement, Box2? worldAABB = null)
|
||||
private void SynchronizeFixtures(PhysicsComponent body, Vector2 displacement, Vector2? worldPosition = null, Box2? worldAABB = null)
|
||||
{
|
||||
// If the entity's still being initialized it might have MoveEvent called (might change in future?)
|
||||
if (!_lastBroadPhases.TryGetValue(body, out var oldBroadPhases))
|
||||
@@ -533,8 +532,13 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: These will need swept broadPhases
|
||||
worldPosition ??= body.Owner.Transform.WorldPosition;
|
||||
var worldRotation = body.Owner.Transform.WorldRotation;
|
||||
|
||||
|
||||
var mapId = body.Owner.Transform.MapID;
|
||||
worldAABB ??= body.GetWorldAABB(_mapManager);
|
||||
worldAABB ??= body.GetWorldAABB(worldPosition, worldRotation);
|
||||
|
||||
// 99% of the time this is going to be 1, maybe 2, so HashSet probably slower?
|
||||
|
||||
@@ -561,9 +565,6 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
}
|
||||
|
||||
// Update retained broadphases
|
||||
// TODO: These will need swept broadPhases
|
||||
var offset = body.Owner.Transform.WorldPosition;
|
||||
var worldRotation = body.Owner.Transform.WorldRotation;
|
||||
|
||||
foreach (var broadPhase in oldBroadPhases)
|
||||
{
|
||||
@@ -575,19 +576,20 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
{
|
||||
if (!fixture.Proxies.TryGetValue(gridId, out var proxies)) continue;
|
||||
|
||||
var gridPosition = worldPosition.Value;
|
||||
double gridRotation = worldRotation;
|
||||
|
||||
if (gridId != GridId.Invalid)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(gridId);
|
||||
gridPosition -= grid.WorldPosition;
|
||||
// TODO: Should probably have a helper for this
|
||||
gridRotation = worldRotation - EntityManager.GetEntity(grid.GridEntityId).Transform.WorldRotation;
|
||||
}
|
||||
|
||||
foreach (var proxy in proxies)
|
||||
{
|
||||
double gridRotation = worldRotation;
|
||||
|
||||
if (gridId != GridId.Invalid)
|
||||
{
|
||||
var grid = _mapManager.GetGrid(gridId);
|
||||
offset -= grid.WorldPosition;
|
||||
// TODO: Should probably have a helper for this
|
||||
gridRotation = worldRotation - body.Owner.EntityManager.GetEntity(grid.GridEntityId).Transform.WorldRotation;
|
||||
}
|
||||
|
||||
var aabb = fixture.Shape.CalculateLocalBounds(gridRotation).Translated(offset);
|
||||
var aabb = fixture.Shape.CalculateLocalBounds(gridRotation).Translated(gridPosition);
|
||||
proxy.AABB = aabb;
|
||||
|
||||
broadPhase.MoveProxy(proxy.ProxyId, in aabb, displacement);
|
||||
@@ -680,6 +682,9 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
|
||||
public IEnumerable<PhysicsComponent> GetCollidingEntities(PhysicsComponent body, Vector2 offset, bool approximate = true)
|
||||
{
|
||||
// TODO: In an ideal world we'd just iterate over the body's contacts (need to make more stuff immediate
|
||||
// for physics for this to be viable so future-work).
|
||||
|
||||
// If the body has just had its collision enabled or disabled it may not be ready yet so we'll wait a tick.
|
||||
if (!body.CanCollide || body.Owner.Transform.MapID == MapId.Nullspace)
|
||||
{
|
||||
@@ -692,10 +697,9 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
AddBody(body);
|
||||
}
|
||||
|
||||
var modifiers = body.Owner.GetAllComponents<ICollideSpecial>();
|
||||
var entities = new List<PhysicsComponent>();
|
||||
|
||||
var state = (body, modifiers, entities);
|
||||
var state = (body, entities);
|
||||
|
||||
foreach (var broadPhase in _lastBroadPhases[body])
|
||||
{
|
||||
@@ -704,32 +708,12 @@ namespace Robust.Shared.Physics.Broadphase
|
||||
foreach (var proxy in fixture.Proxies[GetGridId(broadPhase)])
|
||||
{
|
||||
broadPhase.QueryAabb(ref state,
|
||||
(ref (PhysicsComponent body, IEnumerable<ICollideSpecial> modifiers, List<PhysicsComponent> entities) state,
|
||||
(ref (PhysicsComponent body, List<PhysicsComponent> entities) state,
|
||||
in FixtureProxy other) =>
|
||||
{
|
||||
if (other.Fixture.Body.Deleted || other.Fixture.Body == body) return true;
|
||||
if ((proxy.Fixture.CollisionMask & other.Fixture.CollisionLayer) == 0x0) return true;
|
||||
|
||||
var preventCollision = false;
|
||||
var otherModifiers = other.Fixture.Body.Owner.GetAllComponents<ICollideSpecial>();
|
||||
var preventCollideMessage = new PreventCollideEvent(body, other.Fixture.Body);
|
||||
EntityManager.EventBus.RaiseLocalEvent(body.Owner.Uid, preventCollideMessage);
|
||||
|
||||
if (preventCollideMessage.Cancelled) return true;
|
||||
|
||||
#pragma warning disable 618
|
||||
foreach (var modifier in state.modifiers)
|
||||
{
|
||||
preventCollision |= modifier.PreventCollide(other.Fixture.Body);
|
||||
}
|
||||
foreach (var modifier in otherModifiers)
|
||||
{
|
||||
preventCollision |= modifier.PreventCollide(body);
|
||||
}
|
||||
#pragma warning restore 618
|
||||
|
||||
if (preventCollision)
|
||||
return true;
|
||||
if (!body.ShouldCollide(other.Fixture.Body)) return true;
|
||||
|
||||
state.entities.Add(other.Fixture.Body);
|
||||
return true;
|
||||
|
||||
@@ -1025,10 +1025,11 @@ namespace Robust.Shared.Physics.Collision
|
||||
PolygonShape polyB, in Transform transformB)
|
||||
{
|
||||
manifold.PointCount = 0;
|
||||
float totalRadius = polyA.Radius + polyB.Radius;
|
||||
var totalRadius = polyA.Radius + polyB.Radius;
|
||||
|
||||
var edgeA = 0;
|
||||
var separationA = FindMaxSeparation(out edgeA, polyA, transformA, polyB, transformB);
|
||||
|
||||
int edgeA = 0;
|
||||
float separationA = FindMaxSeparation(out edgeA, polyA, transformA, polyB, transformB);
|
||||
if (separationA > totalRadius)
|
||||
return;
|
||||
|
||||
@@ -1265,84 +1266,64 @@ namespace Robust.Shared.Physics.Collision
|
||||
private static float FindMaxSeparation(out int edgeIndex, PolygonShape poly1, in Transform xf1,
|
||||
PolygonShape poly2, in Transform xf2)
|
||||
{
|
||||
int count1 = poly1.Vertices.Count;
|
||||
List<Vector2> normals1 = poly1.Normals;
|
||||
// MIT License
|
||||
|
||||
// Vector pointing from the centroid of poly1 to the centroid of poly2.
|
||||
// TODO: Center of mass
|
||||
// Vector2 d = Transform.Mul(xf2, poly2.MassData.Centroid) - Transform.Mul(xf1, poly1.MassData.Centroid);
|
||||
Vector2 d = Transform.Mul(xf2, Vector2.Zero) - Transform.Mul(xf1, Vector2.Zero);
|
||||
Vector2 dLocal1 = Transform.MulT(xf1.Quaternion2D, d);
|
||||
// Copyright (c) 2019 Erin Catto
|
||||
|
||||
// Find edge normal on poly1 that has the largest projection onto d.
|
||||
int edge = 0;
|
||||
float maxDot = float.MinValue;
|
||||
for (int i = 0; i < count1; ++i)
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
var n1s = poly1.Normals;
|
||||
var v1s = poly1.Vertices;
|
||||
var v2s = poly2.Vertices;
|
||||
var count1 = v1s.Count;
|
||||
var count2 = v2s.Count;
|
||||
var xf = Transform.MulT(xf2, xf1);
|
||||
|
||||
var bestIndex = 0;
|
||||
var maxSeparation = float.MinValue;
|
||||
|
||||
for (var i = 0; i < count1; i++)
|
||||
{
|
||||
float dot = Vector2.Dot(normals1[i], dLocal1);
|
||||
if (dot > maxDot)
|
||||
// Get poly1 normal in frame2.
|
||||
var n = Transform.Mul(xf.Quaternion2D, n1s[i]);
|
||||
var v1 = Transform.Mul(xf, v1s[i]);
|
||||
|
||||
// Find deepest point for normal i.
|
||||
float si = float.MaxValue;
|
||||
for (var j = 0; j < count2; ++j)
|
||||
{
|
||||
maxDot = dot;
|
||||
edge = i;
|
||||
float sij = Vector2.Dot(n, v2s[j] - v1);
|
||||
if (sij < si)
|
||||
{
|
||||
si = sij;
|
||||
}
|
||||
}
|
||||
|
||||
if (si > maxSeparation)
|
||||
{
|
||||
maxSeparation = si;
|
||||
bestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the separation for the edge normal.
|
||||
float s = EdgeSeparation(poly1, xf1, edge, poly2, xf2);
|
||||
|
||||
// Check the separation for the previous edge normal.
|
||||
int prevEdge = edge - 1 >= 0 ? edge - 1 : count1 - 1;
|
||||
float sPrev = EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2);
|
||||
|
||||
// Check the separation for the next edge normal.
|
||||
int nextEdge = edge + 1 < count1 ? edge + 1 : 0;
|
||||
float sNext = EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2);
|
||||
|
||||
// Find the best edge and the search direction.
|
||||
int bestEdge;
|
||||
float bestSeparation;
|
||||
int increment;
|
||||
if (sPrev > s && sPrev > sNext)
|
||||
{
|
||||
increment = -1;
|
||||
bestEdge = prevEdge;
|
||||
bestSeparation = sPrev;
|
||||
}
|
||||
else if (sNext > s)
|
||||
{
|
||||
increment = 1;
|
||||
bestEdge = nextEdge;
|
||||
bestSeparation = sNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
edgeIndex = edge;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Perform a local search for the best edge normal.
|
||||
for (;;)
|
||||
{
|
||||
if (increment == -1)
|
||||
edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1;
|
||||
else
|
||||
edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0;
|
||||
|
||||
s = EdgeSeparation(poly1, xf1, edge, poly2, xf2);
|
||||
|
||||
if (s > bestSeparation)
|
||||
{
|
||||
bestEdge = edge;
|
||||
bestSeparation = s;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
edgeIndex = bestEdge;
|
||||
return bestSeparation;
|
||||
edgeIndex = bestIndex;
|
||||
return maxSeparation;
|
||||
}
|
||||
|
||||
private static void FindIncidentEdge(Span<ClipVertex> c, PolygonShape poly1, in Transform xf1, int edge1,
|
||||
@@ -1395,50 +1376,6 @@ namespace Robust.Shared.Physics.Collision
|
||||
|
||||
c[1] = cv1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the separation between poly1 and poly2 for a give edge normal on poly1.
|
||||
/// </summary>
|
||||
/// <param name="poly1">The poly1.</param>
|
||||
/// <param name="xf1">The XF1.</param>
|
||||
/// <param name="edge1">The edge1.</param>
|
||||
/// <param name="poly2">The poly2.</param>
|
||||
/// <param name="xf2">The XF2.</param>
|
||||
/// <returns></returns>
|
||||
private static float EdgeSeparation(PolygonShape poly1, in Transform xf1, int edge1, PolygonShape poly2,
|
||||
in Transform xf2)
|
||||
{
|
||||
List<Vector2> vertices1 = poly1.Vertices;
|
||||
List<Vector2> normals1 = poly1.Normals;
|
||||
|
||||
int count2 = poly2.Vertices.Count;
|
||||
List<Vector2> vertices2 = poly2.Vertices;
|
||||
|
||||
DebugTools.Assert(0 <= edge1 && edge1 < poly1.Vertices.Count);
|
||||
|
||||
// Convert normal from poly1's frame into poly2's frame.
|
||||
Vector2 normal1World = Transform.Mul(xf1.Quaternion2D, normals1[edge1]);
|
||||
Vector2 normal1 = Transform.MulT(xf2.Quaternion2D, normal1World);
|
||||
|
||||
// Find support vertex on poly2 for -normal.
|
||||
int index = 0;
|
||||
float minDot = float.MaxValue;
|
||||
|
||||
for (int i = 0; i < count2; ++i)
|
||||
{
|
||||
float dot = Vector2.Dot(vertices2[i], normal1);
|
||||
if (dot < minDot)
|
||||
{
|
||||
minDot = dot;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 v1 = Transform.Mul(xf1, vertices1[edge1]);
|
||||
Vector2 v2 = Transform.Mul(xf2, vertices2[index]);
|
||||
float separation = Vector2.Dot(v2 - v1, normal1World);
|
||||
return separation;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -291,6 +291,7 @@ namespace Robust.Shared.Physics
|
||||
var item = tuple.tree.GetUserData(proxy)!;
|
||||
if (!tuple.approx)
|
||||
{
|
||||
// TODO: The DynamicTree already stores AABBs so do we even need this? I know we fatten them but ehh
|
||||
var precise = tuple.extract(item);
|
||||
if (!precise.Intersects(tuple.aabb))
|
||||
{
|
||||
|
||||
@@ -378,46 +378,52 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
// so we'll do this (TODO: Maybe it's shitty design and we should move to PostCollide? Though we still need to check for each contact anyway I guess).
|
||||
if (!contact.IsTouching) continue;
|
||||
|
||||
var bodyA = contact.FixtureA!.Body;
|
||||
var bodyB = contact.FixtureB!.Body;
|
||||
var fixtureA = contact.FixtureA!;
|
||||
var fixtureB = contact.FixtureB!;
|
||||
var bodyA = fixtureA.Body;
|
||||
var bodyB = fixtureB.Body;
|
||||
var manifold = contact.Manifold;
|
||||
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyA.Owner.Uid, new StartCollideEvent(contact.FixtureA, contact.FixtureB, contact.Manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyB.Owner.Uid, new StartCollideEvent(contact.FixtureB, contact.FixtureA, contact.Manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyA.Owner.Uid, new StartCollideEvent(fixtureA, fixtureB, manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyB.Owner.Uid, new StartCollideEvent(fixtureB, fixtureA, manifold));
|
||||
|
||||
#pragma warning disable 618
|
||||
foreach (var comp in bodyA.Owner.GetAllComponents<IStartCollide>().ToArray())
|
||||
{
|
||||
if (bodyB.Deleted) break;
|
||||
comp.CollideWith(contact.FixtureA!, contact.FixtureB!, contact.Manifold);
|
||||
comp.CollideWith(fixtureA, fixtureB, contact.Manifold);
|
||||
}
|
||||
|
||||
foreach (var comp in bodyB.Owner.GetAllComponents<IStartCollide>().ToArray())
|
||||
{
|
||||
if (bodyA.Deleted) break;
|
||||
comp.CollideWith(contact.FixtureB!, contact.FixtureA!, contact.Manifold);
|
||||
comp.CollideWith(fixtureB, fixtureA, contact.Manifold);
|
||||
}
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
foreach (var contact in _endCollisions)
|
||||
{
|
||||
var bodyA = contact.FixtureA!.Body;
|
||||
var bodyB = contact.FixtureB!.Body;
|
||||
var fixtureA = contact.FixtureA!;
|
||||
var fixtureB = contact.FixtureB!;
|
||||
var bodyA = fixtureA.Body;
|
||||
var bodyB = fixtureB.Body;
|
||||
var manifold = contact.Manifold;
|
||||
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyA.Owner.Uid, new EndCollideEvent(contact.FixtureA, contact.FixtureB, contact.Manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyB.Owner.Uid, new EndCollideEvent(contact.FixtureB, contact.FixtureA, contact.Manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyA.Owner.Uid, new EndCollideEvent(fixtureA, fixtureB, manifold));
|
||||
_entityManager.EventBus.RaiseLocalEvent(bodyB.Owner.Uid, new EndCollideEvent(fixtureB, fixtureA, manifold));
|
||||
|
||||
#pragma warning disable 618
|
||||
foreach (var comp in bodyA.Owner.GetAllComponents<IEndCollide>().ToArray())
|
||||
{
|
||||
if (bodyB.Deleted) break;
|
||||
comp.CollideWith(contact.FixtureA!, contact.FixtureB!, contact.Manifold);
|
||||
comp.CollideWith(fixtureA, fixtureB, manifold);
|
||||
}
|
||||
|
||||
foreach (var comp in bodyB.Owner.GetAllComponents<IEndCollide>().ToArray())
|
||||
{
|
||||
if (bodyA.Deleted) break;
|
||||
comp.CollideWith(contact.FixtureB!, contact.FixtureA!, contact.Manifold);
|
||||
comp.CollideWith(fixtureB, fixtureA, manifold);
|
||||
}
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("name", true)]
|
||||
public string Name { get; set; } = string.Empty;
|
||||
[DataField("id", true)]
|
||||
public string ID { get; set; } = string.Empty;
|
||||
|
||||
public IReadOnlyDictionary<GridId, FixtureProxy[]> Proxies => _proxies;
|
||||
|
||||
@@ -252,7 +252,7 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
/// <param name="fixture"></param>
|
||||
internal void CopyTo(Fixture fixture)
|
||||
{
|
||||
fixture.Name = Name;
|
||||
fixture.ID = ID;
|
||||
fixture.Shape = Shape;
|
||||
fixture._friction = _friction;
|
||||
fixture._restitution = _restitution;
|
||||
@@ -334,9 +334,9 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
mapManager ??= IoCManager.Resolve<IMapManager>();
|
||||
broadPhaseSystem ??= EntitySystem.Get<SharedBroadPhaseSystem>();
|
||||
|
||||
var worldAABB = Body.GetWorldAABB(mapManager);
|
||||
var worldPosition = Body.Owner.Transform.WorldPosition;
|
||||
var worldRotation = Body.Owner.Transform.WorldRotation;
|
||||
var worldAABB = Body.GetWorldAABB(worldPosition, worldRotation);
|
||||
|
||||
foreach (var gridId in mapManager.FindGridIdsIntersecting(mapId, worldAABB, true))
|
||||
{
|
||||
@@ -609,7 +609,7 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
_collisionMask == other.CollisionMask &&
|
||||
Shape.Equals(other.Shape) &&
|
||||
Body == other.Body &&
|
||||
Name.Equals(other.Name);
|
||||
ID.Equals(other.ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,12 @@ namespace Robust.Shared.Physics.Dynamics.Joints
|
||||
[DataDefinition]
|
||||
public abstract class Joint : IEquatable<Joint>
|
||||
{
|
||||
/// <summary>
|
||||
/// Network identifier of this joint.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string ID { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if this joint is enabled or not. Disabling a joint
|
||||
/// means it is still in the simulation, but inactive.
|
||||
|
||||
@@ -6,9 +6,14 @@ namespace Robust.Shared.Physics.Dynamics.Joints
|
||||
// better than calcium
|
||||
public static class JointHelpers
|
||||
{
|
||||
public static DistanceJoint CreateDistanceJoint(this PhysicsComponent bodyA, PhysicsComponent bodyB)
|
||||
public static DistanceJoint CreateDistanceJoint(this PhysicsComponent bodyA, PhysicsComponent bodyB, string? id = null)
|
||||
{
|
||||
var joint = new DistanceJoint(bodyA, bodyB, Vector2.Zero, Vector2.Zero);
|
||||
if (id != null)
|
||||
{
|
||||
joint.ID = id;
|
||||
}
|
||||
|
||||
bodyA.AddJoint(joint);
|
||||
return joint;
|
||||
}
|
||||
|
||||
@@ -25,8 +25,10 @@ using System.Collections.Generic;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Physics.Dynamics.Contacts;
|
||||
using Robust.Shared.Physics.Dynamics.Joints;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -98,9 +100,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
/// </summary>
|
||||
private HashSet<PhysicsComponent> _islandSet = new();
|
||||
|
||||
private HashSet<Joint> _queuedJointAdd = new();
|
||||
private HashSet<Joint> _queuedJointRemove = new();
|
||||
|
||||
private HashSet<PhysicsComponent> _queuedWake = new();
|
||||
private HashSet<PhysicsComponent> _queuedSleep = new();
|
||||
|
||||
@@ -159,13 +158,146 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
|
||||
public void AddJoint(Joint joint)
|
||||
{
|
||||
// TODO: Need static helper class to easily create Joints
|
||||
_queuedJointAdd.Add(joint);
|
||||
var bodyA = joint.BodyA;
|
||||
var bodyB = joint.BodyB;
|
||||
|
||||
// BodyA and BodyB should share joints so we can just check if BodyA already has this joint.
|
||||
for (var je = bodyA.JointEdges; je != null; je = je.Next)
|
||||
{
|
||||
if (je.Joint.Equals(joint)) continue;
|
||||
}
|
||||
|
||||
// TODO: Optimise dafuk out of this.
|
||||
if (Joints.Contains(joint))
|
||||
{
|
||||
Logger.ErrorS("physics", $"Tried to add joint id: {joint.ID} owner: {joint.BodyA.Owner} that's already on map");
|
||||
return;
|
||||
}
|
||||
|
||||
// Connect to the world list.
|
||||
Joints.Add(joint);
|
||||
|
||||
// Connect to the bodies' doubly linked lists.
|
||||
joint.EdgeA.Joint = joint;
|
||||
joint.EdgeA.Other = bodyB;
|
||||
joint.EdgeA.Prev = null;
|
||||
joint.EdgeA.Next = bodyA.JointEdges;
|
||||
|
||||
if (bodyA.JointEdges != null)
|
||||
bodyA.JointEdges.Prev = joint.EdgeA;
|
||||
|
||||
bodyA.JointEdges = joint.EdgeA;
|
||||
|
||||
joint.EdgeB.Joint = joint;
|
||||
joint.EdgeB.Other = bodyA;
|
||||
joint.EdgeB.Prev = null;
|
||||
joint.EdgeB.Next = bodyB.JointEdges;
|
||||
|
||||
if (bodyB.JointEdges != null)
|
||||
bodyB.JointEdges.Prev = joint.EdgeB;
|
||||
|
||||
bodyB.JointEdges = joint.EdgeB;
|
||||
|
||||
joint.BodyAUid = bodyA.Owner.Uid;
|
||||
joint.BodyBUid = bodyB.Owner.Uid;
|
||||
|
||||
// If the joint prevents collisions, then flag any contacts for filtering.
|
||||
if (!joint.CollideConnected)
|
||||
{
|
||||
ContactEdge? edge = bodyB.ContactEdges;
|
||||
while (edge != null)
|
||||
{
|
||||
if (edge.Other == bodyA)
|
||||
{
|
||||
// Flag the contact for filtering at the next time step (where either
|
||||
// body is awake).
|
||||
edge.Contact!.FilterFlag = true;
|
||||
}
|
||||
|
||||
edge = edge.Next;
|
||||
}
|
||||
}
|
||||
|
||||
bodyA.Dirty();
|
||||
bodyB.Dirty();
|
||||
// Note: creating a joint doesn't wake the bodies.
|
||||
}
|
||||
|
||||
public void RemoveJoint(Joint joint)
|
||||
{
|
||||
_queuedJointRemove.Add(joint);
|
||||
if (!Joints.Contains(joint))
|
||||
{
|
||||
Logger.ErrorS("physics", $"Tried to remove joint id: {joint.ID} owner: {joint.BodyA.Owner} that's not on map");
|
||||
return;
|
||||
}
|
||||
|
||||
bool collideConnected = joint.CollideConnected;
|
||||
|
||||
// Remove from the world list.
|
||||
Joints.Remove(joint);
|
||||
|
||||
// Disconnect from island graph.
|
||||
PhysicsComponent bodyA = joint.BodyA;
|
||||
PhysicsComponent bodyB = joint.BodyB;
|
||||
|
||||
// Wake up connected bodies.
|
||||
bodyA.Awake = true;
|
||||
bodyB.Awake = true;
|
||||
|
||||
// Remove from body 1.
|
||||
if (joint.EdgeA.Prev != null)
|
||||
{
|
||||
joint.EdgeA.Prev.Next = joint.EdgeA.Next;
|
||||
}
|
||||
|
||||
if (joint.EdgeA.Next != null)
|
||||
{
|
||||
joint.EdgeA.Next.Prev = joint.EdgeA.Prev;
|
||||
}
|
||||
|
||||
if (joint.EdgeA == bodyA.JointEdges)
|
||||
{
|
||||
bodyA.JointEdges = joint.EdgeA.Next;
|
||||
}
|
||||
|
||||
joint.EdgeA.Prev = null;
|
||||
joint.EdgeA.Next = null;
|
||||
|
||||
// Remove from body 2
|
||||
if (joint.EdgeB.Prev != null)
|
||||
{
|
||||
joint.EdgeB.Prev.Next = joint.EdgeB.Next;
|
||||
}
|
||||
|
||||
if (joint.EdgeB.Next != null)
|
||||
{
|
||||
joint.EdgeB.Next.Prev = joint.EdgeB.Prev;
|
||||
}
|
||||
|
||||
if (joint.EdgeB == bodyB.JointEdges)
|
||||
{
|
||||
bodyB.JointEdges = joint.EdgeB.Next;
|
||||
}
|
||||
|
||||
joint.EdgeB.Prev = null;
|
||||
joint.EdgeB.Next = null;
|
||||
|
||||
// If the joint prevents collisions, then flag any contacts for filtering.
|
||||
if (!collideConnected)
|
||||
{
|
||||
ContactEdge? edge = bodyB.ContactEdges;
|
||||
while (edge != null)
|
||||
{
|
||||
if (edge.Other == bodyA)
|
||||
{
|
||||
// Flag the contact for filtering at the next time step (where either
|
||||
// body is awake).
|
||||
edge.Contact!.FilterFlag = true;
|
||||
}
|
||||
|
||||
edge = edge.Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -176,8 +308,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
ProcessBodyChanges();
|
||||
ProcessWakeQueue();
|
||||
ProcessSleepQueue();
|
||||
ProcessAddedJoints();
|
||||
ProcessRemovedJoints();
|
||||
}
|
||||
|
||||
private void ProcessBodyChanges()
|
||||
@@ -238,183 +368,6 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
|
||||
_queuedSleep.Clear();
|
||||
}
|
||||
|
||||
private void ProcessAddedJoints()
|
||||
{
|
||||
foreach (var joint in _queuedJointAdd)
|
||||
{
|
||||
// TODO: Optimise dafuk out of this.
|
||||
if (Joints.Contains(joint)) continue;
|
||||
|
||||
// Just end me, I fucken hate how garbage the physics compstate is.
|
||||
|
||||
// because EACH body will have a joint update we needs to check if.
|
||||
|
||||
PhysicsComponent? bodyA;
|
||||
PhysicsComponent? bodyB;
|
||||
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (joint.BodyA == null || joint.BodyB == null)
|
||||
{
|
||||
if (!_entityManager.TryGetEntity(joint.BodyAUid, out var bodyAEntity) ||
|
||||
!_entityManager.TryGetEntity(joint.BodyBUid, out var bodyBEntity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!bodyAEntity.TryGetComponent(out bodyA) ||
|
||||
!bodyBEntity.TryGetComponent(out bodyB))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Need to mark all this shit as nullable coming in from the state probably.
|
||||
joint.BodyA = bodyAEntity.GetComponent<PhysicsComponent>();
|
||||
joint.BodyB = bodyBEntity.GetComponent<PhysicsComponent>();
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyA = joint.BodyA;
|
||||
bodyB = joint.BodyB;
|
||||
}
|
||||
|
||||
// BodyA and BodyB should share joints so we can just check if BodyA already has this joint.
|
||||
for (var je = bodyA.JointEdges; je != null; je = je.Next)
|
||||
{
|
||||
if (je.Joint.Equals(joint)) continue;
|
||||
}
|
||||
|
||||
// Connect to the world list.
|
||||
Joints.Add(joint);
|
||||
|
||||
// Connect to the bodies' doubly linked lists.
|
||||
joint.EdgeA.Joint = joint;
|
||||
joint.EdgeA.Other = bodyB;
|
||||
joint.EdgeA.Prev = null;
|
||||
joint.EdgeA.Next = bodyA.JointEdges;
|
||||
|
||||
if (bodyA.JointEdges != null)
|
||||
bodyA.JointEdges.Prev = joint.EdgeA;
|
||||
|
||||
bodyA.JointEdges = joint.EdgeA;
|
||||
|
||||
joint.EdgeB.Joint = joint;
|
||||
joint.EdgeB.Other = bodyA;
|
||||
joint.EdgeB.Prev = null;
|
||||
joint.EdgeB.Next = bodyB.JointEdges;
|
||||
|
||||
if (bodyB.JointEdges != null)
|
||||
bodyB.JointEdges.Prev = joint.EdgeB;
|
||||
|
||||
bodyB.JointEdges = joint.EdgeB;
|
||||
|
||||
joint.BodyAUid = bodyA.Owner.Uid;
|
||||
joint.BodyBUid = bodyB.Owner.Uid;
|
||||
|
||||
// If the joint prevents collisions, then flag any contacts for filtering.
|
||||
if (!joint.CollideConnected)
|
||||
{
|
||||
ContactEdge? edge = bodyB.ContactEdges;
|
||||
while (edge != null)
|
||||
{
|
||||
if (edge.Other == bodyA)
|
||||
{
|
||||
// Flag the contact for filtering at the next time step (where either
|
||||
// body is awake).
|
||||
edge.Contact!.FilterFlag = true;
|
||||
}
|
||||
|
||||
edge = edge.Next;
|
||||
}
|
||||
}
|
||||
|
||||
bodyA.Dirty();
|
||||
bodyB.Dirty();
|
||||
// Note: creating a joint doesn't wake the bodies.
|
||||
}
|
||||
|
||||
_queuedJointAdd.Clear();
|
||||
}
|
||||
|
||||
private void ProcessRemovedJoints()
|
||||
{
|
||||
foreach (var joint in _queuedJointRemove)
|
||||
{
|
||||
bool collideConnected = joint.CollideConnected;
|
||||
|
||||
// TODO: See above how much I hate joints rn
|
||||
if (!Joints.Contains(joint)) continue;
|
||||
// Remove from the world list.
|
||||
Joints.Remove(joint);
|
||||
|
||||
// Disconnect from island graph.
|
||||
PhysicsComponent bodyA = joint.BodyA;
|
||||
PhysicsComponent bodyB = joint.BodyB;
|
||||
|
||||
// Wake up connected bodies.
|
||||
bodyA.Awake = true;
|
||||
|
||||
bodyB.Awake = true;
|
||||
|
||||
// Remove from body 1.
|
||||
if (joint.EdgeA.Prev != null)
|
||||
{
|
||||
joint.EdgeA.Prev.Next = joint.EdgeA.Next;
|
||||
}
|
||||
|
||||
if (joint.EdgeA.Next != null)
|
||||
{
|
||||
joint.EdgeA.Next.Prev = joint.EdgeA.Prev;
|
||||
}
|
||||
|
||||
if (joint.EdgeA == bodyA.JointEdges)
|
||||
{
|
||||
bodyA.JointEdges = joint.EdgeA.Next;
|
||||
}
|
||||
|
||||
joint.EdgeA.Prev = null;
|
||||
joint.EdgeA.Next = null;
|
||||
|
||||
// Remove from body 2
|
||||
if (joint.EdgeB.Prev != null)
|
||||
{
|
||||
joint.EdgeB.Prev.Next = joint.EdgeB.Next;
|
||||
}
|
||||
|
||||
if (joint.EdgeB.Next != null)
|
||||
{
|
||||
joint.EdgeB.Next.Prev = joint.EdgeB.Prev;
|
||||
}
|
||||
|
||||
if (joint.EdgeB == bodyB.JointEdges)
|
||||
{
|
||||
bodyB.JointEdges = joint.EdgeB.Next;
|
||||
}
|
||||
|
||||
joint.EdgeB.Prev = null;
|
||||
joint.EdgeB.Next = null;
|
||||
|
||||
// If the joint prevents collisions, then flag any contacts for filtering.
|
||||
if (!collideConnected)
|
||||
{
|
||||
ContactEdge? edge = bodyB.ContactEdges;
|
||||
while (edge != null)
|
||||
{
|
||||
if (edge.Other == bodyA)
|
||||
{
|
||||
// Flag the contact for filtering at the next time step (where either
|
||||
// body is awake).
|
||||
edge.Contact!.FilterFlag = true;
|
||||
}
|
||||
|
||||
edge = edge.Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_queuedJointRemove.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -474,7 +427,7 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
// We'll store the WorldAABB on the MoveEvent given a lot of stuff ends up re-calculating it.
|
||||
foreach (var (transform, physics) in _deferredUpdates)
|
||||
{
|
||||
transform.RunDeferred(physics.GetWorldAABB(_mapManager));
|
||||
transform.RunDeferred(physics.GetWorldAABB());
|
||||
}
|
||||
|
||||
_deferredUpdates.Clear();
|
||||
@@ -513,12 +466,11 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
// I tried not running prediction for non-contacted entities but unfortunately it looked like shit
|
||||
// when contact broke so if you want to try that then GOOD LUCK.
|
||||
// prediction && !seed.Predict ||
|
||||
// AHHH need a way to ignore paused for mapping (seed.Paused && !seed.Owner.TryGetComponent(out IMoverComponent)) ||
|
||||
if ((prediction && !seed.Predict) ||
|
||||
(seed.Paused && !seed.IgnorePaused) ||
|
||||
if (prediction && !seed.Predict ||
|
||||
seed.Island ||
|
||||
!seed.CanCollide ||
|
||||
seed.BodyType == BodyType.Static) continue;
|
||||
seed.BodyType == BodyType.Static ||
|
||||
(seed.Paused && !seed.IgnorePaused) ||
|
||||
!seed.CanCollide) continue;
|
||||
|
||||
// Start of a new island
|
||||
_island.Clear();
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Robust.Shared.Physics
|
||||
/// <summary>
|
||||
/// AABB of this entity in world space.
|
||||
/// </summary>
|
||||
Box2 GetWorldAABB(IMapManager? mapManager = null);
|
||||
Box2 GetWorldAABB(Vector2? worldPosition = null, Angle? worldRotation = null);
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this body can collide.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user