Compare commits

..

1 Commits

Author SHA1 Message Date
PJB3005
ee330d0ae9 Make DOOM work
I think I lost this work originally
2025-01-15 23:08:44 +01:00
169 changed files with 2306 additions and 3685 deletions

3
.gitmodules vendored
View File

@@ -13,6 +13,3 @@
[submodule "ManagedHttpListener"]
path = ManagedHttpListener
url = https://github.com/space-wizards/ManagedHttpListener.git
[submodule "Linguini"]
path = Linguini
url = https://github.com/space-wizards/Linguini

Submodule Linguini deleted from 62b0e75b91

View File

@@ -23,48 +23,6 @@
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- name: Box2D
license:
MIT License
Copyright (c) 2019 Erin Catto
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.
- name: Bullet Physics SDK
license:
The files in this repository are licensed under the zlib license, except for the files under 'Extras' and examples/ThirdPartyLibs.
Bullet Continuous Collision Detection and Physics Library
http://bulletphysics.org
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it freely,
subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
- name: Castle Core
license: |
Copyright 2004-2016 Castle Project - http://www.castleproject.org/
@@ -359,43 +317,6 @@
See the License for the specific language governing permissions and
limitations under the License.
- name: Farseer Physics Engine
license:
Microsoft Permissive License (Ms-PL)
This license governs use of the accompanying software.
If you use the software, you accept this license.
If you do not accept the license, do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution,
prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to
make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or
derivative works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark,
and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by
including a complete copy of this license with your distribution. If you distribute any portion of the software in
compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties,
guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change.
To the extent permitted under your local laws, the contributors exclude the implied warranties of
merchantability, fitness for a particular purpose and non-infringement.
- name: Mono.Cecil
license: |
Copyright (c) 2008 - 2015 Jb Evain

View File

@@ -6,8 +6,6 @@ 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;
@@ -63,7 +61,6 @@ 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!;
@@ -97,7 +94,7 @@ namespace Robust.Client.Audio.Midi
if (MathHelper.CloseTo(_volume, value))
return;
_cfgMan.SetCVar(CVars.MidiVolume, value);
_volume = value;
_volumeDirty = true;
}
}
@@ -134,12 +131,6 @@ 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;

View File

@@ -7,7 +7,6 @@ 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;
@@ -204,9 +203,7 @@ 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;
@@ -316,7 +313,6 @@ 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);
@@ -326,27 +322,6 @@ 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)
@@ -453,6 +428,9 @@ 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;
@@ -467,16 +445,36 @@ 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;
NumericsHelpers.Add(audio[..l], audio[l..]);
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]));
}
}
}
for (var i = 0; i < buffers.Length; i++)
@@ -534,16 +532,16 @@ namespace Robust.Client.Audio.Midi
lock(_playerStateLock)
switch (midiEvent.Type)
{
// Note Off - 0x80
case 128:
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
break;
// Note On 0x90
// Note On 0x80
case 144:
_synth.NoteOn(midiEvent.Channel, midiEvent.Key, midiEvent.Velocity);
break;
// Note Off - 0x90
case 128:
_synth.NoteOff(midiEvent.Channel, midiEvent.Key);
break;
// After Touch - 0xA
case 160:
_synth.KeyPressure(midiEvent.Channel, midiEvent.Key, midiEvent.Value);
@@ -578,12 +576,6 @@ namespace Robust.Client.Audio.Midi
case 81:
// System Messages - 0xF0
case 240:
switch (midiEvent.Control)
{
case 11:
_synth.AllNotesOff(midiEvent.Channel);
break;
}
return;
default:
@@ -605,7 +597,7 @@ namespace Robust.Client.Audio.Midi
if (Disposed) return;
var seqEv = (SequencerEvent) midiEvent;
seqEv.Dest = _debugEvents ? _debugRegister : _synthRegister;
seqEv.Dest = _synthRegister;
_sequencer.SendAt(seqEv, time, absolute);
}

View File

@@ -17,7 +17,6 @@ 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?
@@ -32,7 +31,6 @@ 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();
@@ -126,26 +124,6 @@ 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();
@@ -164,7 +142,6 @@ namespace Robust.Client
launcher,
username,
cvars,
logLevels,
connectAddress,
ss14Address,
mountOptions);
@@ -185,7 +162,6 @@ 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.
@@ -199,7 +175,6 @@ Options:
bool launcher,
string? username,
IReadOnlyCollection<(string key, string value)> cVars,
IReadOnlyCollection<(string key, string value)> logLevels,
string connectAddress, string? ss14Address,
MountOptions mountOptions)
{
@@ -209,7 +184,6 @@ Options:
Launcher = launcher;
Username = username;
CVars = cVars;
LogLevels = logLevels;
ConnectAddress = connectAddress;
Ss14Address = ss14Address;
MountOptions = mountOptions;

View File

@@ -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>(HandleConCmdReg);
NetManager.RegisterNetMessage<MsgConCmdAck>(HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME, HandleConCmdReg);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME, HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
Reset();
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));

View File

@@ -17,11 +17,11 @@ namespace Robust.Client.Console
public void Initialize()
{
_netManager.RegisterNetMessage<MsgScriptStop>();
_netManager.RegisterNetMessage<MsgScriptEval>();
_netManager.RegisterNetMessage<MsgScriptStart>();
_netManager.RegisterNetMessage<MsgScriptResponse>(ReceiveScriptResponse);
_netManager.RegisterNetMessage<MsgScriptStartAck>(ReceiveScriptStartAckResponse);
_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);
}
private void ReceiveScriptStartAckResponse(MsgScriptStartAck message)

View File

@@ -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 IEntityLookup _lookup = default!;
[Dependency] private readonly IEntityManager _entityManager = 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(_lookup, _eyeManager));
_overlayManager.AddOverlay(new EntityPositionOverlay(_entityManager, _eyeManager));
}
else
{
@@ -171,7 +171,7 @@ namespace Robust.Client.Debugging
// all entities have a TransformComponent
var transform = physBody.Owner.Transform;
var worldBox = physBody.GetWorldAABB();
var worldBox = physBody.GetWorldAABB(_mapManager);
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 IEntityLookup _lookup;
private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public EntityPositionOverlay(IEntityLookup lookup, IEyeManager eyeManager)
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager)
{
_lookup = lookup;
_entityManager = entityManager;
_eyeManager = eyeManager;
}
@@ -286,17 +286,18 @@ namespace Robust.Client.Debugging
const float stubLength = 0.25f;
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
var viewport = _eyeManager.GetWorldViewport();
foreach (var entity in _lookup.GetEntitiesIntersecting(_eyeManager.CurrentMap, viewport))
foreach (var entity in _entityManager.GetEntities())
{
var transform = entity.Transform;
if (transform.MapID != _eyeManager.CurrentMap ||
!_eyeManager.GetWorldViewport().Contains(transform.WorldPosition))
{
continue;
}
var center = transform.WorldPosition;
var worldRotation = transform.WorldRotation;
var xLine = worldRotation.RotateVec(Vector2.UnitX);
var yLine = worldRotation.RotateVec(Vector2.UnitY);
var xLine = transform.WorldRotation.RotateVec(Vector2.UnitX);
var yLine = transform.WorldRotation.RotateVec(Vector2.UnitY);
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);

View File

@@ -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.Enums;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Timing;
using Robust.Shared.Enums;
using Robust.Shared.Network;
namespace Robust.Client.Debugging
{
@@ -54,7 +54,7 @@ namespace Robust.Client.Debugging
public void Initialize()
{
_net.RegisterNetMessage<MsgRay>(HandleDrawRay);
_net.RegisterNetMessage<MsgRay>(MsgRay.NAME, HandleDrawRay);
}
private void HandleDrawRay(MsgRay msg)

View File

@@ -93,7 +93,7 @@ namespace Robust.Client
// Disable load context usage on content start.
// This prevents Content.Client being loaded twice and things like csi blowing up because of it.
_modLoader.SetUseLoadContext(!ContentStart);
_modLoader.SetEnableSandboxing(Options.Sandboxing);
_modLoader.SetEnableSandboxing(false && Options.Sandboxing);
if (!_modLoader.TryLoadModulesFrom(new ResourcePath("/Assemblies/"), Options.ContentModulePrefix))
{
@@ -202,28 +202,6 @@ 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;
}
}
ProgramShared.PrintRuntimeInfo(_logManager.RootSawmill);
// Figure out user data directory.
var userDataDir = GetUserDataDir();

View File

@@ -1,10 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Prometheus;
using Robust.Client.GameStates;
using Robust.Client.Player;
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;
@@ -17,7 +21,6 @@ namespace Robust.Client.GameObjects
/// </summary>
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClientNetManager _networkManager = default!;
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -64,7 +67,7 @@ namespace Robust.Client.GameObjects
/// <inheritdoc />
public void SetupNetworking()
{
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
}
public override void TickUpdate(float frameTime, Histogram? histogram)
@@ -145,11 +148,7 @@ namespace Robust.Client.GameObjects
return;
case EntityMessageType.SystemMessage:
var msg = message.SystemMessage;
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(_playerManager.LocalPlayer!.Session), msg)!;
ReceivedSystemMessage?.Invoke(this, msg);
ReceivedSystemMessage?.Invoke(this, sessionMsg);
ReceivedSystemMessage?.Invoke(this, message.SystemMessage);
return;
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
@@ -44,29 +44,11 @@ namespace Robust.Client.GameObjects
public bool Enabled
{
get => _enabled;
set
{
if (_enabled == value) return;
_enabled = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
}
set => _enabled = value;
}
[ViewVariables(VVAccess.ReadWrite)]
public bool ContainerOccluded
{
get => _containerOccluded;
set
{
if (_containerOccluded == value) return;
_containerOccluded = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PointLightUpdateEvent());
}
}
private bool _containerOccluded;
public bool ContainerOccluded { get; set; }
/// <summary>
/// Determines if the light mask should automatically rotate with the entity. (like a flashlight)
@@ -135,13 +117,30 @@ namespace Robust.Client.GameObjects
public bool VisibleNested
{
get => _visibleNested;
set => _visibleNested = value;
set
{
if (_visibleNested == value) return;
_visibleNested = value;
if (value)
{
if (Owner.Transform.Parent == null) return;
_lightOnParent = true;
}
else
{
if (!_lightOnParent) return;
_lightOnParent = false;
}
}
}
[DataField("radius")]
private float _radius = 5f;
[DataField("nestedvisible")]
private bool _visibleNested = true;
private bool _lightOnParent;
[DataField("color")]
private Color _color = Color.White;
[DataField("offset")]
@@ -207,6 +206,35 @@ namespace Robust.Client.GameObjects
UpdateMask();
}
/// <inheritdoc />
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
if (message is ParentChangedMessage msg)
{
HandleTransformParentChanged(msg);
}
}
private void HandleTransformParentChanged(ParentChangedMessage obj)
{
// TODO: this does not work for things nested multiply layers deep.
if (!VisibleNested)
{
return;
}
if (obj.NewParent != null && obj.NewParent.IsValid())
{
_lightOnParent = true;
}
else
{
_lightOnParent = false;
}
}
public override void OnRemove()
{
base.OnRemove();
@@ -242,9 +270,4 @@ namespace Robust.Client.GameObjects
PointLightComponent = pointLightComponent;
}
}
internal sealed class PointLightUpdateEvent : EntityEventArgs
{
}
}

View File

@@ -42,12 +42,7 @@ namespace Robust.Client.GameObjects
public override bool Visible
{
get => _visible;
set
{
if (_visible == value) return;
_visible = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new SpriteUpdateEvent());
}
set => _visible = value;
}
[DataField("drawdepth", customTypeSerializer: typeof(ConstantSerializer<DrawDepthTag>))]
@@ -157,7 +152,7 @@ namespace Robust.Client.GameObjects
}
set
{
if (value == null) return;
if(value == null) return;
Layers.Clear();
foreach (var layerDatum in value)
@@ -167,12 +162,11 @@ namespace Robust.Client.GameObjects
if (!string.IsNullOrWhiteSpace(layerDatum.RsiPath))
{
var path = TextureRoot / layerDatum.RsiPath;
if (IoCManager.Resolve<IResourceCache>().TryGetResource(path, out RSIResource? resource))
try
{
layer.RSI = resource.RSI;
layer.RSI = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(path).RSI;
}
else
catch
{
Logger.ErrorS(LogCategory, "Unable to load layer RSI '{0}'.", path);
}
@@ -314,24 +308,13 @@ namespace Robust.Client.GameObjects
}
[DataField("sprite", readOnly: true)] private string? rsi;
[DataField("layers", readOnly: true)] private List<PrototypeLayerData> layerDatums = new();
[DataField("layers", readOnly: true)] private List<PrototypeLayerData> layerDatums = new ();
[DataField("state", readOnly: true)] private string? state;
[DataField("texture", readOnly: true)] private string? texture;
[ViewVariables(VVAccess.ReadWrite)]
public bool ContainerOccluded
{
get => _containerOccluded;
set
{
if (_containerOccluded == value) return;
_containerOccluded = value;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new SpriteUpdateEvent());
}
}
private bool _containerOccluded;
public bool ContainerOccluded { get; set; }
[ViewVariables(VVAccess.ReadWrite)]
public bool TreeUpdateQueued { get; set; }
@@ -365,13 +348,13 @@ namespace Robust.Client.GameObjects
if (!string.IsNullOrWhiteSpace(rsi))
{
var rsiPath = TextureRoot / rsi;
if(IoCManager.Resolve<IResourceCache>().TryGetResource(rsiPath, out RSIResource? resource))
try
{
BaseRSI = resource.RSI;
BaseRSI = IoCManager.Resolve<IResourceCache>().GetResource<RSIResource>(rsiPath).RSI;
}
else
catch (Exception e)
{
Logger.ErrorS(LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath);
Logger.ErrorS(SpriteComponent.LogCategory, "Unable to load RSI '{0}'. Trace:\n{1}", rsiPath, e);
}
}
}
@@ -499,7 +482,7 @@ namespace Robust.Client.GameObjects
public int AddBlankLayer(int? newIndex = null)
{
var layer = new Layer(this) { Visible = false };
var layer = new Layer(this) {Visible = false};
return AddLayer(layer, newIndex);
}
@@ -528,13 +511,13 @@ namespace Robust.Client.GameObjects
public int AddLayer(Texture? texture, int? newIndex = null)
{
var layer = new Layer(this) { Texture = texture };
var layer = new Layer(this) {Texture = texture};
return AddLayer(layer, newIndex);
}
public int AddLayer(RSI.StateId stateId, int? newIndex = null)
{
var layer = new Layer(this) { State = stateId };
var layer = new Layer(this) {State = stateId};
if (BaseRSI != null && BaseRSI.TryGetState(stateId, out var state))
{
layer.AnimationTimeLeft = state.GetDelay(0);
@@ -580,7 +563,7 @@ namespace Robust.Client.GameObjects
public int AddLayer(RSI.StateId stateId, RSI? rsi, int? newIndex = null)
{
var layer = new Layer(this) { State = stateId, RSI = rsi };
var layer = new Layer(this) {State = stateId, RSI = rsi};
if (rsi != null && rsi.TryGetState(stateId, out var state))
{
layer.AnimationTimeLeft = state.GetDelay(0);
@@ -1305,8 +1288,8 @@ namespace Robust.Client.GameObjects
var layerColor = color * layer.Color;
var position = -(Vector2)texture.Size / (2f * EyeManager.PixelsPerMeter) + layer.Offset;
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
var position = -(Vector2) texture.Size / (2f * EyeManager.PixelsPerMeter) + layer.Offset;
var textureSize = texture.Size / (float) EyeManager.PixelsPerMeter;
var quad = Box2.FromDimensions(position, textureSize);
// TODO: Implement layer-specific rotation and scale.
@@ -1326,7 +1309,7 @@ namespace Robust.Client.GameObjects
public static Angle CalcRectWorldAngle(Angle worldAngle, int numDirections)
{
var theta = worldAngle.Theta;
var segSize = (MathF.PI * 2) / (numDirections * 2);
var segSize = (MathF.PI*2) / (numDirections * 2);
var segments = (int)(theta / segSize);
var odd = segments % 2;
var result = theta - (segments * segSize) - (odd * segSize);
@@ -1376,6 +1359,18 @@ 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)
@@ -1426,7 +1421,7 @@ namespace Robust.Client.GameObjects
if (curState == null)
return;
var thestate = (SpriteComponentState)curState;
var thestate = (SpriteComponentState) curState;
Visible = thestate.Visible;
DrawDepth = thestate.DrawDepth;
@@ -1996,13 +1991,13 @@ namespace Robust.Client.GameObjects
switch (layerProp)
{
case "texture":
LayerSetTexture(index, (string)value);
LayerSetTexture(index, (string) value);
return;
case "state":
LayerSetState(index, (string)value);
LayerSetState(index, (string) value);
return;
case "color":
LayerSetColor(index, (Color)value);
LayerSetColor(index, (Color) value);
return;
default:
throw new ArgumentException($"Unknown layer property '{layerProp}'");
@@ -2047,7 +2042,7 @@ namespace Robust.Client.GameObjects
yield break;
}
var dummy = new DummyIconEntity { Prototype = prototype };
var dummy = new DummyIconEntity {Prototype = prototype};
var spriteComponent = dummy.AddComponent<SpriteComponent>();
if (prototype.Components.TryGetValue("Appearance", out _))
@@ -2091,7 +2086,7 @@ namespace Robust.Client.GameObjects
return GetFallbackState(resourceCache);
}
var dummy = new DummyIconEntity { Prototype = prototype };
var dummy = new DummyIconEntity {Prototype = prototype};
var spriteComponent = dummy.AddComponent<SpriteComponent>();
dummy.Delete();
@@ -2128,7 +2123,7 @@ namespace Robust.Client.GameObjects
{
var typeFactory = IoCManager.Resolve<IDynamicTypeFactoryInternal>();
var serializationManager = IoCManager.Resolve<ISerializationManager>();
var comp = (T)typeFactory.CreateInstanceUnchecked(typeof(T));
var comp = (T) typeFactory.CreateInstanceUnchecked(typeof(T));
_components[typeof(T)] = comp;
comp.Owner = this;
@@ -2162,7 +2157,7 @@ namespace Robust.Client.GameObjects
public T GetComponent<T>()
{
return (T)_components[typeof(T)];
return (T) _components[typeof(T)];
}
public IComponent GetComponent(Type type)
@@ -2174,7 +2169,7 @@ namespace Robust.Client.GameObjects
{
component = null;
if (!_components.TryGetValue(typeof(T), out var value)) return false;
component = (T)value;
component = (T) value;
return true;
}
@@ -2230,9 +2225,4 @@ namespace Robust.Client.GameObjects
}
#endregion
}
internal sealed class SpriteUpdateEvent : EntityEventArgs
{
}
}

View File

@@ -254,7 +254,7 @@ namespace Robust.Client.GameObjects
if (!source.SetPosition(entity.Transform.WorldPosition))
{
source.Dispose();
Logger.Warning($"Can't play positional audio \"{stream.Name}\", can't set position.");
Logger.Warning("Can't play positional audio, 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 \"{stream.Name}\", can't set position.");
Logger.Warning("Can't play positional audio, can't set position.");
return null;
}

View File

@@ -1,134 +0,0 @@
#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

View File

@@ -173,24 +173,4 @@ namespace Robust.Client.GameObjects
AttachedEntity = attachedEntity;
}
}
public class PlayerAttachedEvent : EntityEventArgs
{
public PlayerAttachedEvent(IEntity entity)
{
Entity = entity;
}
public IEntity Entity { get; }
}
public class PlayerDetachedEvent : EntityEventArgs
{
public PlayerDetachedEvent(IEntity entity)
{
Entity = entity;
}
public IEntity Entity { get; }
}
}

View File

@@ -27,8 +27,6 @@ namespace Robust.Client.GameObjects
private readonly List<SpriteComponent> _spriteQueue = new();
private readonly List<PointLightComponent> _lightQueue = new();
private HashSet<EntityUid> _checkedChildren = new();
internal DynamicTree<SpriteComponent> GetSpriteTreeForMap(MapId map, GridId grid)
{
return _gridTrees[map][grid].SpriteTree;
@@ -52,58 +50,16 @@ 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, ComponentRemove>(RemoveSprite);
SubscribeLocalEvent<SpriteComponent, SpriteUpdateEvent>(HandleSpriteUpdate);
SubscribeLocalEvent<SpriteComponent, RenderTreeRemoveSpriteEvent>(RemoveSprite);
SubscribeLocalEvent<PointLightComponent, EntMapIdChangedMessage>(LightMapChanged);
SubscribeLocalEvent<PointLightComponent, MoveEvent>(LightMoved);
SubscribeLocalEvent<PointLightComponent, EntParentChangedMessage>(LightParentChanged);
SubscribeLocalEvent<PointLightComponent, PointLightRadiusChangedEvent>(PointLightRadiusChanged);
SubscribeLocalEvent<PointLightComponent, RenderTreeRemoveLightEvent>(RemoveLight);
SubscribeLocalEvent<PointLightComponent, PointLightUpdateEvent>(HandleLightUpdate);
}
private void HandleLightUpdate(EntityUid uid, PointLightComponent component, PointLightUpdateEvent args)
{
if (component.TreeUpdateQueued) return;
QueueLightUpdate(component);
}
private void HandleSpriteUpdate(EntityUid uid, SpriteComponent component, SpriteUpdateEvent args)
{
if (component.TreeUpdateQueued) return;
QueueSpriteUpdate(component);
}
private void AnythingMoved(MoveEvent args)
{
AnythingMovedSubHandler(args.Sender.Transform);
}
private void AnythingMovedSubHandler(ITransformComponent sender)
{
// To avoid doing redundant updates (and we don't need to update a grid's children ever)
if (!_checkedChildren.Add(sender.Owner.Uid) ||
sender.Owner.HasComponent<MapGridComponent>() ||
sender.Owner.HasComponent<MapComponent>()) return;
// 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
@@ -117,12 +73,17 @@ namespace Robust.Client.GameObjects
QueueSpriteUpdate(component);
}
private void SpriteMoved(EntityUid uid, SpriteComponent component, MoveEvent args)
{
QueueSpriteUpdate(component);
}
private void SpriteParentChanged(EntityUid uid, SpriteComponent component, EntParentChangedMessage args)
{
QueueSpriteUpdate(component);
}
private void RemoveSprite(EntityUid uid, SpriteComponent component, ComponentRemove args)
private void RemoveSprite(EntityUid uid, SpriteComponent component, RenderTreeRemoveSpriteEvent args)
{
ClearSprite(component);
}
@@ -147,6 +108,22 @@ namespace Robust.Client.GameObjects
component.TreeUpdateQueued = true;
_spriteQueue.Add(component);
foreach (var child in component.Owner.Transform.Children)
{
QueueSpriteUpdate(child.Owner);
}
}
private void QueueSpriteUpdate(IEntity entity)
{
if (!entity.TryGetComponent(out SpriteComponent? spriteComponent)) return;
QueueSpriteUpdate(spriteComponent);
foreach (var child in entity.Transform.Children)
{
QueueSpriteUpdate(child.Owner);
}
}
#endregion
@@ -156,6 +133,11 @@ namespace Robust.Client.GameObjects
QueueLightUpdate(component);
}
private void LightMoved(EntityUid uid, PointLightComponent component, MoveEvent args)
{
QueueLightUpdate(component);
}
private void LightParentChanged(EntityUid uid, PointLightComponent component, EntParentChangedMessage args)
{
QueueLightUpdate(component);
@@ -191,6 +173,22 @@ namespace Robust.Client.GameObjects
component.TreeUpdateQueued = true;
_lightQueue.Add(component);
foreach (var child in component.Owner.Transform.Children)
{
QueueLightUpdate(child.Owner);
}
}
private void QueueLightUpdate(IEntity entity)
{
if (!entity.TryGetComponent(out PointLightComponent? lightComponent)) return;
QueueLightUpdate(lightComponent);
foreach (var child in entity.Transform.Children)
{
QueueLightUpdate(child.Owner);
}
}
#endregion
@@ -265,19 +263,10 @@ namespace Robust.Client.GameObjects
public override void FrameUpdate(float frameTime)
{
_checkedChildren.Clear();
foreach (var sprite in _spriteQueue)
{
sprite.TreeUpdateQueued = false;
var mapId = sprite.Owner.Transform.MapID;
if (!sprite.Visible || sprite.ContainerOccluded)
{
ClearSprite(sprite);
continue;
}
// If we're on a new map then clear the old one.
if (sprite.IntersectingMapId != mapId)
{
@@ -313,19 +302,14 @@ namespace Robust.Client.GameObjects
sprite.IntersectingGrids.Add(gridId);
}
sprite.TreeUpdateQueued = false;
}
foreach (var light in _lightQueue)
{
light.TreeUpdateQueued = false;
var mapId = light.Owner.Transform.MapID;
if (!light.Enabled || light.ContainerOccluded)
{
ClearLight(light);
continue;
}
// If we're on a new map then clear the old one.
if (light.IntersectingMapId != mapId)
{
@@ -341,7 +325,7 @@ namespace Robust.Client.GameObjects
var intersectingGrids = _mapManager.FindGridIdsIntersecting(mapId, aabb, true).ToList();
// Remove from old
foreach (var gridId in light.IntersectingGrids)
foreach (var gridId in intersectingGrids)
{
if (intersectingGrids.Contains(gridId)) continue;
mapTree[gridId].LightTree.Remove(light);
@@ -360,6 +344,8 @@ namespace Robust.Client.GameObjects
mapTree[gridId].LightTree.AddOrUpdate(light, translated);
light.IntersectingGrids.Add(gridId);
}
light.TreeUpdateQueued = false;
}
_spriteQueue.Clear();
@@ -394,6 +380,18 @@ 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)

View File

@@ -0,0 +1,17 @@
using System;
using Robust.Shared.GameObjects;
namespace Robust.Client.GameObjects
{
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class PlayerAttachedMsg : ComponentMessage
{
}
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class PlayerDetachedMsg : ComponentMessage
{
}
}

View File

@@ -1,5 +1,3 @@
// ReSharper disable once RedundantUsingDirective
// Used in EXCEPTION_TOLERANCE preprocessor
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -7,18 +5,19 @@ 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;
@@ -81,8 +80,8 @@ namespace Robust.Client.GameStates
{
_processor = new GameStateProcessor(_timing);
_network.RegisterNetMessage<MsgState>(HandleStateMessage);
_network.RegisterNetMessage<MsgStateAck>();
_network.RegisterNetMessage<MsgState>(MsgState.NAME, HandleStateMessage);
_network.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME);
_client.RunLevelChanged += RunLevelChanged;
_config.OnValueChanged(CVars.NetInterp, b => _processor.Interpolation = b, true);
@@ -319,8 +318,6 @@ namespace Robust.Client.GameStates
private void ResetPredictedEntities(GameTick curTick)
{
var bus = (EntityEventBus) _entities.EventBus;
foreach (var entity in _entities.GetEntities())
{
// TODO: 99% there's an off-by-one here.
@@ -349,7 +346,6 @@ namespace Robust.Client.GameStates
Logger.DebugS(CVars.NetPredict.Name, $" And also its component {comp.Name}");
// TODO: Handle interpolation.
bus.RaiseComponentEvent(comp, new ComponentHandleState(compState, null));
comp.HandleComponentState(compState, null);
}
}
@@ -397,7 +393,7 @@ namespace Robust.Client.GameStates
private List<EntityUid> ApplyGameState(GameState curState, GameState? nextState)
{
_config.TickProcessMessages();
_mapManager.ApplyGameStatePre(curState.MapData, curState.EntityStates);
_mapManager.ApplyGameStatePre(curState.MapData);
var createdEntities = ApplyEntityStates(curState.EntityStates, curState.EntityDeletions,
nextState?.EntityStates);
_players.ApplyPlayerStates(curState.PlayerStates);
@@ -458,14 +454,12 @@ namespace Robust.Client.GameStates
}
}
var bus = (EntityEventBus) _entities.EventBus;
// Make sure this is done after all entities have been instantiated.
foreach (var kvStates in toApply)
{
var ent = kvStates.Key;
var entity = (Entity) ent;
HandleEntityState(entity.EntityManager.ComponentManager, entity, bus, kvStates.Value.Item1,
HandleEntityState(entity.EntityManager.ComponentManager, entity, kvStates.Value.Item1,
kvStates.Value.Item2);
}
@@ -532,7 +526,7 @@ namespace Robust.Client.GameStates
return created;
}
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityEventBus bus, EntityState? curState,
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityState? curState,
EntityState? nextState)
{
var compStateWork = new Dictionary<uint, (ComponentState? curState, ComponentState? nextState)>();
@@ -590,7 +584,6 @@ namespace Robust.Client.GameStates
{
try
{
bus.RaiseComponentEvent(component, new ComponentHandleState(cur, next));
component.HandleComponentState(cur, next);
}
catch (Exception e)

View File

@@ -44,12 +44,8 @@ namespace Robust.Client.Graphics.Clyde
internal bool IsEfxSupported;
private ISawmill _openALSawmill = default!;
private void _initializeAudio()
{
_openALSawmill = Logger.GetSawmill("clyde.oal");
_audioOpenDevice();
// Create OpenAL context.
@@ -78,9 +74,9 @@ namespace Robust.Client.Graphics.Clyde
_alContextExtensions.Add(extension);
}
_openALSawmill.Debug("OpenAL Vendor: {0}", AL.Get(ALGetString.Vendor));
_openALSawmill.Debug("OpenAL Renderer: {0}", AL.Get(ALGetString.Renderer));
_openALSawmill.Debug("OpenAL Version: {0}", AL.Get(ALGetString.Version));
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));
}
private void _audioOpenDevice()
@@ -93,7 +89,7 @@ namespace Robust.Client.Graphics.Clyde
_openALDevice = ALC.OpenDevice(preferredDevice);
if (_openALDevice == IntPtr.Zero)
{
_openALSawmill.Warning("Unable to open preferred audio device '{0}': {1}. Falling back default.",
Logger.WarningS("clyde.oal", "Unable to open preferred audio device '{0}': {1}. Falling back default.",
preferredDevice, ALC.GetError(ALDevice.Null));
_openALDevice = ALC.OpenDevice(null);
@@ -157,7 +153,7 @@ namespace Robust.Client.Graphics.Clyde
// Clear out finalized audio sources.
while (_sourceDisposeQueue.TryDequeue(out var handles))
{
_openALSawmill.Debug("Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
Logger.DebugS("clyde.oal", "Cleaning out source {0} which finalized in another thread.", handles.sourceHandle);
if (IsEfxSupported) RemoveEfx(handles);
AL.DeleteSource(handles.sourceHandle);
_checkAlError();
@@ -167,7 +163,7 @@ namespace Robust.Client.Graphics.Clyde
// Clear out finalized buffered audio sources.
while (_bufferedSourceDisposeQueue.TryDequeue(out var handles))
{
_openALSawmill.Debug("Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
Logger.DebugS("clyde.oal", "Cleaning out buffered source {0} which finalized in another thread.", handles.sourceHandle);
if (IsEfxSupported) RemoveEfx(handles);
AL.DeleteSource(handles.sourceHandle);
_checkAlError();
@@ -187,6 +183,32 @@ namespace Robust.Client.Graphics.Clyde
if (handles.filterHandle != 0) EFX.DeleteFilter(handles.filterHandle);
}
public AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate)
{
var buffer = AL.GenBuffer();
unsafe
{
fixed (short* ptr = samples)
{
AL.BufferData(
buffer,
channels == 1 ? ALFormat.Mono16 : ALFormat.Stereo16,
(IntPtr) ptr,
samples.Length * 2,
sampleRate);
}
}
_checkAlError();
var handle = new ClydeHandle(_audioSampleBuffers.Count);
_audioSampleBuffers.Add(new LoadedAudioSample(buffer));
// ReSharper disable once PossibleLossOfFraction
var length = TimeSpan.FromSeconds(samples.Length / channels / (double) sampleRate);
return new AudioStream(handle, length, channels);
}
public void SetMasterVolume(float newVolume)
{
AL.Listener(ALListenerf.Gain, _baseGain * newVolume);
@@ -215,24 +237,24 @@ namespace Robust.Client.Graphics.Clyde
return audioSource;
}
private void _checkAlcError(ALDevice device,
private static void _checkAlcError(ALDevice device,
[CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
var error = ALC.GetError(device);
if (error != AlcError.NoError)
{
_openALSawmill.Error("[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
Logger.ErrorS("clyde.oal", "[{0}:{1}] ALC error: {2}", callerMember, callerLineNumber, error);
}
}
private void _checkAlError([CallerMemberName] string callerMember = "",
private static void _checkAlError([CallerMemberName] string callerMember = "",
[CallerLineNumber] int callerLineNumber = -1)
{
var error = AL.GetError();
if (error != ALError.NoError)
{
_openALSawmill.Error("[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
Logger.ErrorS("clyde.oal", "[{0}:{1}] AL error: {2}", callerMember, callerLineNumber, error);
}
}
@@ -334,35 +356,6 @@ 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;
@@ -414,14 +407,14 @@ namespace Robust.Client.Graphics.Clyde
{
_checkDisposed();
AL.SourcePlay(SourceHandle);
_master._checkAlError();
_checkAlError();
}
public void StopPlaying()
{
_checkDisposed();
AL.SourceStop(SourceHandle);
_master._checkAlError();
_checkAlError();
}
public bool IsPlaying
@@ -440,14 +433,14 @@ namespace Robust.Client.Graphics.Clyde
{
_checkDisposed();
AL.GetSource(SourceHandle, ALSourceb.Looping, out var ret);
_master._checkAlError();
_checkAlError();
return ret;
}
set
{
_checkDisposed();
AL.Source(SourceHandle, ALSourceb.Looping, value);
_master._checkAlError();
_checkAlError();
}
}
@@ -455,7 +448,7 @@ namespace Robust.Client.Graphics.Clyde
{
_checkDisposed();
AL.Source(SourceHandle, ALSourceb.SourceRelative, true);
_master._checkAlError();
_checkAlError();
}
public void SetVolume(float decibels)
@@ -469,10 +462,10 @@ namespace Robust.Client.Graphics.Clyde
}
_gain = MathF.Pow(10, decibels / 10);
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
_master._checkAlError();
_checkAlError();
}
public void SetVolumeDirect(float scale)
public void SetVolumeDirect(float decibels)
{
_checkDisposed();
var priorOcclusion = 1f;
@@ -481,9 +474,10 @@ namespace Robust.Client.Graphics.Clyde
AL.GetSource(SourceHandle, ALSourcef.Gain, out var priorGain);
priorOcclusion = priorGain / _gain;
}
_gain = scale;
_gain = decibels;
AL.Source(SourceHandle, ALSourcef.Gain, _gain * priorOcclusion);
_master._checkAlError();
_checkAlError();
}
public void SetOcclusion(float blocks)
@@ -500,7 +494,7 @@ namespace Robust.Client.Graphics.Clyde
gain *= gain * gain;
AL.Source(SourceHandle, ALSourcef.Gain, _gain * gain);
}
_master._checkAlError();
_checkAlError();
}
private void SetOcclusionEfx(float gain, float cutoff)
@@ -520,7 +514,7 @@ namespace Robust.Client.Graphics.Clyde
{
_checkDisposed();
AL.Source(SourceHandle, ALSourcef.SecOffset, seconds);
_master._checkAlError();
_checkAlError();
}
public bool SetPosition(Vector2 position)
@@ -546,7 +540,7 @@ namespace Robust.Client.Graphics.Clyde
#endif
AL.Source(SourceHandle, ALSource3f.Position, x, y, 0);
_master._checkAlError();
_checkAlError();
return true;
}
@@ -573,14 +567,14 @@ namespace Robust.Client.Graphics.Clyde
AL.Source(SourceHandle, ALSource3f.Velocity, x, y, 0);
_master._checkAlError();
_checkAlError();
}
public void SetPitch(float pitch)
{
_checkDisposed();
AL.Source(SourceHandle, ALSourcef.Pitch, pitch);
_master._checkAlError();
_checkAlError();
}
~AudioSource()
@@ -606,7 +600,7 @@ namespace Robust.Client.Graphics.Clyde
if (FilterHandle != 0) EFX.DeleteFilter(FilterHandle);
AL.DeleteSource(SourceHandle);
_master._audioSources.Remove(SourceHandle);
_master._checkAlError();
_checkAlError();
}
SourceHandle = -1;
@@ -656,7 +650,7 @@ namespace Robust.Client.Graphics.Clyde
_checkDisposed();
// ReSharper disable once PossibleInvalidOperationException
AL.SourcePlay(stackalloc int[] {SourceHandle!.Value});
_master._checkAlError();
_checkAlError();
}
public void StopPlaying()
@@ -664,7 +658,7 @@ namespace Robust.Client.Graphics.Clyde
_checkDisposed();
// ReSharper disable once PossibleInvalidOperationException
AL.SourceStop(SourceHandle!.Value);
_master._checkAlError();
_checkAlError();
}
public bool IsPlaying
@@ -690,7 +684,7 @@ namespace Robust.Client.Graphics.Clyde
_mono = false;
// ReSharper disable once PossibleInvalidOperationException
AL.Source(SourceHandle!.Value, ALSourceb.SourceRelative, true);
_master._checkAlError();
_checkAlError();
}
public void SetLooping()
@@ -709,21 +703,7 @@ namespace Robust.Client.Graphics.Clyde
}
_gain = MathF.Pow(10, decibels / 10);
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
_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();
_checkAlError();
}
public void SetOcclusion(float blocks)
@@ -741,7 +721,7 @@ namespace Robust.Client.Graphics.Clyde
AL.Source(SourceHandle!.Value, ALSourcef.Gain, gain * _gain);
}
_master._checkAlError();
_checkAlError();
}
private void SetOcclusionEfx(float gain, float cutoff)
@@ -761,7 +741,7 @@ namespace Robust.Client.Graphics.Clyde
_checkDisposed();
// ReSharper disable once PossibleInvalidOperationException
AL.Source(SourceHandle!.Value, ALSourcef.SecOffset, seconds);
_master._checkAlError();
_checkAlError();
}
public bool SetPosition(Vector2 position)
@@ -778,7 +758,7 @@ namespace Robust.Client.Graphics.Clyde
_mono = true;
// ReSharper disable once PossibleInvalidOperationException
AL.Source(SourceHandle!.Value, ALSource3f.Position, x, y, 0);
_master._checkAlError();
_checkAlError();
return true;
}
@@ -805,7 +785,22 @@ namespace Robust.Client.Graphics.Clyde
AL.Source(SourceHandle!.Value, ALSource3f.Velocity, x, y, 0);
_master._checkAlError();
_checkAlError();
}
public void SetVolumeDirect(float masterVolumeDecay)
{
_checkDisposed();
var priorOcclusion = 1f;
if (!IsEfxSupported)
{
AL.GetSource(SourceHandle!.Value, ALSourcef.Gain, out var priorGain);
priorOcclusion = priorGain / _gain;
}
_gain = masterVolumeDecay;
AL.Source(SourceHandle!.Value, ALSourcef.Gain, _gain * priorOcclusion);
_checkAlError();
}
public void SetPitch(float pitch)
@@ -813,7 +808,7 @@ namespace Robust.Client.Graphics.Clyde
_checkDisposed();
// ReSharper disable once PossibleInvalidOperationException
AL.Source(SourceHandle!.Value, ALSourcef.Pitch, pitch);
_master._checkAlError();
_checkAlError();
}
~BufferedAudioSource()
@@ -844,7 +839,7 @@ namespace Robust.Client.Graphics.Clyde
AL.DeleteSource(SourceHandle.Value);
AL.DeleteBuffers(BufferHandles);
_master._bufferedAudioSources.Remove(SourceHandle.Value);
_master._checkAlError();
_checkAlError();
}
SourceHandle = null;

View File

@@ -387,6 +387,12 @@ namespace Robust.Client.Graphics.Clyde
ref RefList<(SpriteComponent sprite, Matrix3 matrix, Angle worldRot, float yWorldPos)> state,
in SpriteComponent value) =>
{
// TODO: Probably value in storing this as its own DynamicTree
if (value.ContainerOccluded || !value.Visible)
{
return true;
}
var entity = value.Owner;
var transform = entity.Transform;

View File

@@ -149,16 +149,16 @@ namespace Robust.Client.Graphics.Clyde
// FOV FBO.
_fovRenderTarget = CreateRenderTarget((FovMapSize, 2),
new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true),
new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat },
new TextureSampleParameters {WrapMode = TextureWrapMode.Repeat},
nameof(_fovRenderTarget));
if (_hasGLSamplerObjects)
{
_fovFilterSampler = new GLHandle(GL.GenSampler());
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMagFilter, (int)All.Linear);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMinFilter, (int)All.Linear);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapS, (int)All.Repeat);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapT, (int)All.Repeat);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMagFilter, (int) All.Linear);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureMinFilter, (int) All.Linear);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapS, (int) All.Repeat);
GL.SamplerParameter(_fovFilterSampler.Handle, SamplerParameterName.TextureWrapT, (int) All.Repeat);
CheckGlError();
}
@@ -180,17 +180,20 @@ namespace Robust.Client.Graphics.Clyde
_fovCalculationProgram = _compileProgram(depthVert, depthFrag, attribLocations, "Shadow Depth Program");
var debugShader = _resourceCache.GetResource<ShaderSourceResource>("/Shaders/Internal/depth-debug.swsl");
_fovDebugShaderInstance = (ClydeShaderInstance)InstanceShader(debugShader.ClydeHandle);
_fovDebugShaderInstance = (ClydeShaderInstance) InstanceShader(debugShader.ClydeHandle);
ClydeHandle LoadShaderHandle(string path)
{
if (_resourceCache.TryGetResource(path, out ShaderSourceResource? resource))
try
{
return resource.ClydeHandle;
var shaderSource = _resourceCache.GetResource<ShaderSourceResource>(path);
return shaderSource.ClydeHandle;
}
catch (Exception ex)
{
Logger.Warning($"Can't load shader {path}\n{ex.GetType().Name}: {ex.Message}");
return default;
}
Logger.Warning($"Can't load shader {path}\n");
return default;
}
_lightSoftShaderHandle = LoadShaderHandle("/Shaders/Internal/light-soft.swsl");
@@ -211,7 +214,7 @@ namespace Robust.Client.Graphics.Clyde
{
// Calculate maximum distance for the projection based on screen size.
var screenSizeCut = viewport.Size / EyeManager.PixelsPerMeter;
var maxDist = (float)Math.Max(screenSizeCut.X, screenSizeCut.Y);
var maxDist = (float) Math.Max(screenSizeCut.X, screenSizeCut.Y);
// FOV is rendered twice.
// Once with back face culling like regular lighting.
@@ -521,6 +524,12 @@ namespace Robust.Client.Graphics.Clyde
return false;
}
// TODO: Don't insert into trees for these, same as sprites.
if (!light.Enabled || light.ContainerOccluded)
{
return true;
}
var lightPos = transform.WorldMatrix.Transform(light.Offset);
var circle = new Circle(lightPos, light.Radius);
@@ -581,7 +590,7 @@ namespace Robust.Client.Graphics.Clyde
SetupGlobalUniformsImmediate(shader, viewport.LightRenderTarget.Texture);
shader.SetUniformMaybe("size", (Vector2)viewport.WallBleedIntermediateRenderTarget1.Size);
shader.SetUniformMaybe("size", (Vector2) viewport.WallBleedIntermediateRenderTarget1.Size);
shader.SetUniformTextureMaybe(UniIMainTexture, TextureUnit.Texture0);
var size = viewport.WallBleedIntermediateRenderTarget1.Size;
@@ -800,7 +809,7 @@ namespace Robust.Client.Graphics.Clyde
occluderTree.QueryAabb((in OccluderComponent sOccluder) =>
{
var occluder = (ClientOccluderComponent)sOccluder;
var occluder = (ClientOccluderComponent) sOccluder;
var transform = occluder.Owner.Transform;
if (!occluder.Enabled)
{
@@ -884,11 +893,11 @@ namespace Robust.Client.Graphics.Clyde
// DddD
// HHhh
// deflection
arrayVIBuffer[avi++] = (byte)((((vi + 1) & 2) != 0) ? 0 : 255);
arrayVIBuffer[avi++] = (byte) ((((vi + 1) & 2) != 0) ? 0 : 255);
// height
arrayVIBuffer[avi++] = (byte)(((vi & 2) != 0) ? 0 : 255);
arrayVIBuffer[avi++] = (byte) (((vi & 2) != 0) ? 0 : 255);
}
QuadBatchIndexWrite(indexBuffer, ref ii, (ushort)aiBase);
QuadBatchIndexWrite(indexBuffer, ref ii, (ushort) aiBase);
}
// North face (TL/TR)
@@ -922,7 +931,7 @@ namespace Robust.Client.Graphics.Clyde
arrayMaskBuffer[ami + 3] = new Vector2(blX, blY);
// Generate mask indices.
QuadBatchIndexWrite(indexMaskBuffer, ref imi, (ushort)ami);
QuadBatchIndexWrite(indexMaskBuffer, ref imi, (ushort) ami);
ami += 4;
@@ -964,7 +973,7 @@ namespace Robust.Client.Graphics.Clyde
var lightMapSize = GetLightMapSize(viewport.Size);
var lightMapSizeQuart = GetLightMapSize(viewport.Size, true);
var lightMapColorFormat = _hasGLFloatFramebuffers ? RenderTargetColorFormat.R11FG11FB10F : RenderTargetColorFormat.Rgba8;
var lightMapSampleParameters = new TextureSampleParameters { Filter = true };
var lightMapSampleParameters = new TextureSampleParameters {Filter = true};
viewport.LightRenderTarget?.Dispose();
viewport.WallMaskRenderTarget?.Dispose();
@@ -1001,14 +1010,14 @@ namespace Robust.Client.Graphics.Clyde
private Vector2i GetLightMapSize(Vector2i screenSize, bool furtherDivide = false)
{
var divider = (float)_lightmapDivider;
var divider = (float) _lightmapDivider;
if (furtherDivide)
{
divider *= 2;
}
var w = (int)Math.Ceiling(screenSize.X / divider);
var h = (int)Math.Ceiling(screenSize.Y / divider);
var w = (int) Math.Ceiling(screenSize.X / divider);
var h = (int) Math.Ceiling(screenSize.Y / divider);
return (w, h);
}
@@ -1034,7 +1043,7 @@ namespace Robust.Client.Graphics.Clyde
// Shadow FBO.
_shadowRenderTarget = CreateRenderTarget((ShadowMapSize, _maxLightsPerScene),
new RenderTargetFormatParameters(_hasGLFloatFramebuffers ? RenderTargetColorFormat.RG32F : RenderTargetColorFormat.Rgba8, true),
new TextureSampleParameters { WrapMode = TextureWrapMode.Repeat, Filter = true },
new TextureSampleParameters {WrapMode = TextureWrapMode.Repeat, Filter = true},
nameof(_shadowRenderTarget));
}

View File

@@ -248,10 +248,9 @@ namespace Robust.Client.Graphics.Clyde
return new(default, default, 1, name);
}
public AudioStream LoadAudioRaw(ReadOnlySpan<short> samples, int channels, int sampleRate, string? name = null)
public AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate)
{
// TODO: Might wanna actually load this so the length gets reported correctly.
return new(default, default, channels, name);
throw new NotImplementedException();
}
public IClydeAudioSource CreateAudioSource(AudioStream stream)
@@ -329,11 +328,6 @@ namespace Robust.Client.Graphics.Clyde
// Nada.
}
public void SetVolumeDirect(float scale)
{
// Nada.
}
public void SetOcclusion(float blocks)
{
// Nada.
@@ -348,6 +342,11 @@ namespace Robust.Client.Graphics.Clyde
{
// Nada.
}
public void SetVolumeDirect(float masterVolumeDecay)
{
// Nada.
}
}
private sealed class DummyBufferedAudioSource : DummyAudioSource, IClydeBufferedAudioSource

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using Robust.Client.Audio;
namespace Robust.Client.Graphics
@@ -9,7 +8,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);
AudioStream LoadAudioRaw(short[] samples, int channels, int sampleRate);
void SetMasterVolume(float newVolume);

View File

@@ -18,9 +18,9 @@ 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);
void SetVolumeDirect(float masterVolumeDecay);
}
}

View File

@@ -4,7 +4,7 @@ using Robust.Shared.Maths;
namespace Robust.Client.Graphics
{
internal interface IRenderHandle
public interface IRenderHandle
{
DrawingHandleScreen DrawingHandleScreen { get; }
DrawingHandleWorld DrawingHandleWorld { get; }

View File

@@ -1,111 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Utility;
namespace Robust.Client.Map
{
internal class ClientMapManager : MapManager, IClientMapManager
{
public void ApplyGameStatePre(GameStateMapData? data, EntityState[]? entityStates)
[Dependency] private readonly INetManager _netManager = default!;
public void ApplyGameStatePre(GameStateMapData? data)
{
// There was no map data this tick, so nothing to do.
if(data == null)
return;
var createdGrids = data.CreatedGrids != null
? new Dictionary<GridId, GameStateMapData.GridCreationDatum>(data.CreatedGrids)
: null;
// First we need to figure out all the NEW MAPS.
if(data.CreatedMaps != null)
{
DebugTools.Assert(entityStates is not null, "Received new maps, but no entity state.");
foreach (var mapId in data.CreatedMaps)
{
// map already exists from a previous state.
if (_maps.Contains(mapId))
continue;
EntityUid mapEuid = default;
//get shared euid of map comp entity
foreach (var entityState in entityStates!)
{
if(entityState.ComponentStates is null)
continue;
foreach (var compState in entityState.ComponentStates)
{
if (compState is not MapComponentState mapCompState || mapCompState.MapId != mapId)
continue;
mapEuid = entityState.Uid;
goto BreakMapEntSearch;
}
continue;
}
BreakMapEntSearch:
DebugTools.Assert(mapEuid != default, $"Could not find corresponding entity state for new map {mapId}.");
CreateMap(mapId, mapEuid);
CreateMap(mapId);
}
}
// Then make all the grids.
if(data.CreatedGrids != null)
{
DebugTools.Assert(data.GridData is not null, "Received new grids, but GridData was null.");
var gridData = data.GridData != null
? new Dictionary<GridId, GameStateMapData.GridDatum>(data.GridData)
: null;
DebugTools.AssertNotNull(createdGrids);
foreach (var (gridId, creationDatum) in data.CreatedGrids)
{
if (_grids.ContainsKey(gridId))
{
continue;
EntityUid gridEuid = default;
//get shared euid of map comp entity
foreach (var entityState in entityStates!)
{
if(entityState.ComponentStates is null)
continue;
foreach (var compState in entityState.ComponentStates)
{
if (compState is not MapGridComponentState gridCompState || gridCompState.GridIndex != gridId)
continue;
gridEuid = entityState.Uid;
goto BreakGridEntSearch;
}
}
BreakGridEntSearch:
DebugTools.Assert(gridEuid != default, $"Could not find corresponding entity state for new grid {gridId}.");
MapId gridMapId = default;
foreach (var kvData in data.GridData!)
{
if (kvData.Key != gridId)
continue;
gridMapId = kvData.Value.Coordinates.MapId;
break;
}
DebugTools.Assert(gridMapId != default, $"Could not find corresponding gridData for new grid {gridId}.");
CreateGrid(gridMapId, gridId, creationDatum.ChunkSize, gridEuid);
CreateGrid(gridData![gridId].Coordinates.MapId, gridId, creationDatum.ChunkSize);
}
}
// Process all grid updates.
if(data.GridData != null)
{
SuppressOnTileChanged = true;
// Ok good all the grids and maps exist now.
foreach (var (gridId, gridDatum) in data.GridData)
{
var grid = _grids[gridId];
if (grid.ParentMapId != gridDatum.Coordinates.MapId)
{
@@ -151,9 +112,99 @@ namespace Robust.Client.Map
public void ApplyGameStatePost(GameStateMapData? data)
{
DebugTools.Assert(_netManager.IsClient, "Only the client should call this.");
if(data == null) // if there is no data, there is nothing to do!
return;
// maps created on the client in pre-state are linked to client entities
// resolve new maps with their shared component that the server just gave us
// and delete the client entities
if (data.CreatedMaps != null)
{
foreach (var mapId in data.CreatedMaps)
{
// CreateMap should have set this
DebugTools.Assert(_mapEntities.ContainsKey(mapId));
// this was already linked in a previous state.
if(!_mapEntities[mapId].IsClientSide())
continue;
// get the existing client entity for the map.
var cEntity = EntityManager.GetEntity(_mapEntities[mapId]);
// locate the entity that represents this map that was just sent to us
IEntity? sharedMapEntity = null;
var mapComps = EntityManager.ComponentManager.EntityQuery<IMapComponent>(true);
foreach (var mapComp in mapComps)
{
if (!mapComp.Owner.Uid.IsClientSide() && mapComp.WorldMap == mapId)
{
sharedMapEntity = mapComp.Owner;
_mapEntities[mapId] = mapComp.Owner.Uid;
Logger.DebugS("map", $"Map {mapId} pivoted bound entity from {cEntity.Uid} to {mapComp.Owner.Uid}.");
break;
}
}
// verify shared entity was found (the server sent us one)
DebugTools.AssertNotNull(sharedMapEntity);
DebugTools.Assert(!_mapEntities[mapId].IsClientSide());
// Transfer client child grids made in GameStatePre to the shared component
// so they are not deleted
foreach (var childGridTrans in cEntity.Transform.Children.ToList())
{
childGridTrans.AttachParent(sharedMapEntity!);
}
// remove client entity
var cGridComp = cEntity.GetComponent<IMapComponent>();
cGridComp.ClearMapId();
cEntity.Delete();
}
}
// grids created on the client in pre-state are linked to client entities
// resolve new grids with their shared component that the server just gave us
// and delete the client entities
if (data.CreatedGrids != null)
{
foreach (var kvNewGrid in data.CreatedGrids)
{
var grid = _grids[kvNewGrid.Key];
// this was already linked in a previous state.
if(!grid.GridEntityId.IsClientSide())
continue;
// remove the existing client entity.
var cEntity = EntityManager.GetEntity(grid.GridEntityId);
var cGridComp = cEntity.GetComponent<IMapGridComponent>();
// prevents us from deleting the grid when deleting the grid entity
if(cEntity.Uid.IsClientSide())
cGridComp.ClearGridId();
cEntity.Delete(); // normal entities are already parented to the shared comp, client comp has no children
var gridComps = EntityManager.ComponentManager.EntityQuery<IMapGridComponent>(true);
foreach (var gridComp in gridComps)
{
if (gridComp.GridIndex == kvNewGrid.Key)
{
grid.GridEntityId = gridComp.Owner.Uid;
Logger.DebugS("map", $"Grid {grid.Index} pivoted bound entity from {cEntity.Uid} to {grid.GridEntityId}.");
break;
}
}
DebugTools.Assert(!grid.GridEntityId.IsClientSide());
}
}
if(data.DeletedGrids != null)
{
foreach (var grid in data.DeletedGrids)

View File

@@ -1,4 +1,3 @@
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
@@ -8,7 +7,7 @@ namespace Robust.Client.Map
{
// Two methods here, so that new grids etc can be made BEFORE entities get states applied,
// but old ones can be deleted after.
void ApplyGameStatePre(GameStateMapData? data, EntityState[]? entityStates);
void ApplyGameStatePre(GameStateMapData? data);
void ApplyGameStatePost(GameStateMapData? data);
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Client.GameObjects;
@@ -25,6 +25,7 @@ namespace Robust.Client.Placement
{
public partial class PlacementManager : IPlacementManager, IDisposable
{
[Dependency] public readonly IPhysicsManager PhysicsManager = default!;
[Dependency] private readonly IClientNetManager NetworkManager = default!;
[Dependency] public readonly IPlayerManager PlayerManager = default!;
[Dependency] public readonly IResourceCache ResourceCache = default!;
@@ -155,7 +156,7 @@ namespace Robust.Client.Placement
{
_drawingShader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
NetworkManager.RegisterNetMessage<MsgPlacement>(HandlePlacementMessage);
NetworkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandlePlacementMessage);
_modeDictionary.Clear();
foreach (var type in ReflectionManager.GetAllChildren<PlacementMode>())

View File

@@ -1,5 +1,6 @@
using System;
using System;
using Robust.Client.GameObjects;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.Network;
@@ -70,10 +71,10 @@ namespace Robust.Client.Player
eye.Current = true;
EntityAttached?.Invoke(new EntityAttachedEventArgs(entity));
entity.SendMessage(null, new PlayerAttachedMsg());
// notify ECS Systems
ControlledEntity.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerAttachSysMessage(ControlledEntity));
ControlledEntity.EntityManager.EventBus.RaiseLocalEvent(ControlledEntity.Uid, new PlayerAttachedEvent(ControlledEntity));
}
/// <summary>
@@ -82,13 +83,13 @@ namespace Robust.Client.Player
public void DetachEntity()
{
var previous = ControlledEntity;
if (previous is {Initialized: true, Deleted: false})
if (previous != null && previous.Initialized && !previous.Deleted)
{
previous.GetComponent<EyeComponent>().Current = false;
previous.SendMessage(null, new PlayerDetachedMsg());
// notify ECS Systems
previous.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerAttachSysMessage(null));
previous.EntityManager.EventBus.RaiseLocalEvent(previous.Uid, new PlayerDetachedEvent(previous));
}
ControlledEntity = null;

View File

@@ -1,6 +1,7 @@
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;
@@ -81,8 +82,8 @@ namespace Robust.Client.Player
{
_client.RunLevelChanged += OnRunLevelChanged;
_network.RegisterNetMessage<MsgPlayerListReq>();
_network.RegisterNetMessage<MsgPlayerList>(HandlePlayerList);
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME);
_network.RegisterNetMessage<MsgPlayerList>(MsgPlayerList.NAME, HandlePlayerList);
}
/// <inheritdoc />

View File

@@ -30,7 +30,7 @@ namespace Robust.Client.Prototypes
{
base.Initialize();
_netManager.RegisterNetMessage<MsgReloadPrototypes>(accept: NetMessageAccept.Server);
_netManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, accept: NetMessageAccept.Server);
_clyde.OnWindowFocused += WindowFocusedChanged;

View File

@@ -463,7 +463,7 @@ namespace Robust.Client.UserInterface
{
}
internal virtual void DrawInternal(IRenderHandle renderHandle)
public virtual void DrawInternal(IRenderHandle renderHandle)
{
Draw(renderHandle.DrawingHandleScreen);
}

View File

@@ -1,4 +1,4 @@
using Robust.Client.GameObjects;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Maths;
@@ -41,14 +41,14 @@ namespace Robust.Client.UserInterface.Controls
return (32, 32) * Scale;
}
internal override void DrawInternal(IRenderHandle renderHandle)
public override void DrawInternal(IRenderHandle renderHandle)
{
if (Sprite == null || Sprite.Deleted)
{
return;
}
renderHandle.DrawEntity(Sprite.Owner, GlobalPixelPosition + PixelSize / 2, Scale * UIScale, OverrideDirection);
renderHandle.DrawEntity(Sprite.Owner, GlobalPixelPosition + PixelSize / 2, Scale, OverrideDirection);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.IoC;
@@ -165,8 +165,6 @@ namespace Robust.Client.UserInterface.CustomControls
protected internal override void MouseExited()
{
base.MouseExited();
if (Resizable && CurrentDrag == DragMode.None)
{
DefaultCursorShape = CursorShape.Arrow;

View File

@@ -36,8 +36,7 @@ namespace Robust.Client.UserInterface.CustomControls
return;
}
var functionsText = string.Join("\n", _inputManager.DownKeyFunctions);
_label.Text = $"Context: {_inputManager.Contexts.ActiveContext.Name}\n{functionsText}";
_label.Text = string.Join("\n", _inputManager.DownKeyFunctions);
}
}
}

View File

@@ -16,7 +16,7 @@ using Robust.Shared.Network.Messages;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using static Robust.Client.ViewVariables.Editors.VVPropEditorNumeric;
using NumberType = Robust.Client.ViewVariables.Editors.VVPropEditorNumeric.NumberType;
namespace Robust.Client.ViewVariables
{
@@ -43,13 +43,17 @@ namespace Robust.Client.ViewVariables
public void Initialize()
{
_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>();
_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);
}
public VVPropEditor PropertyFor(Type? type)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime;
@@ -217,28 +217,6 @@ 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;
}
}
ProgramShared.PrintRuntimeInfo(_log.RootSawmill);
SelfLog.Enable(s => { System.Console.WriteLine("SERILOG ERROR: {0}", s); });
if (!SetupLoki())
@@ -352,7 +330,6 @@ namespace Robust.Server
_watchdogApi.Initialize();
AddFinalStringsToSerializer();
_stringSerializer.LockStrings();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && _config.GetCVar(CVars.SysWinTickPeriod) >= 0)
@@ -365,29 +342,6 @@ 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);
}
using var extraMappedStrings = typeof(BaseServer).Assembly
.GetManifestResourceStream("Robust.Server.ExtraMappedSerializerStrings.txt");
if (extraMappedStrings != null)
{
using var sr = new StreamReader(extraMappedStrings);
string? line;
while ((line = sr.ReadLine()) != null)
{
_stringSerializer.AddString(line);
}
}
}
private bool SetupLoki()
{
var enabled = _config.GetCVar(CVars.LokiEnabled);

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared;
using Robust.Shared.Utility;
@@ -12,7 +12,6 @@ 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?
@@ -22,7 +21,6 @@ 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();
@@ -95,33 +93,13 @@ 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, logLevels, mountOptions);
parsed = new CommandLineArgs(configFile, dataDir, cvars, mountOptions);
return true;
}
@@ -132,19 +110,17 @@ 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, IReadOnlyCollection<(string, string)> logLevels, MountOptions mountOptions)
private CommandLineArgs(string? configFile, string? dataDir, IReadOnlyCollection<(string, string)> cVars, MountOptions mountOptions)
{
ConfigFile = configFile;
DataDir = dataDir;
CVars = cVars;
LogLevels = logLevels;
MountOptions = mountOptions;
}
}

View File

@@ -62,14 +62,15 @@ 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>(ProcessCommand);
NetManager.RegisterNetMessage<MsgConCmdAck>();
NetManager.RegisterNetMessage<MsgConCmd>(MsgConCmd.NAME, ProcessCommand);
NetManager.RegisterNetMessage<MsgConCmdAck>(MsgConCmdAck.NAME);
NetManager.RegisterNetMessage<MsgConCmdReg>(message => HandleRegistrationRequest(message.MsgChannel));
NetManager.RegisterNetMessage<MsgConCmdReg>(MsgConCmdReg.NAME,
message => HandleRegistrationRequest(message.MsgChannel));
}
private void ExecuteInShell(IConsoleShell shell, string command)

View File

@@ -14,7 +14,7 @@ namespace Robust.Server.Debugging
public void Initialize()
{
_net.RegisterNetMessage<MsgRay>();
_net.RegisterNetMessage<MsgRay>(MsgRay.NAME);
// TODO _physics.DebugDrawRay += data => PhysicsOnDebugDrawRay(data);
}

View File

@@ -1 +0,0 @@
fixture-0

View File

@@ -1,3 +1,4 @@
using System;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
@@ -8,7 +9,69 @@ namespace Robust.Server.GameObjects
{
public override string Name => "Actor";
[ViewVariables]
public IPlayerSession PlayerSession { get; internal set; } = default!;
[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();
}
}
/// <summary>
/// Raised on an entity whenever a player attaches to this entity.
/// </summary>
[Serializable]
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class PlayerAttachedMsg : ComponentMessage
{
public IPlayerSession NewPlayer { get; }
public PlayerAttachedMsg(IPlayerSession newPlayer)
{
NewPlayer = newPlayer;
}
}
/// <summary>
/// Raised on an entity whenever a player detaches from this entity.
/// </summary>
[Serializable]
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class PlayerDetachedMsg : ComponentMessage
{
public IPlayerSession OldPlayer { get; }
public PlayerDetachedMsg(IPlayerSession oldPlayer)
{
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; }
}
}

View File

@@ -1,227 +0,0 @@
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 sealed 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)
{
args.Result = Attach(args.Entity, args.Player, args.Force, out var forceKicked);
args.ForceKicked = forceKicked;
}
/// <summary>
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
/// </summary>
/// <param name="entity">The entity to attach the player to</param>
/// <param name="player">The player to attach to the entity</param>
/// <param name="force">Whether to kick any existing players from the entity</param>
/// <returns>Whether the attach succeeded, or not.</returns>
public bool Attach(IEntity entity, IPlayerSession player, bool force = false)
{
return Attach(entity, player, false, out _);
}
/// <summary>
/// Attaches a player session to an entity, optionally kicking any sessions already attached to it.
/// </summary>
/// <param name="entity">The entity to attach the player to</param>
/// <param name="player">The player to attach to the entity</param>
/// <param name="force">Whether to kick any existing players from the entity</param>
/// <param name="forceKicked">The player that was forcefully kicked, or null.</param>
/// <returns>Whether the attach succeeded, or not.</returns>
public bool Attach(IEntity entity, IPlayerSession player, bool force, out IPlayerSession? forceKicked)
{
// Null by default.
forceKicked = null;
// Cannot attach to a deleted entity.
if (entity.Deleted)
{
return false;
}
var uid = 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 (!force)
{
return false;
}
// Set the event's force-kicked session before detaching it.
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>(entity);
actor.PlayerSession = player;
player.SetAttachedEntity(entity);
// The player is fully attached now, raise an event!
RaiseLocalEvent(uid, new PlayerAttachedEvent(entity, player, forceKicked));
return true;
}
// Not gonna make this method call Detach as all we have to do is remove a component...
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;
}
/// <summary>
/// Detaches an attached session from the entity, if any.
/// </summary>
/// <param name="entity">The entity player sessions will be detached from.</param>
/// <returns>Whether any player session was detached.</returns>
public bool Detach(IEntity entity)
{
if (!entity.HasComponent<ActorComponent>())
return false;
// Removing the component will call shutdown, and our subscription will handle the rest of the detach logic.
entity.RemoveComponent<ActorComponent>();
return true;
}
/// <summary>
/// Detaches this player from its attached entity, if any.
/// </summary>
/// <param name="player">The player session that will be detached from any attached entities.</param>
/// <returns>Whether the player is now detached from any entities.
/// This returns true if the player wasn't attached to any entity.</returns>
public bool Detach(IPlayerSession player)
{
var entity = player.AttachedEntity;
return entity == null || Detach(entity);
}
private void OnActorShutdown(EntityUid uid, ActorComponent component, ComponentShutdown args)
{
component.PlayerSession.SetAttachedEntity(null);
var entity = EntityManager.GetEntity(uid);
// 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 sealed 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 sealed 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 sealed class PlayerAttachedEvent : EntityEventArgs
{
public IEntity Entity { get; }
public IPlayerSession Player { get; }
/// <summary>
/// The player session that was forcefully kicked from the entity, if any.
/// </summary>
public IPlayerSession? Kicked { get; }
public PlayerAttachedEvent(IEntity entity, IPlayerSession player, IPlayerSession? kicked = null)
{
Entity = entity;
Player = player;
Kicked = kicked;
}
}
/// <summary>
/// Event for when a player has been detached from an entity.
/// </summary>
public sealed class PlayerDetachedEvent : EntityEventArgs
{
public IEntity Entity { get; }
public IPlayerSession Player { get; }
public PlayerDetachedEvent(IEntity entity, IPlayerSession player)
{
Entity = entity;
Player = player;
}
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
@@ -8,7 +7,7 @@ namespace Robust.Server.GameObjects
internal sealed class GridTileLookupNode
{
internal GridTileLookupChunk ParentChunk { get; }
internal Vector2i Indices { get; }
internal IEnumerable<IEntity> Entities
@@ -18,18 +17,7 @@ namespace Robust.Server.GameObjects
foreach (var entity in _entities)
{
if (!entity.Deleted)
{
yield return entity;
}
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager)) continue;
foreach (var container in containerManager.GetAllContainers())
{
foreach (var child in container.ContainedEntities)
{
yield return child;
}
}
}
}
}
@@ -41,7 +29,7 @@ namespace Robust.Server.GameObjects
ParentChunk = parentChunk;
Indices = indices;
}
internal void AddEntity(IEntity entity)
{
_entities.Add(entity);

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
@@ -17,7 +16,6 @@ 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 =
@@ -200,21 +198,17 @@ namespace Robust.Server.GameObjects
private Box2 GetEntityBox(IEntity entity)
{
var aabb = _lookup.GetWorldAabbFromEntity(entity);
// Need to clip the aabb as anything with an edge intersecting another tile might be picked up, such as walls.
return aabb.Scale(0.98f);
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);
}
public override void Initialize()
{
base.Initialize();
#if DEBUG
SubscribeNetworkEvent<RequestGridTileLookupMessage>(HandleRequest);
#endif
SubscribeLocalEvent<MoveEvent>(HandleEntityMove);
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
SubscribeLocalEvent<EntRemovedFromContainerMessage>(HandleContainerRemove);
SubscribeLocalEvent<EntityInitializedMessage>(HandleEntityInitialized);
SubscribeLocalEvent<EntityDeletedMessage>(HandleEntityDeleted);
_mapManager.OnGridCreated += HandleGridCreated;
@@ -222,16 +216,6 @@ namespace Robust.Server.GameObjects
_mapManager.TileChanged += HandleTileChanged;
}
private void HandleContainerRemove(EntRemovedFromContainerMessage ev)
{
HandleEntityAdd(ev.Entity);
}
private void HandleContainerInsert(EntInsertedIntoContainerMessage ev)
{
HandleEntityRemove(ev.Entity);
}
public override void Shutdown()
{
base.Shutdown();
@@ -241,21 +225,8 @@ 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)
{
if (message.Entity.IsInContainer())
{
return;
}
HandleEntityAdd(message.Entity);
}

View File

@@ -9,6 +9,7 @@ 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;
@@ -107,7 +108,7 @@ namespace Robust.Server.GameObjects
/// <inheritdoc />
public void SetupNetworking()
{
_networkManager.RegisterNetMessage<MsgEntity>(HandleEntityNetworkMessage);
_networkManager.RegisterNetMessage<MsgEntity>(MsgEntity.NAME, HandleEntityNetworkMessage);
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
@@ -232,7 +233,6 @@ namespace Robust.Server.GameObjects
var msg = message.SystemMessage;
var sessionType = typeof(EntitySessionMessage<>).MakeGenericType(msg.GetType());
var sessionMsg = Activator.CreateInstance(sessionType, new EntitySessionEventArgs(player), msg)!;
ReceivedSystemMessage?.Invoke(this, msg);
ReceivedSystemMessage?.Invoke(this, sessionMsg);
return;
}

View File

@@ -12,6 +12,7 @@ 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;
@@ -62,8 +63,8 @@ namespace Robust.Server.GameStates
/// <inheritdoc />
public void Initialize()
{
_networkManager.RegisterNetMessage<MsgState>();
_networkManager.RegisterNetMessage<MsgStateAck>(HandleStateAck);
_networkManager.RegisterNetMessage<MsgState>(MsgState.NAME);
_networkManager.RegisterNetMessage<MsgStateAck>(MsgStateAck.NAME, HandleStateAck);
_networkManager.Connected += HandleClientConnected;
_networkManager.Disconnect += HandleClientDisconnect;
@@ -210,10 +211,7 @@ namespace Robust.Server.GameStates
_logger.Log(LogLevel.Error, e, string.Empty);
}
var msg = _networkManager.CreateNetMessage<MsgState>();
msg.MsgChannel = session.ConnectedClient;
return (msg, null);
return (new MsgState(session.ConnectedClient), null);
}
var mailBag = _playerManager.GetAllPlayers()

View File

@@ -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>(HandleNetMessage);
_networkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandleNetMessage);
}
/// <summary>

View File

@@ -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="entity">The entity to attach to.</param>
void AttachToEntity(IEntity? entity);
/// <param name="a">The entity to attach to.</param>
void AttachToEntity(IEntity? a);
/// <summary>
/// Detaches this player from an entity.
@@ -36,12 +36,5 @@ 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);
}
}

View File

@@ -107,8 +107,8 @@ namespace Robust.Server.Player
MaxPlayers = maxPlayers;
_network.RegisterNetMessage<MsgPlayerListReq>(HandlePlayerListReq);
_network.RegisterNetMessage<MsgPlayerList>();
_network.RegisterNetMessage<MsgPlayerListReq>(MsgPlayerListReq.NAME, HandlePlayerListReq);
_network.RegisterNetMessage<MsgPlayerList>(MsgPlayerList.NAME);
_network.Connecting += OnConnecting;
_network.Connected += NewSession;

View File

@@ -6,7 +6,6 @@ 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
{
@@ -37,7 +36,7 @@ namespace Robust.Server.Player
[ViewVariables] public INetChannel ConnectedClient { get; }
[ViewVariables] public IEntity? AttachedEntity { get; set; }
[ViewVariables] public IEntity? AttachedEntity { get; private set; }
[ViewVariables] public EntityUid? AttachedEntityUid => AttachedEntity?.Uid;
@@ -110,17 +109,21 @@ namespace Robust.Server.Player
public event EventHandler<SessionStatusEventArgs>? PlayerStatusChanged;
/// <inheritdoc />
public void AttachToEntity(IEntity? entity)
public void AttachToEntity(IEntity? a)
{
DetachFromEntity();
if (entity == null)
return;
if (!EntitySystem.Get<ActorSystem>().Attach(entity, this))
if (a == null)
{
Logger.Warning($"Couldn't attach player \"{this}\" to entity \"{entity}\"! Did it have a player already attached to it?");
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();
}
/// <inheritdoc />
@@ -129,21 +132,22 @@ namespace Robust.Server.Player
if (AttachedEntity == null)
return;
#if EXCEPTION_TOLERANCE
if (AttachedEntity.Deleted)
{
Logger.Warning($"Player \"{this}\" was attached to an entity that was deleted. THIS SHOULD NEVER HAPPEN, BUT DOES.");
// We can't contact ActorSystem because trying to fire an entity event would crash.
// Work around it.
throw new InvalidOperationException("Tried to detach player, but my entity does not exist!");
}
if (AttachedEntity.TryGetComponent<ActorComponent>(out var actor))
{
AttachedEntity.SendMessage(actor, new PlayerDetachedMsg(this));
AttachedEntity.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PlayerDetachedSystemMessage(AttachedEntity));
AttachedEntity.RemoveComponent<ActorComponent>();
AttachedEntity = null;
UpdatePlayerState();
return;
}
#endif
if (!EntitySystem.Get<ActorSystem>().Detach(AttachedEntity))
else
{
Logger.Warning($"Couldn't detach player \"{this}\" to entity \"{AttachedEntity}\"! Is it missing an ActorComponent?");
throw new InvalidOperationException("Tried to detach player, but entity does not have ActorComponent!");
}
}
@@ -186,13 +190,6 @@ namespace Robust.Server.Player
public LoginType AuthType => ConnectedClient.AuthType;
/// <inheritdoc />
void IPlayerSession.SetAttachedEntity(IEntity? entity)
{
AttachedEntity = entity;
UpdatePlayerState();
}
private void UpdatePlayerState()
{
PlayerState.Status = Status;

View File

@@ -24,7 +24,7 @@ namespace Robust.Server.Prototypes
{
base.Initialize();
_netManager.RegisterNetMessage<MsgReloadPrototypes>(HandleReloadPrototypes, NetMessageAccept.Server);
_netManager.RegisterNetMessage<MsgReloadPrototypes>(MsgReloadPrototypes.NAME, HandleReloadPrototypes, NetMessageAccept.Server);
}
private void HandleReloadPrototypes(MsgReloadPrototypes msg)

View File

@@ -29,9 +29,6 @@
<Content Include="server_config.toml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<EmbeddedResource Include="ExtraMappedSerializerStrings.txt">
<LogicalName>Robust.Server.ExtraMappedSerializerStrings.txt</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Import Project="..\MSBuild\Robust.Engine.targets" />
</Project>

View File

@@ -36,11 +36,11 @@ namespace Robust.Server.Scripting
public void Initialize()
{
_netManager.RegisterNetMessage<MsgScriptStop>(ReceiveScriptEnd);
_netManager.RegisterNetMessage<MsgScriptEval>(ReceiveScriptEval);
_netManager.RegisterNetMessage<MsgScriptStart>(ReceiveScriptStart);
_netManager.RegisterNetMessage<MsgScriptResponse>();
_netManager.RegisterNetMessage<MsgScriptStartAck>();
_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);
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
}

View File

@@ -13,7 +13,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using static Robust.Shared.Network.Messages.MsgViewVariablesDenySession;
using DenyReason = Robust.Shared.Network.Messages.MsgViewVariablesDenySession.DenyReason;
namespace Robust.Server.ViewVariables
{
@@ -34,13 +34,16 @@ namespace Robust.Server.ViewVariables
public void Initialize()
{
_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>();
_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);
}
private void _msgCloseSession(MsgViewVariablesCloseSession message)

View File

@@ -0,0 +1,45 @@
using System;
using System.Runtime.CompilerServices;
namespace Robust.Shared.Maths
{
[Obsolete("Use MathHelper instead.")]
public static class FloatMath
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Clamp01(float val)
{
return MathHelper.Clamp01(val);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Clamp<T>(T val, T min, T max) where T : IComparable<T>
{
return MathHelper.Clamp(val, min, max);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Clamp(float val, float min, float max)
{
return MathHelper.Clamp(val, min, max);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Clamp(double val, double min, double max)
{
return MathHelper.Clamp(val, min, max);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool CloseTo(float a, float b, double tolerance = .00001)
{
return MathHelper.CloseTo(a, b, tolerance);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Lerp(float a, float b, float blend)
{
return MathHelper.Lerp(a, b, blend);
}
}
}

View File

@@ -463,12 +463,5 @@ 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);
}
}

View File

@@ -82,7 +82,7 @@ namespace Robust.Shared.Configuration
_netManager.Disconnect += PeerDisconnected;
}
_netManager.RegisterNetMessage<MsgConVars>(HandleNetVarMessage);
_netManager.RegisterNetMessage<MsgConVars>(MsgConVars.NAME, HandleNetVarMessage);
}
private void PeerConnected(object? sender, NetChannelArgs e)

View File

@@ -145,8 +145,9 @@ namespace Robust.Shared.Containers
{
DebugTools.Assert(!Deleted);
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntInsertedIntoContainerMessage(toinsert, this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntInsertedIntoContainerMessage(toinsert, this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toinsert));
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toinsert, false));
Manager.Dirty();
}
@@ -161,8 +162,9 @@ namespace Robust.Shared.Containers
DebugTools.AssertNotNull(toremove);
DebugTools.Assert(toremove.IsValid());
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntRemovedFromContainerMessage(toremove, this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new EntRemovedFromContainerMessage(toremove, this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new UpdateContainerOcclusionMessage(toremove));
Manager.Owner.SendMessage(Manager, new ContainerContentsModifiedMessage(this, toremove, true));
Manager.Dirty();
}
}

View File

@@ -0,0 +1,40 @@
using System;
using Robust.Shared.GameObjects;
namespace Robust.Shared.Containers
{
/// <summary>
/// The contents of this container have been changed.
/// </summary>
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class ContainerContentsModifiedMessage : ComponentMessage
{
/// <summary>
/// Container whose contents were modified.
/// </summary>
public IContainer Container { get; }
/// <summary>
/// Entity that was added or removed from the container.
/// </summary>
public IEntity Entity { get; }
/// <summary>
/// If true, the entity was removed. If false, it was added to the container.
/// </summary>
public bool Removed { get; }
/// <summary>
/// Constructs a new instance of <see cref="ContainerContentsModifiedMessage"/>.
/// </summary>
/// <param name="container">Container whose contents were modified.</param>
/// <param name="entity">Entity that was added or removed in the container.</param>
/// <param name="removed">If true, the entity was removed. If false, it was added to the container.</param>
public ContainerContentsModifiedMessage(IContainer container, IEntity entity, bool removed)
{
Container = container;
Entity = entity;
Removed = removed;
}
}
}

View File

@@ -197,27 +197,6 @@ 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.

View File

@@ -106,12 +106,6 @@ 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))
@@ -410,8 +404,7 @@ 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)
{

View File

@@ -106,16 +106,8 @@ namespace Robust.Shared.ContentPack
Logger.DebugS("res.mod", $"Verified assemblies in {checkerSw.ElapsedMilliseconds}ms");
}
var nodes = TopologicalSort.FromBeforeAfter(
files,
kv => kv.Key,
kv => kv.Value.Path,
_ => Array.Empty<string>(),
kv => kv.Value.references,
allowMissing: true); // missing refs would be non-content assemblies so allow that.
// Actually load them in the order they depend on each other.
foreach (var path in TopologicalSort.Sort(nodes))
foreach (var path in TopologicalSortModules(files))
{
Logger.DebugS("res.mod", $"Loading module: '{path}'");
try
@@ -144,6 +136,38 @@ namespace Robust.Shared.ContentPack
return true;
}
private static IEnumerable<ResourcePath> TopologicalSortModules(
IEnumerable<KeyValuePair<string, (ResourcePath Path, string[] references)>> modules)
{
var elems = modules.ToDictionary(
node => node.Key,
node => (node.Value.Path, refs: new HashSet<string>(node.Value.references)));
// Remove assembly references we aren't sorting for.
foreach (var (_, set) in elems.Values)
{
set.RemoveWhere(r => !elems.ContainsKey(r));
}
while (elems.Count > 0)
{
var elem = elems.FirstOrNull(x => x.Value.refs.Count == 0);
if (elem == null)
{
throw new InvalidOperationException(
"Found circular dependency in assembly dependency graph");
}
elems.Remove(elem.Value.Key);
foreach (var sElem in elems)
{
sElem.Value.refs.Remove(elem.Value.Key);
}
yield return elem.Value.Value.Path;
}
}
private static (string[] refs, string name) GetAssemblyReferenceData(Stream stream)
{
using var reader = new PEReader(stream);

View File

@@ -1,4 +1,3 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.Log;
@@ -94,21 +93,5 @@ namespace Robust.Shared.GameObjects
return entity.AddComponent<T>();
}
public static IComponent SetAndDirtyIfChanged<TValue>(
this IComponent comp,
ref TValue backingField,
TValue value)
{
if (EqualityComparer<TValue>.Default.Equals(backingField, value))
{
return comp;
}
backingField = value;
comp.Dirty();
return comp;
}
}
}

View File

@@ -21,8 +21,6 @@ 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;

View File

@@ -0,0 +1,45 @@
using System;
using JetBrains.Annotations;
namespace Robust.Shared.GameObjects
{
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class RelayMovementEntityMessage : ComponentMessage
{
[PublicAPI]
public readonly IEntity Entity;
public RelayMovementEntityMessage(IEntity entity)
{
Entity = entity;
}
}
/// <summary>
/// The entity transform parent has been changed.
/// </summary>
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public class ParentChangedMessage : ComponentMessage
{
/// <summary>
/// The new parent of the transform.
/// </summary>
public IEntity? NewParent { get; }
/// <summary>
/// The old parent of the transform.
/// </summary>
public IEntity? OldParent { get; }
/// <summary>
/// Constructs a new instance of <see cref="ParentChangedMessage"/>.
/// </summary>
/// <param name="newParent">The new parent of the transform.</param>
/// <param name="oldParent">The old parent of the transform.</param>
public ParentChangedMessage(IEntity? newParent, IEntity? oldParent)
{
NewParent = newParent;
OldParent = oldParent;
}
}
}

View File

@@ -30,6 +30,12 @@ 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
{

View File

@@ -24,7 +24,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Containers;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -62,9 +61,8 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Store the body's index within the island so we can lookup its data.
/// Key is Island's ID and value is our index.
/// </summary>
public Dictionary<int, int> IslandIndex { get; set; } = new();
public int IslandIndex { get; set; }
// TODO: Actually implement after the initial pr dummy
/// <summary>
@@ -165,11 +163,11 @@ namespace Robust.Shared.GameObjects
if (value)
{
_sleepTime = 0.0f;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsWakeMessage(this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsWakeMessage(this));
}
else
{
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsSleepMessage(this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsSleepMessage(this));
ResetDynamics();
_sleepTime = 0.0f;
}
@@ -263,7 +261,10 @@ namespace Robust.Shared.GameObjects
{
fixture.Body = this;
fixture.ComputeProperties();
fixture.ID = GetFixtureName(fixture);
if (string.IsNullOrEmpty(fixture.Name))
{
fixture.Name = GetFixtureName(fixture);
}
}
ResetMassData();
@@ -284,7 +285,7 @@ namespace Robust.Shared.GameObjects
joints.Add(je.Joint);
}
return new PhysicsComponentState(_canCollide, _sleepingAllowed, _fixedRotation, _bodyStatus, _fixtures, joints, LinearVelocity, AngularVelocity, BodyType);
return new PhysicsComponentState(_canCollide, _sleepingAllowed, _fixedRotation, _bodyStatus, _fixtures, joints, _mass, LinearVelocity, AngularVelocity, BodyType);
}
/// <inheritdoc />
@@ -303,76 +304,60 @@ 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 --
*/
// 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>();
var existingJoints = new List<Joint>();
foreach (var newJoint in newState.Joints)
for (var je = JointEdges; je != null; je = je.Next)
{
var jointFound = false;
existingJoints.Add(je.Joint);
}
foreach (var joint in existingJoints)
var jointsDiff = true;
if (existingJoints.Count == newState.Joints.Count)
{
var anyDiff = false;
for (var i = 0; i < existingJoints.Count; i++)
{
if (joint.ID.Equals(newJoint.ID))
{
if (!newJoint.Equals(joint) && TrySetupNetworkedJoint(newJoint))
{
toAddJoints.Add(newJoint);
toRemoveJoints.Add(joint);
}
var existing = existingJoints[i];
var newJoint = newState.Joints[i];
jointFound = true;
if (!existing.Equals(newJoint))
{
anyDiff = true;
break;
}
}
if (!jointFound && TrySetupNetworkedJoint(newJoint))
if (!anyDiff)
{
toAddJoints.Add(newJoint);
jointsDiff = false;
}
}
foreach (var joint in existingJoints)
if (jointsDiff)
{
var jointFound = false;
ClearJoints();
foreach (var newJoint in newState.Joints)
foreach (var joint in newState.Joints)
{
if (joint.ID.Equals(newJoint.ID))
{
jointFound = true;
break;
}
joint.EdgeA = new JointEdge();
joint.EdgeB = new JointEdge();
// Defer joints given it relies on 2 bodies.
AddJoint(joint);
}
if (jointFound) continue;
toRemoveJoints.Add(joint);
}
foreach (var joint in toRemoveJoints)
{
RemoveJoint(joint);
}
foreach (var joint in toAddJoints)
{
AddJoint(joint);
}
/*
* -- Fixtures --
*/
var toAddFixtures = new List<Fixture>();
var toRemoveFixtures = new List<Fixture>();
var toAdd = new List<Fixture>();
var toRemove = new List<Fixture>();
var computeProperties = false;
// Given a bunch of data isn't serialized need to sort of re-initialise it
@@ -392,12 +377,12 @@ namespace Robust.Shared.GameObjects
foreach (var existing in _fixtures)
{
if (!fixture.ID.Equals(existing.ID)) continue;
if (!fixture.Name.Equals(existing.Name)) continue;
if (!fixture.Equals(existing))
{
toAddFixtures.Add(fixture);
toRemoveFixtures.Add(existing);
toAdd.Add(fixture);
toRemove.Add(existing);
}
found = true;
@@ -406,7 +391,7 @@ namespace Robust.Shared.GameObjects
if (!found)
{
toAddFixtures.Add(fixture);
toAdd.Add(fixture);
}
}
@@ -417,7 +402,7 @@ namespace Robust.Shared.GameObjects
foreach (var fixture in newFixtures)
{
if (fixture.ID.Equals(existing.ID))
if (fixture.Name.Equals(existing.Name))
{
found = true;
break;
@@ -426,18 +411,18 @@ namespace Robust.Shared.GameObjects
if (!found)
{
toRemoveFixtures.Add(existing);
toRemove.Add(existing);
}
}
foreach (var fixture in toRemoveFixtures)
foreach (var fixture in toRemove)
{
computeProperties = true;
RemoveFixture(fixture);
}
// TODO: We also still need event listeners for shapes (Probably need C# events)
foreach (var fixture in toAddFixtures)
foreach (var fixture in toAdd)
{
computeProperties = true;
AddFixture(fixture);
@@ -461,29 +446,6 @@ 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
@@ -493,7 +455,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.ID.Equals(name))
if (fixture.Name.Equals(name))
{
return fixture;
}
@@ -515,23 +477,35 @@ namespace Robust.Shared.GameObjects
Dirty();
}
public Box2 GetWorldAABB(Vector2? worldPos = null, Angle? worldRot = null)
public Box2 GetWorldAABB(IMapManager? mapManager = null)
{
worldPos ??= Owner.Transform.WorldPosition;
worldRot ??= Owner.Transform.WorldRotation;
var worldPosValue = worldPos.Value;
var worldRotValue = worldRot.Value;
var bounds = new Box2(worldPosValue, worldPosValue);
mapManager ??= IoCManager.Resolve<IMapManager>();
var bounds = new Box2();
foreach (var fixture in _fixtures)
{
var boundy = fixture.Shape.CalculateLocalBounds(worldRotValue);
bounds = bounds.Union(boundy.Translated(worldPosValue));
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);
}
}
}
return bounds;
return bounds.IsEmpty() ? Box2.UnitCentered.Translated(Owner.Transform.WorldPosition) : bounds;
}
/// <inheritdoc />
@@ -572,6 +546,7 @@ 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();
@@ -993,46 +968,12 @@ namespace Robust.Shared.GameObjects
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new FixtureUpdateMessage(this, fixture));
}
private string GetFixtureName(Fixture fixture)
internal 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);
@@ -1180,10 +1121,15 @@ 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.RaiseLocalEvent(Owner.Uid, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new PhysicsUpdateMessage(this));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new CollisionChangeMessage(this, Owner.Uid, _canCollide));
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new PhysicsUpdateMessage(this));
}
}
if (EntitySystem.Get<SharedPhysicsSystem>().Maps.TryGetValue(Owner.Transform.MapID, out var map))
{
PhysicsMap = map;
}
}
public void ClearJoints()
@@ -1235,23 +1181,12 @@ 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()
@@ -1359,10 +1294,17 @@ namespace Robust.Shared.GameObjects
if (preventCollideMessage.Cancelled) return false;
preventCollideMessage = new PreventCollideEvent(other, this);
Owner.EntityManager.EventBus.RaiseLocalEvent(other.Owner.Uid, preventCollideMessage);
#pragma warning disable 618
foreach (var comp in Owner.GetAllComponents<ICollideSpecial>())
{
if (comp.PreventCollide(other)) return false;
}
if (preventCollideMessage.Cancelled) return false;
foreach (var comp in other.Owner.GetAllComponents<ICollideSpecial>())
{
if (comp.PreventCollide(this)) return false;
}
#pragma warning restore 618
return true;
}

View File

@@ -18,6 +18,10 @@ namespace Robust.Shared.GameObjects
public readonly List<Fixture> Fixtures;
public readonly List<Joint> Joints;
/// <summary>
/// Current mass of the entity, stored in grams.
/// </summary>
public readonly int Mass;
public readonly Vector2 LinearVelocity;
public readonly float AngularVelocity;
public readonly BodyType BodyType;
@@ -31,6 +35,7 @@ namespace Robust.Shared.GameObjects
/// <param name="status"></param>
/// <param name="fixtures"></param>
/// <param name="joints"></param>
/// <param name="mass">Current Mass of the entity.</param>
/// <param name="linearVelocity">Current linear velocity of the entity in meters per second.</param>
/// <param name="angularVelocity">Current angular velocity of the entity in radians per sec.</param>
/// <param name="bodyType"></param>
@@ -41,6 +46,7 @@ namespace Robust.Shared.GameObjects
BodyStatus status,
List<Fixture> fixtures,
List<Joint> joints,
float mass,
Vector2 linearVelocity,
float angularVelocity,
BodyType bodyType)
@@ -55,6 +61,7 @@ namespace Robust.Shared.GameObjects
LinearVelocity = linearVelocity;
AngularVelocity = angularVelocity;
Mass = (int) Math.Round(mass * 1000); // rounds kg to nearest gram
BodyType = bodyType;
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Linq;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
@@ -33,7 +32,7 @@ namespace Robust.Shared.GameObjects
}
}
internal void RaiseStateChange()
private void RaiseStateChange()
{
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new CollisionWakeStateMessage(), false);
}

View File

@@ -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;
_localRotation = value;
SetRotation(value);
Dirty();
if (!DeferUpdates)
@@ -258,46 +258,17 @@ 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)
{
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);
// offset position from world to parent
_parent = newParentEnt.Uid;
ChangeMapId(newConcrete.MapID);
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntParentChangedMessage(Owner, oldParent?.Owner));
GridID = GetGridIndex();
var newEntity = Owner.EntityManager.GetEntity(value.EntityId);
AttachParent(newEntity);
}
// 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)
@@ -305,6 +276,7 @@ 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));
}
}
@@ -332,7 +304,7 @@ namespace Robust.Shared.GameObjects
_nextPosition = null;
var oldGridPos = Coordinates;
_localPosition = value;
SetPosition(value);
Dirty();
if (!DeferUpdates)
@@ -567,8 +539,13 @@ namespace Robust.Shared.GameObjects
var uid = Owner.Uid;
oldConcrete._children.Remove(uid);
var oldParentOwner = oldParent?.Owner;
var entMessage = new EntParentChangedMessage(Owner, oldParentOwner);
var compMessage = new ParentChangedMessage(null, oldParentOwner);
_parent = EntityUid.Invalid;
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, new EntParentChangedMessage(Owner, oldParent?.Owner));
Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, entMessage);
Owner.SendMessage(this, compMessage);
var oldMapId = MapID;
MapID = MapId.Nullspace;
@@ -579,22 +556,49 @@ namespace Robust.Shared.GameObjects
}
/// <summary>
/// Sets another entity as the parent entity, maintaining world position.
/// Sets another entity as the parent entity.
/// </summary>
/// <param name="newParent"></param>
public virtual void AttachParent(ITransformComponent newParent)
{
//NOTE: This function must be callable from before initialize
// don't attach to something we're already attached to
// nothing to attach to.
if (ParentUid == newParent.Owner.Uid)
return;
DebugTools.Assert(newParent != this,
$"Can't parent a {nameof(ITransformComponent)} to itself.");
// offset position from world to parent, and set
Coordinates = new EntityCoordinates(newParent.Owner.Uid, newParent.InvWorldMatrix.Transform(WorldPosition));
// 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();
}
internal void ChangeMapId(MapId newMapId)
@@ -709,14 +713,14 @@ namespace Robust.Shared.GameObjects
if (LocalRotation != newState.Rotation)
{
_localRotation = newState.Rotation;
SetRotation(newState.Rotation);
rebuildMatrices = true;
}
if (!_localPosition.EqualsApprox(newState.LocalPosition, 0.0001))
{
var oldPos = Coordinates;
_localPosition = newState.LocalPosition;
SetPosition(newState.LocalPosition);
var ev = new MoveEvent(Owner, oldPos, Coordinates);
EntitySystem.Get<SharedTransformSystem>().DeferMoveEvent(ev);
@@ -751,6 +755,17 @@ 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;

View File

@@ -24,15 +24,6 @@ namespace Robust.Shared.GameObjects
void SubscribeEvent<T>(EventSource source, IEntityEventSubscriber subscriber,
EntityEventHandler<T> eventHandler) where T : notnull;
void SubscribeEvent<T>(
EventSource source,
IEntityEventSubscriber subscriber,
EntityEventHandler<T> eventHandler,
Type orderType,
Type[]? before=null,
Type[]? after=null)
where T : notnull;
/// <summary>
/// Unsubscribes all event handlers of a given type.
/// </summary>
@@ -146,7 +137,7 @@ namespace Robust.Shared.GameObjects
return;
// UnsubscribeEvent modifies _inverseEventSubscriptions, requires val to be cached
foreach (var (type, (source, originalHandler, handler, _)) in val.ToList())
foreach (var (type, (source, originalHandler, handler)) in val.ToList())
{
UnsubscribeEvent(source, type, originalHandler, handler, subscriber);
}
@@ -163,36 +154,7 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public void SubscribeEvent<T>(
EventSource source,
IEntityEventSubscriber subscriber,
EntityEventHandler<T> eventHandler)
where T : notnull
{
SubscribeEventCommon(source, subscriber, eventHandler, null);
}
public void SubscribeEvent<T>(
EventSource source,
IEntityEventSubscriber subscriber,
EntityEventHandler<T> eventHandler,
Type orderType,
Type[]? before=null,
Type[]? after=null)
where T : notnull
{
var order = new OrderingData(orderType, before, after);
SubscribeEventCommon(source, subscriber, eventHandler, order);
HandleOrderRegistration(typeof(T), order);
}
private void SubscribeEventCommon<T>(
EventSource source,
IEntityEventSubscriber subscriber,
EntityEventHandler<T> eventHandler,
OrderingData? order)
where T : notnull
public void SubscribeEvent<T>(EventSource source, IEntityEventSubscriber subscriber, EntityEventHandler<T> eventHandler) where T : notnull
{
if (source == EventSource.None)
throw new ArgumentOutOfRangeException(nameof(source));
@@ -204,7 +166,7 @@ namespace Robust.Shared.GameObjects
throw new ArgumentNullException(nameof(subscriber));
var eventType = typeof(T);
var subscriptionTuple = new Registration(source, eventHandler, ev => eventHandler((T) ev), eventHandler, order);
var subscriptionTuple = new Registration(source, eventHandler, ev => eventHandler((T) ev), eventHandler);
if (!_eventSubscriptions.TryGetValue(eventType, out var subscriptions))
_eventSubscriptions.Add(eventType, new List<Registration> {subscriptionTuple});
else if (!subscriptions.Any(p => p.Mask == source && p.Original == (Delegate) eventHandler))
@@ -222,6 +184,7 @@ namespace Robust.Shared.GameObjects
inverseSubscription
);
}
else if (!inverseSubscription.ContainsKey(eventType))
{
inverseSubscription.Add(eventType, subscriptionTuple);
@@ -327,13 +290,15 @@ namespace Robust.Shared.GameObjects
});
}
_awaitingMessages.Add(type, (source, reg, tcs));
return tcs.Task;
}
private void UnsubscribeEvent(EventSource source, Type eventType, Delegate originalHandler, EventHandler handler, IEntityEventSubscriber subscriber)
{
var tuple = new Registration(source, originalHandler, handler, originalHandler, null);
var tuple = new Registration(source, originalHandler, handler, originalHandler);
if (_eventSubscriptions.TryGetValue(eventType, out var subscriptions) && subscriptions.Contains(tuple))
subscriptions.Remove(tuple);
@@ -345,11 +310,7 @@ namespace Robust.Shared.GameObjects
{
var eventType = eventArgs.GetType();
if (_orderedEvents.Contains(eventType))
{
ProcessSingleEventOrdered(source, eventArgs, eventType);
}
else if (_eventSubscriptions.TryGetValue(eventType, out var subs))
if (_eventSubscriptions.TryGetValue(eventType, out var subs))
{
foreach (var handler in subs)
{
@@ -358,20 +319,25 @@ namespace Robust.Shared.GameObjects
}
}
ProcessAwaitingMessages(source, eventArgs, eventType);
if (_awaitingMessages.TryGetValue(eventType, out var awaiting))
{
var (mask, _, tcs) = awaiting;
if ((source & mask) != 0)
{
tcs.TrySetResult(eventArgs);
_awaitingMessages.Remove(eventType);
}
}
}
private void ProcessSingleEvent<T>(EventSource source, T eventArgs) where T : notnull
{
var eventType = typeof(T);
if (_orderedEvents.Contains(eventType))
if (_eventSubscriptions.TryGetValue(eventType, out var subs))
{
ProcessSingleEventOrdered(source, eventArgs, eventType);
}
else if (_eventSubscriptions.TryGetValue(eventType, out var subs))
{
foreach (var (mask, originalHandler, _, _) in subs)
foreach (var (mask, originalHandler, _) in subs)
{
if ((mask & source) != 0)
{
@@ -381,13 +347,6 @@ namespace Robust.Shared.GameObjects
}
}
ProcessAwaitingMessages(source, eventArgs, eventType);
}
// Generic here so we can avoid boxing alloc unless actually awaiting.
private void ProcessAwaitingMessages<T>(EventSource source, T eventArgs, Type eventType)
where T : notnull
{
if (_awaitingMessages.TryGetValue(eventType, out var awaiting))
{
var (mask1, _, tcs) = awaiting;
@@ -407,20 +366,13 @@ namespace Robust.Shared.GameObjects
public readonly Delegate Original;
public readonly EventHandler Handler;
public readonly OrderingData? Ordering;
public Registration(
EventSource mask,
Delegate original,
EventHandler handler,
object equalityToken,
OrderingData? ordering)
public Registration(EventSource mask, Delegate original, EventHandler handler, object equalityToken)
{
Mask = mask;
Original = original;
Handler = handler;
EqualityToken = equalityToken;
Ordering = ordering;
}
public bool Equals(Registration other)
@@ -451,16 +403,11 @@ namespace Robust.Shared.GameObjects
return !left.Equals(right);
}
public void Deconstruct(
out EventSource mask,
out Delegate originalHandler,
out EventHandler handler,
out OrderingData? order)
public void Deconstruct(out EventSource mask, out Delegate originalHandler, out EventHandler handler)
{
mask = Mask;
originalHandler = Original;
handler = Handler;
order = Ordering;
}
}
}

View File

@@ -14,12 +14,6 @@ namespace Robust.Shared.GameObjects
where TComp : IComponent
where TEvent : EntityEventArgs;
void SubscribeLocalEvent<TComp, TEvent>(
ComponentEventHandler<TComp, TEvent> handler,
Type orderType, Type[]? before=null, Type[]? after=null)
where TComp : IComponent
where TEvent : EntityEventArgs;
[Obsolete("Use the overload without the handler argument.")]
void UnsubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
where TComp : IComponent
@@ -67,12 +61,6 @@ namespace Robust.Shared.GameObjects
public void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = true)
where TEvent : EntityEventArgs
{
if (_orderedEvents.Contains(typeof(TEvent)))
{
RaiseLocalOrdered(uid, args, broadcast);
return;
}
_eventTables.Dispatch(uid, typeof(TEvent), args);
// we also broadcast it so the call site does not have to.
@@ -88,24 +76,7 @@ namespace Robust.Shared.GameObjects
void EventHandler(EntityUid uid, IComponent comp, EntityEventArgs args)
=> handler(uid, (TComp) comp, (TEvent) args);
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler, null);
}
public void SubscribeLocalEvent<TComp, TEvent>(
ComponentEventHandler<TComp, TEvent> handler,
Type orderType,
Type[]? before=null,
Type[]? after=null)
where TComp : IComponent
where TEvent : EntityEventArgs
{
void EventHandler(EntityUid uid, IComponent comp, EntityEventArgs args)
=> handler(uid, (TComp) comp, (TEvent) args);
var orderData = new OrderingData(orderType, before, after);
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler, orderData);
HandleOrderRegistration(typeof(TEvent), orderData);
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler);
}
/// <inheritdoc />
@@ -128,13 +99,12 @@ 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;
// EventType -> CompType -> Handler
private Dictionary<Type, Dictionary<Type, (DirectedEventHandler handler, OrderingData? ordering)>> _subscriptions;
private Dictionary<Type, Dictionary<Type, DirectedEventHandler>> _subscriptions;
// prevents shitcode, get your subscriptions figured out before you start spawning entities
private bool _subscriptionLock;
@@ -142,7 +112,6 @@ namespace Robust.Shared.GameObjects
public EventTables(IEntityManager entMan)
{
_entMan = entMan;
_comFac = entMan.ComponentManager.ComponentFactory;
_entMan.EntityAdded += OnEntityAdded;
_entMan.EntityDeleted += OnEntityDeleted;
@@ -177,21 +146,25 @@ namespace Robust.Shared.GameObjects
RemoveComponent(e.OwnerUid, e.Component.GetType());
}
public void Subscribe(Type compType, Type eventType, DirectedEventHandler handler, OrderingData? order)
public void Subscribe(Type compType, Type eventType, DirectedEventHandler handler)
{
if (_subscriptionLock)
throw new InvalidOperationException("Subscription locked.");
if (!_subscriptions.TryGetValue(compType, out var compSubs))
{
compSubs = new Dictionary<Type, (DirectedEventHandler, OrderingData?)>();
compSubs = new Dictionary<Type, DirectedEventHandler>();
_subscriptions.Add(compType, compSubs);
compSubs.Add(eventType, handler);
}
else
{
if (compSubs.ContainsKey(eventType))
throw new InvalidOperationException($"Duplicate Subscriptions for comp={compType.Name}, event={eventType.Name}");
if (compSubs.ContainsKey(eventType))
throw new InvalidOperationException($"Duplicate Subscriptions for comp={compType.Name}, event={eventType.Name}");
compSubs.Add(eventType, (handler, order));
compSubs.Add(eventType, handler);
}
}
public void Unsubscribe(Type compType, Type eventType)
@@ -221,21 +194,18 @@ namespace Robust.Shared.GameObjects
{
var eventTable = _eventTables[euid];
foreach (var type in GetReferences(compType))
if (!_subscriptions.TryGetValue(compType, out var compSubs))
return;
foreach (var kvSub in compSubs)
{
if (!_subscriptions.TryGetValue(type, out var compSubs))
continue;
foreach (var kvSub in compSubs)
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
{
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
{
subscribedComps = new HashSet<Type>();
eventTable.Add(kvSub.Key, subscribedComps);
}
subscribedComps.Add(type);
subscribedComps = new HashSet<Type>();
eventTable.Add(kvSub.Key, subscribedComps);
}
subscribedComps.Add(compType);
}
}
@@ -243,18 +213,15 @@ namespace Robust.Shared.GameObjects
{
var eventTable = _eventTables[euid];
foreach (var type in GetReferences(compType))
if (!_subscriptions.TryGetValue(compType, out var compSubs))
return;
foreach (var kvSub in compSubs)
{
if (!_subscriptions.TryGetValue(type, out var compSubs))
continue;
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
return;
foreach (var kvSub in compSubs)
{
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
return;
subscribedComps.Remove(type);
}
subscribedComps.Remove(compType);
}
}
@@ -270,54 +237,23 @@ namespace Robust.Shared.GameObjects
if(!_subscriptions.TryGetValue(compType, out var compSubs))
return;
if(!compSubs.TryGetValue(eventType, out var sub))
if(!compSubs.TryGetValue(eventType, out var handler))
return;
var (handler, _) = sub;
var component = _entMan.ComponentManager.GetComponent(euid, compType);
handler(euid, component, args);
}
}
public void CollectOrdered(
EntityUid euid,
Type eventType,
List<(EventHandler, OrderingData?)> found)
{
var eventTable = _eventTables[euid];
if(!eventTable.TryGetValue(eventType, out var subscribedComps))
return;
foreach (var compType in subscribedComps)
{
if(!_subscriptions.TryGetValue(compType, out var compSubs))
return;
if(!compSubs.TryGetValue(eventType, out var sub))
return;
var (handler, order) = sub;
var component = _entMan.ComponentManager.GetComponent(euid, compType);
found.Add((ev => handler(euid, component, (EntityEventArgs) ev), order));
}
}
public void DispatchComponent(EntityUid euid, IComponent component, Type eventType, EntityEventArgs args)
{
foreach (var type in GetReferences(component.GetType()))
{
if (!_subscriptions.TryGetValue(type, out var compSubs))
continue;
if (!_subscriptions.TryGetValue(component.GetType(), out var compSubs))
return;
if (!compSubs.TryGetValue(eventType, out var sub))
continue;
if (!compSubs.TryGetValue(eventType, out var handler))
return;
var (handler, _) = sub;
handler(euid, component, args);
}
handler(euid, component, args);
}
public void ClearEntities()
@@ -345,22 +281,6 @@ namespace Robust.Shared.GameObjects
_eventTables = null!;
_subscriptions = null!;
}
/// <summary>
/// Enumerates the type's component references, returning the type itself last.
/// </summary>
private IEnumerable<Type> GetReferences(Type type)
{
var list = _comFac.GetRegistration(type).References;
foreach (var t in list)
{
if (t == type) continue;
yield return t;
}
yield return type;
}
}
/// <inheritdoc />

View File

@@ -1,95 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
internal partial class EntityEventBus
{
// TODO: Topological sort is currently done every time an event is emitted.
// This should be fine for low-volume stuff like interactions, but definitely not for anything high volume.
// Not sure if we could pre-cache the topological sort, here.
// Ordered event raising is slow so if this event has any ordering dependencies we use a slower path.
private readonly HashSet<Type> _orderedEvents = new();
private void ProcessSingleEventOrdered(EventSource source, object eventArgs, Type eventType)
{
var found = new List<(EventHandler, OrderingData?)>();
CollectBroadcastOrdered(source, eventType, found);
DispatchOrderedEvents(eventArgs, found);
}
private void CollectBroadcastOrdered(
EventSource source,
Type eventType,
List<(EventHandler, OrderingData?)> found)
{
if (!_eventSubscriptions.TryGetValue(eventType, out var subs))
return;
foreach (var handler in subs)
{
if ((handler.Mask & source) != 0)
found.Add((handler.Handler, handler.Ordering));
}
}
private void RaiseLocalOrdered<TEvent>(
EntityUid uid,
TEvent args,
bool broadcast)
where TEvent : EntityEventArgs
{
var found = new List<(EventHandler, OrderingData?)>();
if (broadcast)
CollectBroadcastOrdered(EventSource.Local, typeof(TEvent), found);
_eventTables.CollectOrdered(uid, typeof(TEvent), found);
DispatchOrderedEvents(args, found);
if (broadcast)
ProcessAwaitingMessages(EventSource.Local, args, typeof(TEvent));
}
private static void DispatchOrderedEvents(object eventArgs, List<(EventHandler, OrderingData?)> found)
{
var nodes = TopologicalSort.FromBeforeAfter(
found.Where(f => f.Item2 != null),
n => n.Item2!.OrderType,
n => n.Item1!,
n => n.Item2!.Before ?? Array.Empty<Type>(),
n => n.Item2!.After ?? Array.Empty<Type>(),
allowMissing: true);
foreach (var handler in TopologicalSort.Sort(nodes))
{
handler(eventArgs);
}
// Go over all handlers that don't have ordering so weren't included in the sort.
foreach (var (handler, orderData) in found)
{
if (orderData == null)
handler(eventArgs);
}
}
private void HandleOrderRegistration(Type eventType, OrderingData? data)
{
if (data == null)
return;
if (data.Before != null || data.After != null)
_orderedEvents.Add(eventType);
}
private sealed record OrderingData(Type OrderType, Type[]? Before, Type[]? After);
}
}

View File

@@ -142,11 +142,6 @@ namespace Robust.Shared.GameObjects
#region Entity Management
public IEntity CreateEntityUninitialized(string? prototypeName, EntityUid? euid)
{
return CreateEntity(prototypeName, euid);
}
/// <inheritdoc />
public virtual IEntity CreateEntityUninitialized(string? prototypeName)
{

View File

@@ -7,22 +7,16 @@ namespace Robust.Shared.GameObjects
{
private List<SubBase>? _subscriptions;
// NOTE: EntityEventHandler<T> and EntitySessionEventHandler<T> CANNOT BE ORDERED BETWEEN EACH OTHER.
protected void SubscribeNetworkEvent<T>(
EntityEventHandler<T> handler,
Type[]? before=null, Type[]? after=null)
protected void SubscribeNetworkEvent<T>(EntityEventHandler<T> handler)
where T : notnull
{
EntityManager.EventBus.SubscribeEvent(EventSource.Network, this, handler, GetType(), before, after);
EntityManager.EventBus.SubscribeEvent(EventSource.Network, this, handler);
_subscriptions ??= new();
_subscriptions.Add(new SubBroadcast<T>(EventSource.Network));
}
protected void SubscribeNetworkEvent<T>(
EntitySessionEventHandler<T> handler,
Type[]? before=null, Type[]? after=null)
protected void SubscribeNetworkEvent<T>(EntitySessionEventHandler<T> handler)
where T : notnull
{
EntityManager.EventBus.SubscribeSessionEvent(EventSource.Network, this, handler);
@@ -31,20 +25,16 @@ namespace Robust.Shared.GameObjects
_subscriptions.Add(new SubBroadcast<EntitySessionMessage<T>>(EventSource.Network));
}
protected void SubscribeLocalEvent<T>(
EntityEventHandler<T> handler,
Type[]? before=null, Type[]? after=null)
protected void SubscribeLocalEvent<T>(EntityEventHandler<T> handler)
where T : notnull
{
EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, handler, GetType(), before, after);
EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, handler);
_subscriptions ??= new();
_subscriptions.Add(new SubBroadcast<T>(EventSource.Local));
}
protected void SubscribeLocalEvent<T>(
EntitySessionEventHandler<T> handler,
Type[]? before=null, Type[]? after=null)
protected void SubscribeLocalEvent<T>(EntitySessionEventHandler<T> handler)
where T : notnull
{
EntityManager.EventBus.SubscribeSessionEvent(EventSource.Local, this, handler);
@@ -67,9 +57,8 @@ namespace Robust.Shared.GameObjects
EntityManager.EventBus.UnsubscribeEvent<T>(EventSource.Local, this);
}
protected void SubscribeLocalEvent<TComp, TEvent>(
ComponentEventHandler<TComp, TEvent> handler,
Type[]? before=null, Type[]? after=null)
protected void SubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
where TComp : IComponent
where TEvent : EntityEventArgs
{

View File

@@ -9,23 +9,11 @@ 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
{
@@ -33,6 +21,12 @@ 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();
@@ -166,15 +160,15 @@ namespace Robust.Shared.GameObjects
Dictionary<Type, IEntitySystem>.ValueCollection systems,
Dictionary<Type, IEntitySystem> supertypeSystems)
{
var allNodes = new List<TopologicalSort.GraphNode<IEntitySystem>>();
var typeToNode = new Dictionary<Type, TopologicalSort.GraphNode<IEntitySystem>>();
var allNodes = new List<GraphNode<IEntitySystem>>();
var typeToNode = new Dictionary<Type, GraphNode<IEntitySystem>>();
foreach (var system in systems)
{
var node = new TopologicalSort.GraphNode<IEntitySystem>(system);
var node = new GraphNode<IEntitySystem>(system);
typeToNode.Add(system.GetType(), node);
allNodes.Add(node);
typeToNode.Add(system.GetType(), node);
}
foreach (var (type, system) in supertypeSystems)
@@ -183,30 +177,52 @@ namespace Robust.Shared.GameObjects
typeToNode[type] = node;
}
foreach (var node in typeToNode.Values)
foreach (var node in allNodes)
{
foreach (var after in node.Value.UpdatesAfter)
foreach (var after in node.System.UpdatesAfter)
{
var system = typeToNode[after];
system.Dependant.Add(node);
node.DependsOn.Add(system);
}
foreach (var before in node.Value.UpdatesBefore)
foreach (var before in node.System.UpdatesBefore)
{
var system = typeToNode[before];
node.Dependant.Add(system);
system.DependsOn.Add(node);
}
}
var order = TopologicalSort.Sort(allNodes).ToArray();
var order = TopologicalSort(allNodes).Select(p => p.System).ToArray();
var frameUpdate = order.Where(p => NeedsFrameUpdate(p.GetType()));
var update = order.Where(p => NeedsUpdate(p.GetType()));
return (frameUpdate, update);
}
internal static IEnumerable<GraphNode<T>> TopologicalSort<T>(IEnumerable<GraphNode<T>> nodes)
{
var elems = nodes.ToDictionary(node => node,
node => new HashSet<GraphNode<T>>(node.DependsOn));
while (elems.Count > 0)
{
var elem =
elems.FirstOrDefault(x => x.Value.Count == 0);
if (elem.Key == null)
{
throw new InvalidOperationException(
"Found circular dependency when resolving entity system update dependency graph");
}
elems.Remove(elem.Key);
foreach (var selem in elems)
{
selem.Value.Remove(elem.Key);
}
yield return elem.Key;
}
}
private static IEnumerable<Type> GetBaseTypes(Type type) {
if(type.BaseType == null) return type.GetInterfaces();
@@ -252,7 +268,7 @@ namespace Robust.Shared.GameObjects
}
catch (Exception e)
{
_runtimeLog.LogException(e, "entsys");
Logger.ErrorS("entsys", e.ToString());
}
#endif
@@ -277,7 +293,7 @@ namespace Robust.Shared.GameObjects
}
catch (Exception e)
{
_runtimeLog.LogException(e, "entsys");
Logger.ErrorS("entsys", e.ToString());
}
#endif
}
@@ -322,6 +338,18 @@ namespace Robust.Shared.GameObjects
return mFrameUpdate!.DeclaringType != typeof(EntitySystem);
}
[DebuggerDisplay("GraphNode: {" + nameof(System) + "}")]
internal sealed class GraphNode<T>
{
public readonly T System;
public readonly List<GraphNode<T>> DependsOn = new();
public GraphNode(T system)
{
System = system;
}
}
private struct UpdateReg
{
[ViewVariables] public IEntitySystem System;

View File

@@ -252,7 +252,5 @@ 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; }
}
}

View File

@@ -37,8 +37,6 @@ namespace Robust.Shared.GameObjects
event EventHandler<EntityUid>? EntityStarted;
event EventHandler<EntityUid>? EntityDeleted;
IEntity CreateEntityUninitialized(string? prototypeName, EntityUid? euid);
IEntity CreateEntityUninitialized(string? prototypeName);
IEntity CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates);

View File

@@ -12,23 +12,13 @@ namespace Robust.Shared.GameObjects
void MapInit();
}
/// <summary>
/// Raised directed on an entity when the map is initialized.
/// </summary>
public class MapInitEvent : EntityEventArgs
{
}
public static class MapInitExt
{
private static readonly MapInitEvent MapInit = new MapInitEvent();
public static void RunMapInit(this IEntity entity)
{
DebugTools.Assert(entity.LifeStage == EntityLifeStage.Initialized);
entity.LifeStage = EntityLifeStage.MapInitialized;
entity.EntityManager.EventBus.RaiseLocalEvent(entity.Uid, MapInit, false);
foreach (var init in entity.GetAllComponents<IMapInit>())
{
init.MapInit();

View File

@@ -1,61 +1,33 @@
using System.Linq;
using Robust.Shared.Physics;
namespace Robust.Shared.GameObjects
{
public sealed class CollisionWakeSystem : EntitySystem
public class CollisionWakeSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CollisionWakeComponent, EntityInitializedMessage>(HandleInitialize);
SubscribeLocalEvent<CollisionWakeComponent, PhysicsWakeMessage>(HandleWake);
SubscribeLocalEvent<CollisionWakeComponent, PhysicsSleepMessage>(HandleSleep);
SubscribeLocalEvent<PhysicsWakeMessage>(HandleWake);
SubscribeLocalEvent<PhysicsSleepMessage>(HandleSleep);
SubscribeLocalEvent<CollisionWakeComponent, CollisionWakeStateMessage>(HandleCollisionWakeState);
SubscribeLocalEvent<CollisionWakeComponent, JointAddedEvent>(HandleJointAdd);
SubscribeLocalEvent<CollisionWakeComponent, JointRemovedEvent>(HandleJointRemove);
}
private void HandleInitialize(EntityUid uid, CollisionWakeComponent component, EntityInitializedMessage args)
private void HandleWake(PhysicsWakeMessage message)
{
component.RaiseStateChange();
if (!message.Body.Owner.TryGetComponent<CollisionWakeComponent>(out var comp) || !comp.Enabled) return;
message.Body.CanCollide = true;
}
private void HandleJointRemove(EntityUid uid, CollisionWakeComponent component, JointRemovedEvent args)
private void HandleSleep(PhysicsSleepMessage message)
{
if (component.Owner.TryGetComponent(out PhysicsComponent? body) && body.Joints.Any()) return;
// Force an update
component.RaiseStateChange();
}
private void HandleJointAdd(EntityUid uid, CollisionWakeComponent component, JointAddedEvent args)
{
if (!ComponentManager.TryGetComponent(uid, out PhysicsComponent body)) return;
body.CanCollide = true;
}
private void HandleWake(EntityUid uid, CollisionWakeComponent component, PhysicsWakeMessage args)
{
if (!component.Enabled) return;
args.Body.CanCollide = true;
}
private void HandleSleep(EntityUid uid, CollisionWakeComponent component, PhysicsSleepMessage args)
{
if (!component.Enabled) return;
args.Body.CanCollide = false;
if (!message.Body.Owner.TryGetComponent<CollisionWakeComponent>(out var comp) || !comp.Enabled) return;
message.Body.CanCollide = false;
}
private void HandleCollisionWakeState(EntityUid uid, CollisionWakeComponent component, CollisionWakeStateMessage args)
{
if (!ComponentManager.TryGetComponent<PhysicsComponent>(uid, out var body)) return;
body.CanCollide = !component.Enabled || body.Awake || body.Joints.Any();
if(ComponentManager.TryGetComponent<IPhysBody>(uid, out var body))
body.CanCollide = !component.Enabled || body.Awake;
}
}

View File

@@ -278,7 +278,7 @@ namespace Robust.Shared.GameObjects
{
if (entity.TryGetComponent<IPhysBody>(out var component))
{
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(), approximate);
return GetEntitiesIntersecting(entity.Transform.MapID, component.GetWorldAABB(_mapManager), approximate);
}
return GetEntitiesIntersecting(entity.Transform.Coordinates, approximate);
@@ -295,7 +295,7 @@ namespace Robust.Shared.GameObjects
{
if (entity.TryGetComponent(out IPhysBody? component))
{
if (component.GetWorldAABB().Contains(mapPosition))
if (component.GetWorldAABB(_mapManager).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(), range, approximate);
return GetEntitiesInRange(entity.Transform.MapID, component.GetWorldAABB(_mapManager), range, approximate);
}
return GetEntitiesInRange(entity.Transform.Coordinates, range, approximate);
@@ -476,11 +476,10 @@ namespace Robust.Shared.GameObjects
if (ent.Deleted)
return new Box2(0, 0, 0, 0);
var pos = ent.Transform.WorldPosition;
if (ent.TryGetComponent(out IPhysBody? collider))
return collider.GetWorldAABB(pos);
return collider.GetWorldAABB(_mapManager);
var pos = ent.Transform.WorldPosition;
return new Box2(pos, pos);
}

View File

@@ -1,37 +0,0 @@
#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

View File

@@ -14,7 +14,6 @@ using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Reflection;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;
using Logger = Robust.Shared.Log.Logger;
@@ -108,8 +107,6 @@ namespace Robust.Shared.GameObjects
SubscribeLocalEvent<EntRemovedFromContainerMessage>(HandleContainerRemoved);
BuildControllers();
Logger.DebugS("physics", $"Found {_controllers.Count} physics controllers.");
IoCManager.Resolve<IIslandManager>().Initialize();
}
@@ -117,24 +114,45 @@ namespace Robust.Shared.GameObjects
{
var reflectionManager = IoCManager.Resolve<IReflectionManager>();
var typeFactory = IoCManager.Resolve<IDynamicTypeFactory>();
var instantiated = new List<VirtualController>();
var allControllerTypes = new List<Type>();
foreach (var type in reflectionManager.GetAllChildren(typeof(VirtualController)))
{
if (type.IsAbstract)
continue;
instantiated.Add(typeFactory.CreateInstance<VirtualController>(type));
if (type.IsAbstract) continue;
allControllerTypes.Add(type);
}
var nodes = TopologicalSort.FromBeforeAfter(
instantiated,
c => c.GetType(),
c => c,
c => c.UpdatesBefore,
c => c.UpdatesAfter);
var instantiated = new Dictionary<Type, VirtualController>();
_controllers = TopologicalSort.Sort(nodes).ToList();
foreach (var type in allControllerTypes)
{
instantiated.Add(type, (VirtualController) typeFactory.CreateInstance(type));
}
// Build dependency graph, copied from EntitySystemManager *COUGH
var nodes = new Dictionary<Type, EntitySystemManager.GraphNode<VirtualController>>();
foreach (var (_, controller) in instantiated)
{
var node = new EntitySystemManager.GraphNode<VirtualController>(controller);
nodes[controller.GetType()] = node;
}
foreach (var (type, node) in nodes)
{
foreach (var before in instantiated[type].UpdatesBefore)
{
nodes[before].DependsOn.Add(node);
}
foreach (var after in instantiated[type].UpdatesAfter)
{
node.DependsOn.Add(nodes[after]);
}
}
_controllers = GameObjects.EntitySystemManager.TopologicalSort(nodes.Values).Select(c => c.System).ToList();
foreach (var controller in _controllers)
{

View File

@@ -1,16 +0,0 @@
using Robust.Shared.GameObjects;
namespace Robust.Shared.GameStates
{
public class ComponentHandleState : EntityEventArgs
{
public ComponentState? Current { get; }
public ComponentState? Next { get; }
public ComponentHandleState(ComponentState? current, ComponentState? next)
{
Current = current;
Next = next;
}
}
}

View File

@@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Log;
using Robust.Shared.Utility;
namespace Robust.Shared.Input.Binding
{
@@ -14,8 +13,8 @@ namespace Robust.Shared.Input.Binding
// handlers in the order they should be resolved for the given key function.
// internally we use a graph to construct this but we render it down to a flattened
// list so we don't need to do any graph traversal at query time
private Dictionary<BoundKeyFunction, List<InputCmdHandler>> _bindingsForKey = new();
private bool _graphDirty = false;
private Dictionary<BoundKeyFunction, List<InputCmdHandler>> _bindingsForKey =
new();
/// <inheritdoc />
public void Register<TOwner>(CommandBinds commandBinds)
@@ -42,15 +41,12 @@ namespace Robust.Shared.Input.Binding
_bindings.Add(new TypedCommandBind(owner, binding));
}
_graphDirty = true;
RebuildGraph();
}
/// <inheritdoc />
public IEnumerable<InputCmdHandler> GetHandlers(BoundKeyFunction function)
{
if (_graphDirty)
RebuildGraph();
if (_bindingsForKey.TryGetValue(function, out var handlers))
{
return handlers;
@@ -62,8 +58,7 @@ namespace Robust.Shared.Input.Binding
public void Unregister(Type owner)
{
_bindings.RemoveAll(binding => binding.ForType == owner);
_graphDirty = true;
RebuildGraph();
}
/// <inheritdoc />
@@ -72,7 +67,7 @@ namespace Robust.Shared.Input.Binding
Unregister(typeof(TOwner));
}
internal void RebuildGraph()
private void RebuildGraph()
{
_bindingsForKey.Clear();
@@ -82,7 +77,6 @@ namespace Robust.Shared.Input.Binding
}
_graphDirty = false;
}
private Dictionary<BoundKeyFunction, List<TypedCommandBind>> FunctionToBindings()
@@ -111,16 +105,16 @@ namespace Robust.Shared.Input.Binding
//TODO: Probably could be optimized if needed! Generally shouldn't be a big issue since there is a relatively
// tiny amount of bindings
List<TopologicalSort.GraphNode<TypedCommandBind>> allNodes = new();
Dictionary<Type,List<TopologicalSort.GraphNode<TypedCommandBind>>> typeToNode = new();
List<GraphNode> allNodes = new();
Dictionary<Type,List<GraphNode>> typeToNode = new();
// build the dict for quick lookup on type
foreach (var binding in bindingsForFunction)
{
if (!typeToNode.ContainsKey(binding.ForType))
{
typeToNode[binding.ForType] = new List<TopologicalSort.GraphNode<TypedCommandBind>>();
typeToNode[binding.ForType] = new List<GraphNode>();
}
var newNode = new TopologicalSort.GraphNode<TypedCommandBind>(binding);
var newNode = new GraphNode(binding);
typeToNode[binding.ForType].Add(newNode);
allNodes.Add(newNode);
}
@@ -128,7 +122,7 @@ namespace Robust.Shared.Input.Binding
//add the graph edges
foreach (var curBinding in allNodes)
{
foreach (var afterType in curBinding.Value.CommandBind.After)
foreach (var afterType in curBinding.TypedCommandBind.CommandBind.After)
{
// curBinding should always fire after bindings associated with this afterType, i.e.
// this binding DEPENDS ON afterTypes' bindings
@@ -136,12 +130,11 @@ namespace Robust.Shared.Input.Binding
{
foreach (var afterBinding in afterBindings)
{
afterBinding.Dependant.Add(curBinding);
curBinding.DependsOn.Add(afterBinding);
}
}
}
foreach (var beforeType in curBinding.Value.CommandBind.Before)
foreach (var beforeType in curBinding.TypedCommandBind.CommandBind.Before)
{
// curBinding should always fire before bindings associated with this beforeType, i.e.
// beforeTypes' bindings DEPENDS ON this binding
@@ -149,7 +142,7 @@ namespace Robust.Shared.Input.Binding
{
foreach (var beforeBinding in beforeBindings)
{
curBinding.Dependant.Add(beforeBinding);
beforeBinding.DependsOn.Add(curBinding);
}
}
}
@@ -158,7 +151,54 @@ namespace Robust.Shared.Input.Binding
//TODO: Log graph structure for debugging
//use toposort to build the final result
return TopologicalSort.Sort(allNodes).Select(c => c.CommandBind.Handler).ToList();
var topoSorted = TopologicalSort(allNodes, function);
List<InputCmdHandler> result = new();
foreach (var node in topoSorted)
{
result.Add(node.TypedCommandBind.CommandBind.Handler);
}
return result;
}
//Adapted from https://stackoverflow.com/a/24058279
private static IEnumerable<GraphNode> TopologicalSort(IEnumerable<GraphNode> nodes, BoundKeyFunction function)
{
var elems = nodes.ToDictionary(node => node,
node => new HashSet<GraphNode>(node.DependsOn));
while (elems.Count > 0)
{
var elem =
elems.FirstOrDefault(x => x.Value.Count == 0);
if (elem.Key == null)
{
throw new InvalidOperationException("Found circular dependency when resolving" +
$" command binding handler order for key function {function.FunctionName}." +
$" Please check the systems which register bindings for" +
$" this function and eliminate the circular dependency.");
}
elems.Remove(elem.Key);
foreach (var selem in elems)
{
selem.Value.Remove(elem.Key);
}
yield return elem.Key;
}
}
/// <summary>
/// node in our temporary dependency graph
/// </summary>
private class GraphNode
{
public List<GraphNode> DependsOn = new();
public readonly TypedCommandBind TypedCommandBind;
public GraphNode(TypedCommandBind typedCommandBind)
{
TypedCommandBind = typedCommandBind;
}
}
/// <summary>

View File

@@ -33,8 +33,6 @@ namespace Robust.Shared.Input
/// </summary>
/// <param name="function">Function to remove.</param>
void RemoveFunction(BoundKeyFunction function);
string Name { get; }
}
/// <inheritdoc />
@@ -42,25 +40,20 @@ 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, string name)
internal InputCmdContext(IInputCmdContext? parent)
{
_parent = parent;
Name = name;
}
/// <summary>
/// Creates a instance of <see cref="InputCmdContext"/> with no parent.
/// </summary>
internal InputCmdContext(string name)
{
Name = name;
}
internal InputCmdContext() { }
/// <inheritdoc />
public void AddFunction(BoundKeyFunction function)

View File

@@ -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(DefaultContextName));
_contexts.Add(DefaultContextName, new InputCmdContext());
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, uniqueName);
var newContext = new InputCmdContext(parentContext);
_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, uniqueName);
var newContext = new InputCmdContext(parent);
_contexts.Add(uniqueName, newContext);
return newContext;
}

View File

@@ -104,23 +104,6 @@ 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.

View File

@@ -52,7 +52,7 @@ namespace Robust.Shared.Localization
/// Does not log a warning if the message does not exist.
/// Does however log errors if any occur while formatting.
/// </remarks>
bool TryGetString(string messageId, [NotNullWhen(true)] out string? value, params (string, object)[] keyArgs);
bool TryGetString(string messageId, [NotNullWhen(true)] out string? value, params (string, object)[] args);
/// <summary>
/// Default culture used by other methods when no culture is explicitly specified.

View File

@@ -25,7 +25,7 @@ namespace Robust.Shared.Localization
private static ILocalizationManager LocalizationManager => IoCManager.Resolve<ILocalizationManager>();
/// <summary>
/// Gets a language appropriate string represented by the supplied messageId.
/// Gets a language approrpiate string represented by the supplied messageId.
/// </summary>
/// <param name="messageId">Unique Identifier for a translated message.</param>
/// <returns>

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Fluent.Net;
using JetBrains.Annotations;
using Linguini.Bundle;
using Robust.Shared.GameObjects;
namespace Robust.Shared.Localization
@@ -25,13 +25,13 @@ namespace Robust.Shared.Localization
[PublicAPI]
public readonly struct LocContext
{
public CultureInfo Culture => Bundle.Culture;
public CultureInfo Culture => Context.Culture;
internal readonly FluentBundle Bundle;
internal readonly MessageContext Context;
internal LocContext(FluentBundle bundle)
internal LocContext(MessageContext ctx)
{
Bundle = bundle;
Context = ctx;
}
}
@@ -86,7 +86,7 @@ namespace Robust.Shared.Localization
/// <summary>
/// Checks if this value matches a string in a select expression.
/// </summary>
bool Matches(LocContext bundle, string matchValue)
bool Matches(LocContext ctx, string matchValue)
{
return false;
}
@@ -119,7 +119,7 @@ namespace Robust.Shared.Localization
public abstract string Format(LocContext ctx);
/*
public virtual bool Matches(LocContext bundle, string matchValue)
public virtual bool Matches(LocContext ctx, string matchValue)
{
return false;
}
@@ -150,7 +150,6 @@ namespace Robust.Shared.Localization
}
}
/// <summary>
/// Stores an "invalid" string value. Produced by e.g. unresolved variable references.
/// </summary>
@@ -174,13 +173,13 @@ namespace Robust.Shared.Localization
/*public sealed record LocValueBool(bool Value) : LocValue<bool>(Value)
{
public override string Format(LocContext bundle)
public override string Format(LocContext ctx)
{
return Value.ToString(bundle.Culture);
return Value.ToString(ctx.Culture);
}
/*
public override bool Matches(LocContext bundle, string matchValue)
public override bool Matches(LocContext ctx, string matchValue)
{
var word = Value ? "true" : "false";
return word.Equals(matchValue, StringComparison.InvariantCultureIgnoreCase);
@@ -190,14 +189,14 @@ namespace Robust.Shared.Localization
public sealed record LocValueEnum(Enum Value) : LocValue<Enum>(Value)
{
public override string Format(LocContext bundle)
public override string Format(LocContext ctx)
{
return Value.ToString().ToLowerInvariant();
}
/*public override bool Matches(LocContext bundle, string matchValue)
/*public override bool Matches(LocContext ctx, string matchValue)
{
return matchValue.Equals(Value.ToString(), StringComparison.InvariantCultureIgnoreCase);
}#1#
}*/
}
}

View File

@@ -1,30 +0,0 @@
using System;
using System.Text;
using Linguini.Bundle.Errors;
using Linguini.Syntax.Parser.Error;
namespace Robust.Shared.Localization
{
internal static class LocHelper
{
public static string FormatCompileErrors(this ParseError self, ReadOnlyMemory<char> resource)
{
ErrorSpan span = new(self.Row, self.Slice!.Value.Start.Value, self.Slice.Value.End.Value,
self.Position.Start.Value, self.Position.End.Value);
return FormatErrors(self.Message, span, resource);
}
private static string FormatErrors(string message, ErrorSpan span, ReadOnlyMemory<char> resource)
{
var sb = new StringBuilder();
var row = $" {span.Row} ";
var errContext = resource.Slice(span.StartSpan, span.EndSpan - span.StartSpan).ToString();
sb.Append(row).Append('|')
.AppendLine(errContext);
sb.Append(' ', row.Length).Append('|')
.Append(' ', span.StartMark - span.StartSpan - 1).Append('^', span.EndMark - span.StartMark)
.AppendLine($" {message}");
return sb.ToString();
}
}
}

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Linguini.Bundle.Errors;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Prototypes;
@@ -42,7 +41,7 @@ namespace Robust.Shared.Localization
// Flush caches conservatively on prototype/localization changes.
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
{
if (!args.ByType.ContainsKey(typeof(EntityPrototype)))
if (!args.ByType.TryGetValue(typeof(EntityPrototype), out var changeSet))
return;
FlushEntityCache();
@@ -53,58 +52,48 @@ namespace Robust.Shared.Localization
string? name = null;
string? desc = null;
string? suffix = null;
Dictionary<string, string>? attributes = null;
Dictionary<string, string>? attribs = null;
while (true)
{
var prototype = _prototype.Index<EntityPrototype>(prototypeId);
var locId = prototype.CustomLocalizationID ?? $"ent-{prototypeId}";
if (TryGetMessage(locId, out var bundle, out var msg))
if (TryGetMessage(locId, out var ctx, out var msg))
{
// Localization override exists.
var msgAttrs = msg.Attributes;
var mAttribs = msg.Attributes;
if (name == null && msg.Value != null)
{
// Only set name if the value isn't empty.
// So that you can override *only* a desc/suffix.
name = bundle.FormatPattern(msg.Value, null, out var fmtErr);
WriteWarningForErrs(fmtErr, locId);
name = ctx.Format(msg.Value);
}
if (msgAttrs.Count != 0)
if (mAttribs != null && mAttribs.Count != 0)
{
foreach (var (attrId, pattern) in msg.Attributes)
if (desc == null && mAttribs.TryGetValue("desc", out var mDesc))
{
var attrib = attrId.ToString();
if (attrib.Equals("desc")
|| attrib.Equals("suffix"))
desc = ctx.Format(mDesc);
}
if (suffix == null && mAttribs.TryGetValue("suffix", out var mSuffix))
{
suffix = ctx.Format(mSuffix);
}
foreach (var (attrib, value) in msg.Attributes)
{
if (attrib is "desc" or "suffix")
continue;
attributes ??= new Dictionary<string, string>();
if (!attributes.ContainsKey(attrib))
attribs ??= new Dictionary<string, string>();
if (!attribs.ContainsKey(attrib))
{
var value = bundle.FormatPattern(pattern, null, out var errors);
WriteWarningForErrs(errors, locId);
attributes[attrib] = value;
attribs[attrib] = ctx.Format(value);
}
}
var allErrors = new List<FluentError>();
if (desc == null
&& bundle.TryGetMsg(locId, "desc", null, out var err1, out desc))
{
allErrors.AddRange(err1);
}
if (suffix == null
&& bundle.TryGetMsg(locId, "suffix", null, out var err, out suffix))
{
allErrors.AddRange(err);
}
WriteWarningForErrs(allErrors, locId);
}
}
@@ -116,10 +105,10 @@ namespace Robust.Shared.Localization
{
foreach (var (attrib, value) in prototype.LocProperties)
{
attributes ??= new Dictionary<string, string>();
if (!attributes.ContainsKey(attrib))
attribs ??= new Dictionary<string, string>();
if (!attribs.ContainsKey(attrib))
{
attributes[attrib] = value;
attribs[attrib] = value;
}
}
}
@@ -134,10 +123,9 @@ namespace Robust.Shared.Localization
name ?? "",
desc ?? "",
suffix,
attributes?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty);
attribs?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty);
}
public EntityLocData GetEntityData(string prototypeId)
{
return _entityCache.GetOrAdd(prototypeId, (id, t) => t.CalcEntityLoc(id), this);

View File

@@ -1,10 +1,6 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Globalization;
using Linguini.Bundle;
using Linguini.Bundle.Types;
using Linguini.Shared.Types.Bundle;
using Fluent.Net;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Localization;
@@ -13,51 +9,25 @@ namespace Robust.Shared.Localization
{
internal sealed partial class LocalizationManager
{
private void AddBuiltInFunctions(FluentBundle bundle)
private void AddBuiltinFunctions(MessageContext context)
{
// Grammatical gender / pronouns
AddCtxFunction(bundle, "GENDER", FuncGender);
AddCtxFunction(bundle, "SUBJECT", FuncSubject);
AddCtxFunction(bundle, "OBJECT", FuncObject);
AddCtxFunction(bundle, "POSS-ADJ", FuncPossAdj);
AddCtxFunction(bundle, "POSS-PRONOUN", FuncPossPronoun);
AddCtxFunction(bundle, "REFLEXIVE", FuncReflexive);
// Conjugation
AddCtxFunction(bundle, "CONJUGATE-BE", FuncConjugateBe);
AddCtxFunction(bundle, "CONJUGATE-HAVE", FuncConjugateHave);
// Grammatical gender
AddCtxFunction(context, "GENDER", FuncGender);
// Proper nouns
AddCtxFunction(bundle, "PROPER", FuncProper);
AddCtxFunction(bundle, "THE", FuncThe);
AddCtxFunction(context, "PROPER", FuncProper);
// Misc
AddCtxFunction(bundle, "ATTRIB", args => FuncAttrib(bundle, args));
AddCtxFunction(bundle, "CAPITALIZE", FuncCapitalize);
// Misc Attribs
AddCtxFunction(context, "ATTRIB", args => FuncAttrib(context, args));
AddCtxFunction(context, "THE", FuncThe);
}
/// <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[0].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));
@@ -65,7 +35,7 @@ namespace Robust.Shared.Localization
ILocValue entity0 = args.Args[0];
if (entity0.Value != null)
{
IEntity entity = (IEntity)entity0.Value;
IEntity entity = (IEntity) entity0.Value;
if (entity.TryGetComponent<GrammarComponent>(out var grammar) && grammar.Gender.HasValue)
{
@@ -81,72 +51,16 @@ 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(FluentBundle bundle, LocArgs args)
private ILocValue FuncAttrib(MessageContext context, LocArgs args)
{
if (args.Args.Count < 2) return new LocValueString("other");
ILocValue entity0 = args.Args[0];
if (entity0.Value != null)
{
IEntity entity = (IEntity)entity0.Value;
IEntity entity = (IEntity) entity0.Value;
ILocValue attrib0 = args.Args[1];
if (TryGetEntityLocAttrib(entity, attrib0.Format(new LocContext(bundle)), out var attrib))
if (TryGetEntityLocAttrib(entity, attrib0.Format(new LocContext(context)), out var attrib))
{
return new LocValueString(attrib);
}
@@ -155,9 +69,6 @@ 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");
@@ -165,7 +76,7 @@ namespace Robust.Shared.Localization
ILocValue entity0 = args.Args[0];
if (entity0.Value != null)
{
IEntity entity = (IEntity)entity0.Value;
IEntity entity = (IEntity) entity0.Value;
if (entity.TryGetComponent<GrammarComponent>(out var grammar) && grammar.ProperNoun.HasValue)
{
@@ -181,108 +92,39 @@ namespace Robust.Shared.Localization
return new LocValueString("false");
}
private void AddCtxFunction(FluentBundle ctx, string name, LocFunction function)
private void AddCtxFunction(MessageContext ctx, string name, LocFunction function)
{
ctx.AddFunction(name, (args, options)
=> CallFunction(function, args, options), out _, InsertBehavior.Overriding);
}
private IFluentType CallFunction(LocFunction function,
IList<IFluentType> positionalArgs, IDictionary<string, IFluentType> namedArgs)
{
var args = new ILocValue[positionalArgs.Count];
for (var i = 0; i < args.Length; i++)
{
args[i] = positionalArgs[i].ToLocValue();
}
var options = new Dictionary<string, ILocValue>(namedArgs.Count);
foreach (var (k, v) in namedArgs)
{
options.Add(k, v.ToLocValue());
}
var argStruct = new LocArgs(args, options);
return function.Invoke(argStruct).FluentFromVal();
ctx.Functions.Add(name, (args, options) => CallFunction(function, args, options));
}
public void AddFunction(CultureInfo culture, string name, LocFunction function)
{
var bundle = _contexts[culture];
var context = _contexts[culture];
bundle.AddFunction(name, (args, options)
=> CallFunction(function, args, options), out _, InsertBehavior.Overriding);
context.Functions.Add(name, (args, options) => CallFunction(function, args, options));
}
private FluentType CallFunction(
LocFunction function,
IList<object> fluentArgs, IDictionary<string, object> fluentOptions)
{
var args = new ILocValue[fluentArgs.Count];
for (var i = 0; i < args.Length; i++)
{
args[i] = ValFromFluent(fluentArgs[i]);
}
var options = new Dictionary<string, ILocValue>(fluentOptions.Count);
foreach (var (k, v) in fluentOptions)
{
options.Add(k, ValFromFluent(v));
}
var argStruct = new LocArgs(args, options);
var ret = function(argStruct);
return ValToFluent(ret);
}
}
internal sealed class FluentLocWrapperType : IFluentType
{
public readonly ILocValue WrappedValue;
public FluentLocWrapperType(ILocValue wrappedValue)
{
WrappedValue = wrappedValue;
}
public string AsString()
{
return WrappedValue.Format(new LocContext());
}
public IFluentType Copy()
{
return this;
}
}
static class LinguiniAdapter
{
internal static ILocValue ToLocValue(this IFluentType arg)
{
return arg switch
{
FluentNone => new LocValueNone(""),
FluentNumber number => new LocValueNumber(number),
FluentString str => new LocValueString(str),
FluentLocWrapperType value => value.WrappedValue,
_ => throw new ArgumentOutOfRangeException(nameof(arg)),
};
}
public static IFluentType FluentFromObject(this object obj)
{
return obj switch
{
ILocValue wrap => new FluentLocWrapperType(wrap),
IEntity entity => new FluentLocWrapperType(new LocValueEntity(entity)),
DateTime dateTime => new FluentLocWrapperType(new LocValueDateTime(dateTime)),
bool or Enum => (FluentString)obj.ToString()!.ToLowerInvariant(),
string str => (FluentString)str,
byte num => (FluentNumber)num,
sbyte num => (FluentNumber)num,
short num => (FluentNumber)num,
ushort num => (FluentNumber)num,
int num => (FluentNumber)num,
uint num => (FluentNumber)num,
long num => (FluentNumber)num,
ulong num => (FluentNumber)num,
double dbl => (FluentNumber)dbl,
float dbl => (FluentNumber)dbl,
_ => (FluentString)obj.ToString()!,
};
}
public static IFluentType FluentFromVal(this ILocValue locValue)
{
return locValue switch
{
LocValueNone => FluentNone.None,
LocValueNumber number => (FluentNumber)number.Value,
LocValueString str => (FluentString)str.Value,
LocValueDateTime dateTime => new FluentLocWrapperType(dateTime),
_ => new FluentLocWrapperType(locValue),
};
}
}
}
}

View File

@@ -4,14 +4,9 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using Fluent.Net;
using Fluent.Net.RuntimeAst;
using JetBrains.Annotations;
using Linguini.Bundle;
using Linguini.Bundle.Builder;
using Linguini.Bundle.Errors;
using Linguini.Shared.Types.Bundle;
using Linguini.Syntax.Ast;
using Linguini.Syntax.Parser;
using Linguini.Syntax.Parser.Error;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -29,7 +24,7 @@ namespace Robust.Shared.Localization
[Dependency] private readonly IPrototypeManager _prototype = default!;
private ISawmill _logSawmill = default!;
private readonly Dictionary<CultureInfo, FluentBundle> _contexts = new();
private readonly Dictionary<CultureInfo, MessageContext> _contexts = new();
private CultureInfo? _defaultCulture;
@@ -53,6 +48,16 @@ namespace Robust.Shared.Localization
return msg;
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
{
if (!TryGetNode(messageId, out var context, out var node))
{
value = null;
return false;
}
return DoFormat(messageId, out value, context, node);
}
public string GetString(string messageId, params (string, object)[] args0)
{
@@ -68,38 +73,144 @@ namespace Robust.Shared.Localization
return msg;
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
{
return TryGetString(messageId, out value, null);
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value,
params (string, object)[]? keyArgs)
params (string, object)[] args0)
{
var args = new Dictionary<string, IFluentType>();
if (keyArgs != null)
{
foreach (var (k, v) in keyArgs)
{
args.Add(k, v.FluentFromObject());
}
}
if (!HasMessage(messageId, out var bundle))
if (!TryGetNode(messageId, out var context, out var node))
{
value = null;
return false;
}
try
var args = new Dictionary<string, object>();
foreach (var (k, v) in args0)
{
var result = bundle.TryGetAttrMsg(messageId, args, out var errs, out value);
foreach (var err in errs)
var val = v switch
{
_logSawmill.Error("{culture}/{messageId}: {error}", _defaultCulture!.Name, messageId, err);
IEntity entity => new LocValueEntity(entity),
bool or Enum => v.ToString()!.ToLowerInvariant(),
_ => v,
};
if (val is ILocValue locVal)
val = new FluentLocWrapperType(locVal);
args.Add(k, val);
}
return DoFormat(messageId, out value, context, node, args);
}
private bool TryGetMessage(
string messageId,
[NotNullWhen(true)] out MessageContext? ctx,
[NotNullWhen(true)] out Message? message)
{
if (_defaultCulture == null)
{
ctx = null;
message = null;
return false;
}
ctx = _contexts[_defaultCulture];
message = ctx.GetMessage(messageId);
return message != null;
}
private bool TryGetNode(
string messageId,
[NotNullWhen(true)] out MessageContext? ctx,
[NotNullWhen(true)] out Node? node)
{
string? attribName = null;
if (messageId.Contains('.'))
{
var split = messageId.Split('.');
messageId = split[0];
attribName = split[1];
}
if (!TryGetMessage(messageId, out ctx, out var message))
{
node = null;
return false;
}
if (attribName != null)
{
if (message.Attributes == null || !message.Attributes.TryGetValue(attribName, out var attrib))
{
node = null;
return false;
}
return result;
node = attrib;
}
else
{
node = message;
}
return true;
}
public void ReloadLocalizations()
{
foreach (var (culture, context) in _contexts.ToArray())
{
// Fluent.Net doesn't allow us to remove messages so...
var newContext = new MessageContext(
culture.Name,
new MessageContextOptions
{
UseIsolating = false,
Functions = context.Functions
}
);
_contexts[culture] = newContext;
_loadData(_res, culture, newContext);
}
FlushEntityCache();
}
private static ILocValue ValFromFluent(object arg)
{
return arg switch
{
FluentNone none => new LocValueNone(none.Value),
FluentNumber number => new LocValueNumber(double.Parse(number.Value)),
FluentString str => new LocValueString(str.Value),
FluentDateTime dateTime =>
new LocValueDateTime(DateTime.Parse(dateTime.Value, null, DateTimeStyles.RoundtripKind)),
FluentLocWrapperType wrap => wrap.WrappedValue,
_ => throw new ArgumentOutOfRangeException(nameof(arg))
};
}
private static FluentType ValToFluent(ILocValue arg)
{
return arg switch
{
LocValueNone =>
throw new NotSupportedException("Cannot currently return LocValueNone from loc functions."),
LocValueNumber number => new FluentNumber(number.Value.ToString("R")),
LocValueString str => new FluentString(str.Value),
LocValueDateTime dateTime => new FluentDateTime(dateTime.Value),
_ => new FluentLocWrapperType(arg)
};
}
private bool DoFormat(string messageId, out string? value, MessageContext context, Node node, IDictionary<string, object>? args = null)
{
var errs = new List<FluentError>();
try
{
value = context.Format(node, args, errs);
}
catch (Exception e)
{
@@ -107,52 +218,13 @@ namespace Robust.Shared.Localization
value = null;
return false;
}
}
private bool HasMessage(
string messageId,
[NotNullWhen(true)] out FluentBundle? bundle)
{
if (_defaultCulture == null)
foreach (var err in errs)
{
bundle = null;
return false;
_logSawmill.Error("{culture}/{messageId}: {error}", _defaultCulture!.Name, messageId, err);
}
bundle = _contexts[_defaultCulture];
if (messageId.Contains('.'))
{
var split = messageId.Split('.');
return bundle.HasMessage(split[0]);
}
return bundle.HasMessage(messageId);
}
private bool TryGetMessage(
string messageId,
[NotNullWhen(true)] out FluentBundle? bundle,
[NotNullWhen(true)] out AstMessage? message)
{
if (_defaultCulture == null)
{
bundle = null;
message = null;
return false;
}
bundle = _contexts[_defaultCulture];
return bundle.TryGetAstMessage(messageId, out message);
}
public void ReloadLocalizations()
{
foreach (var (culture, context) in _contexts.ToArray())
{
_loadData(_res, culture, context);
}
FlushEntityCache();
return true;
}
/// <summary>
@@ -189,18 +261,26 @@ namespace Robust.Shared.Localization
public void LoadCulture(CultureInfo culture)
{
var bundle = LinguiniBuilder.Builder()
.CultureInfo(culture)
.SkipResources()
.SetUseIsolating(false)
.UseConcurrent()
.UncheckedBuild();
var context = new MessageContext(
culture.Name,
new MessageContextOptions
{
UseIsolating = false,
// Have to pass empty dict here or else Fluent.Net will fuck up
// and share the same dict between multiple message contexts.
// Yes, you read that right.
Functions = new Dictionary<string, Resolver.ExternalFunction>(),
}
);
AddBuiltinFunctions(context);
_contexts.Add(culture, bundle);
AddBuiltInFunctions(bundle);
_contexts.Add(culture, context);
_loadData(_res, culture, bundle);
DefaultCulture ??= culture;
_loadData(_res, culture, context);
if (DefaultCulture == null)
{
DefaultCulture = culture;
}
}
public void AddLoadedToStringSerializer(IRobustMappedStringSerializer serializer)
@@ -227,7 +307,7 @@ namespace Robust.Shared.Localization
*/
}
private void _loadData(IResourceManager resourceManager, CultureInfo culture, FluentBundle context)
private void _loadData(IResourceManager resourceManager, CultureInfo culture, MessageContext context)
{
// Load data from .ftl files.
// Data is loaded from /Locale/<language-code>/*
@@ -243,33 +323,40 @@ namespace Robust.Shared.Localization
using var fileStream = resourceManager.ContentFileRead(path);
using var reader = new StreamReader(fileStream, EncodingHelpers.UTF8);
var parser = new LinguiniParser(reader);
var resource = parser.Parse();
return (path, resource, parser.GetReadonlyData);
var resource = FluentResource.FromReader(reader);
return (path, resource);
});
foreach (var (path, resource, data) in resources)
foreach (var (path, resource) in resources)
{
var errors = resource.Errors;
context.AddResourceOverriding(resource);
WriteWarningForErrs(path, errors, data);
var errors = context.AddResource(resource);
foreach (var error in errors)
{
_logSawmill.Error("{path}: {exception}", path, error.Message);
}
}
}
private void WriteWarningForErrs(ResourcePath path, List<ParseError> errs, ReadOnlyMemory<char> resource)
private sealed class FluentLocWrapperType : FluentType
{
foreach (var err in errs)
{
_logSawmill.Warning("{path}:\n{exception}", path, err.FormatCompileErrors(resource));
}
}
public readonly ILocValue WrappedValue;
private void WriteWarningForErrs(IList<FluentError> errs, string locId)
{
foreach (var err in errs)
public FluentLocWrapperType(ILocValue wrappedValue)
{
_logSawmill.Warning("Error extracting `{locId}`\n{e1}", locId, err);
WrappedValue = wrappedValue;
}
public override string Format(MessageContext ctx)
{
return WrappedValue.Format(new LocContext(ctx));
}
public override bool Match(MessageContext ctx, object obj)
{
return false;
/*var strVal = obj is IFluentType ft ? ft.Value : obj.ToString() ?? "";
return WrappedValue.Matches(new LocContext(ctx), strVal);*/
}
}
}
}
}

View File

@@ -220,11 +220,6 @@ namespace Robust.Shared.Map
public MapId CreateMap(MapId? mapID = null)
{
return CreateMap(mapID, null);
}
public MapId CreateMap(MapId? mapID, EntityUid? entityUid)
{
#if DEBUG
DebugTools.Assert(_dbgGuardRunning);
#endif
@@ -274,7 +269,7 @@ namespace Robust.Shared.Map
}
else
{
var newEnt = (Entity) _entityManager.CreateEntityUninitialized(null, entityUid);
var newEnt = (Entity) _entityManager.CreateEntityUninitialized(null, EntityCoordinates.Invalid);
_mapEntities.Add(actualID, newEnt.Uid);
var mapComp = newEnt.AddComponent<MapComponent>();
@@ -405,15 +400,10 @@ namespace Robust.Shared.Map
public IMapGrid CreateGrid(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16)
{
return CreateGridImpl(currentMapID, gridID, chunkSize, true, null);
return CreateGridImpl(currentMapID, gridID, chunkSize, true);
}
public IMapGrid CreateGrid(MapId currentMapID, GridId gridID, ushort chunkSize, EntityUid euid)
{
return CreateGridImpl(currentMapID, gridID, chunkSize, true, euid);
}
private IMapGridInternal CreateGridImpl(MapId currentMapID, GridId? gridID, ushort chunkSize, bool createEntity, EntityUid? euid)
private IMapGridInternal CreateGridImpl(MapId currentMapID, GridId? gridID, ushort chunkSize, bool createEntity)
{
#if DEBUG
DebugTools.Assert(_dbgGuardRunning);
@@ -465,23 +455,20 @@ namespace Robust.Shared.Map
}
else
{
var gridEnt = (Entity) EntityManager.CreateEntityUninitialized(null, euid);
grid.GridEntityId = gridEnt.Uid;
var newEnt =
(Entity) _entityManager.CreateEntityUninitialized(null,
new MapCoordinates(Vector2.Zero, currentMapID));
grid.GridEntityId = newEnt.Uid;
Logger.DebugS("map", $"Binding grid {actualID} to entity {grid.GridEntityId}");
var gridComp = gridEnt.AddComponent<MapGridComponent>();
var gridComp = newEnt.AddComponent<MapGridComponent>();
gridComp.GridIndex = grid.Index;
//TODO: This is a hack to get TransformComponent.MapId working before entity states
//are applied. After they are applied the parent may be different, but the MapId will
//be the same. This causes TransformComponent.ParentUid of a grid to be unsafe to
//use in transform states anytime before the state parent is properly set.
gridEnt.Transform.AttachParent(GetMapEntity(currentMapID));
gridEnt.InitializeComponents();
gridEnt.StartAllComponents();
newEnt.Transform.AttachParent(_entityManager.GetEntity(_mapEntities[currentMapID]));
newEnt.InitializeComponents();
newEnt.StartAllComponents();
}
}
else
@@ -495,7 +482,7 @@ namespace Robust.Shared.Map
public IMapGridInternal CreateGridNoEntity(MapId currentMapID, GridId? gridID = null, ushort chunkSize = 16)
{
return CreateGridImpl(currentMapID, gridID, chunkSize, false, null);
return CreateGridImpl(currentMapID, gridID, chunkSize, false);
}
public IMapGrid GetGrid(GridId gridID)

View File

@@ -128,25 +128,10 @@ 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>

Some files were not shown because too many files have changed in this diff Show More