mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7e8178736 | ||
|
|
31f921e4aa | ||
|
|
aa1c25637c | ||
|
|
71f2c48463 | ||
|
|
d65f4ca898 | ||
|
|
b35568ffe5 | ||
|
|
a0d241e551 | ||
|
|
33a6934582 | ||
|
|
f237a8bbbc | ||
|
|
4bc775c01c | ||
|
|
93b4d81505 | ||
|
|
0afb85a09e | ||
|
|
7b9315cea4 | ||
|
|
dc3af45096 | ||
|
|
00ce0179ae | ||
|
|
81947ba3d8 |
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -41,14 +42,14 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugColliders = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<PhysicsOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new PhysicsOverlay(_componentManager, _eyeManager,
|
||||
_prototypeManager, _inputManager, _physicsManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(PhysicsOverlay));
|
||||
_overlayManager.RemoveOverlay<PhysicsOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,13 +67,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugPositions = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_entityManager, _eyeManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(EntityPositionOverlay));
|
||||
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,8 +92,8 @@ namespace Robust.Client.Debugging
|
||||
private Vector2 _hoverStartScreen = Vector2.Zero;
|
||||
private List<IPhysBody> _hoverBodies = new();
|
||||
|
||||
|
||||
public PhysicsOverlay(IComponentManager compMan, IEyeManager eyeMan, IPrototypeManager protoMan, IInputManager inputManager, IPhysicsManager physicsManager)
|
||||
: base(nameof(PhysicsOverlay))
|
||||
{
|
||||
_componentManager = compMan;
|
||||
_eyeManager = eyeMan;
|
||||
@@ -265,7 +266,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager) : base(nameof(EntityPositionOverlay))
|
||||
public EntityPositionOverlay(IEntityManager entityManager, IEyeManager eyeManager)
|
||||
{
|
||||
_entityManager = entityManager;
|
||||
_eyeManager = eyeManager;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
@@ -38,13 +39,13 @@ namespace Robust.Client.Debugging
|
||||
|
||||
_debugDrawRays = value;
|
||||
|
||||
if (value)
|
||||
if (value && !_overlayManager.HasOverlay<DebugDrawRayOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new DebugDrawRayOverlay(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay(nameof(DebugDrawRayOverlay));
|
||||
_overlayManager.RemoveOverlay<DebugDrawRayOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,7 +82,7 @@ namespace Robust.Client.Debugging
|
||||
private readonly DebugDrawingManager _owner;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public DebugDrawRayOverlay(DebugDrawingManager owner) : base(nameof(DebugDrawRayOverlay))
|
||||
public DebugDrawRayOverlay(DebugDrawingManager owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
using System;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -55,7 +56,7 @@ namespace Robust.Client.Debugging
|
||||
IoCManager.Resolve<IOverlayManager>().AddOverlay(new PhysicsDebugOverlay(this));
|
||||
|
||||
if (value == PhysicsDebugFlags.None)
|
||||
IoCManager.Resolve<IOverlayManager>().RemoveOverlay(nameof(PhysicsDebugOverlay));
|
||||
IoCManager.Resolve<IOverlayManager>().RemoveOverlay(typeof(PhysicsDebugOverlay));
|
||||
|
||||
_flags = value;
|
||||
}
|
||||
@@ -119,7 +120,7 @@ namespace Robust.Client.Debugging
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public PhysicsDebugOverlay(DebugPhysicsSystem system) : base(nameof(PhysicsDebugOverlay))
|
||||
public PhysicsDebugOverlay(DebugPhysicsSystem system)
|
||||
{
|
||||
_physics = system;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,8 @@ namespace Robust.Client
|
||||
_configurationManager.OverrideConVars(_commandLineArgs.CVars);
|
||||
}
|
||||
|
||||
ProfileOptSetup.Setup(_configurationManager);
|
||||
|
||||
_resourceCache.Initialize(LoadConfigAndUserData ? userDataDir : null);
|
||||
|
||||
ProgramShared.DoMounts(_resourceCache, _commandLineArgs?.MountOptions, "Content.Client", _loaderArgs != null);
|
||||
@@ -161,6 +163,7 @@ namespace Robust.Client
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.PreInit);
|
||||
_modLoader.BroadcastRunLevel(ModRunLevel.Init);
|
||||
|
||||
_resourceCache.PreloadTextures();
|
||||
_userInterfaceManager.Initialize();
|
||||
_networkManager.Initialize(false);
|
||||
IoCManager.Resolve<INetConfigurationManager>().SetupNetworking();
|
||||
@@ -188,6 +191,8 @@ namespace Robust.Client
|
||||
|
||||
_authManager.LoadFromEnv();
|
||||
|
||||
GC.Collect();
|
||||
|
||||
_clyde.Ready();
|
||||
|
||||
if ((_commandLineArgs?.Connect == true || _commandLineArgs?.Launcher == true)
|
||||
|
||||
@@ -45,12 +45,12 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message)
|
||||
{
|
||||
SendSystemNetworkMessage(message, default(uint));
|
||||
}
|
||||
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, uint sequence)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, uint sequence)
|
||||
{
|
||||
var msg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
msg.Type = EntityMessageType.SystemMessage;
|
||||
@@ -62,7 +62,7 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, INetChannel channel)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel channel)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Client.GameObjects
|
||||
public static class EntityManagerExt
|
||||
{
|
||||
public static void RaisePredictiveEvent<T>(this IEntityManager entityManager, T msg)
|
||||
where T : EntitySystemMessage
|
||||
where T : EntityEventArgs
|
||||
{
|
||||
var localPlayer = IoCManager.Resolve<IPlayerManager>().LocalPlayer;
|
||||
DebugTools.AssertNotNull(localPlayer);
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Event raised by a <see cref="ClientOccluderComponent"/> when it needs to be recalculated.
|
||||
/// </summary>
|
||||
internal sealed class OccluderDirtyEvent : EntitySystemMessage
|
||||
internal sealed class OccluderDirtyEvent : EntityEventArgs
|
||||
{
|
||||
public OccluderDirtyEvent(IEntity sender, (GridId grid, Vector2i pos)? lastPosition, SnapGridOffset offset)
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -42,7 +43,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
overlayManager.RemoveOverlay("EffectSystem");
|
||||
overlayManager.RemoveOverlay(typeof(EffectOverlay));
|
||||
}
|
||||
|
||||
public void CreateEffect(EffectSystemMessage message)
|
||||
@@ -329,7 +330,6 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
private readonly IPlayerManager _playerManager;
|
||||
|
||||
public override bool AlwaysDirty => true;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
private readonly ShaderInstance _unshadedShader;
|
||||
@@ -337,8 +337,7 @@ namespace Robust.Client.GameObjects
|
||||
private readonly IMapManager _mapManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IMapManager mapMan, IPlayerManager playerMan, IEntityManager entityManager) : base(
|
||||
"EffectSystem")
|
||||
public EffectOverlay(EffectSystem owner, IPrototypeManager protoMan, IMapManager mapMan, IPlayerManager playerMan, IEntityManager entityManager)
|
||||
{
|
||||
_owner = owner;
|
||||
_unshadedShader = protoMan.Index<ShaderPrototype>("unshaded").Instance();
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Entity system message that is raised when the player changes attached entities.
|
||||
/// </summary>
|
||||
public class PlayerAttachSysMessage : EntitySystemMessage
|
||||
public class PlayerAttachSysMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// New entity the player is attached to.
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Client.GameStates
|
||||
private uint _nextInputCmdSeq = 1;
|
||||
private readonly Queue<FullInputCmdMessage> _pendingInputs = new();
|
||||
|
||||
private readonly Queue<(uint sequence, GameTick sourceTick, EntitySystemMessage msg, object sessionMsg)>
|
||||
private readonly Queue<(uint sequence, GameTick sourceTick, EntityEventArgs msg, object sessionMsg)>
|
||||
_pendingSystemMessages
|
||||
= new();
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Robust.Client.GameStates
|
||||
_nextInputCmdSeq++;
|
||||
}
|
||||
|
||||
public uint SystemMessageDispatched<T>(T message) where T : EntitySystemMessage
|
||||
public uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs
|
||||
{
|
||||
if (!Predicting)
|
||||
{
|
||||
@@ -298,7 +298,7 @@ namespace Robust.Client.GameStates
|
||||
// because the rest of the main loop will call into it with the target tick later,
|
||||
// and it won't be a past prediction.
|
||||
_entitySystemManager.Update((float) _timing.TickPeriod.TotalSeconds);
|
||||
((IEntityEventBus) _entities.EventBus).ProcessEventQueue();
|
||||
((IBroadcastEventBusInternal) _entities.EventBus).ProcessEventQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,6 @@ namespace Robust.Client.GameStates
|
||||
/// <param name="message">Message being dispatched.</param>
|
||||
void InputCommandDispatched(FullInputCmdMessage message);
|
||||
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntitySystemMessage;
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -33,7 +34,7 @@ namespace Robust.Client.GameStates
|
||||
private readonly int _lineHeight;
|
||||
private readonly List<NetEntity> _netEnts = new();
|
||||
|
||||
public NetEntityOverlay() : base(nameof(NetEntityOverlay))
|
||||
public NetEntityOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
@@ -179,11 +180,10 @@ namespace Robust.Client.GameStates
|
||||
return Color.Green; // Entity in PVS, but not updated recently.
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_gameStateManager.GameStateApplied -= HandleGameStateApplied;
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private static void DrawString(DrawingHandleScreen handle, Font font, Vector2 pos, string str, Color textColor)
|
||||
@@ -238,14 +238,14 @@ namespace Robust.Client.GameStates
|
||||
var bValue = iValue > 0;
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if(bValue && !overlayMan.HasOverlay(nameof(NetEntityOverlay)))
|
||||
if(bValue && !overlayMan.HasOverlay(typeof(NetEntityOverlay)))
|
||||
{
|
||||
overlayMan.AddOverlay(new NetEntityOverlay());
|
||||
shell.WriteLine("Enabled network entity report overlay.");
|
||||
}
|
||||
else if(!bValue && overlayMan.HasOverlay(nameof(NetEntityOverlay)))
|
||||
else if(!bValue && overlayMan.HasOverlay(typeof(NetEntityOverlay)))
|
||||
{
|
||||
overlayMan.RemoveOverlay(nameof(NetEntityOverlay));
|
||||
overlayMan.RemoveOverlay(typeof(NetEntityOverlay));
|
||||
shell.WriteLine("Disabled network entity report overlay.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -34,7 +36,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly List<(GameTick Tick, int Payload, int lag, int interp)> _history = new(HistorySize+10);
|
||||
|
||||
public NetGraphOverlay() : base(nameof(NetGraphOverlay))
|
||||
public NetGraphOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var cache = IoCManager.Resolve<IResourceCache>();
|
||||
@@ -142,11 +144,11 @@ namespace Robust.Client.GameStates
|
||||
DrawString((DrawingHandleScreen)handle, _font, new Vector2(leftMargin, height + LowerGraphOffset), $"{_gameStateManager.CurrentBufferSize.ToString()} states");
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
protected override void DisposeBehavior()
|
||||
{
|
||||
_gameStateManager.GameStateApplied -= HandleGameStateApplied;
|
||||
|
||||
base.Dispose(disposing);
|
||||
base.DisposeBehavior();
|
||||
}
|
||||
|
||||
private void DrawString(DrawingHandleScreen handle, Font font, Vector2 pos, string str)
|
||||
@@ -183,14 +185,14 @@ namespace Robust.Client.GameStates
|
||||
var bValue = iValue > 0;
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if(bValue && !overlayMan.HasOverlay(nameof(NetGraphOverlay)))
|
||||
if(bValue && !overlayMan.HasOverlay(typeof(NetGraphOverlay)))
|
||||
{
|
||||
overlayMan.AddOverlay(new NetGraphOverlay());
|
||||
shell.WriteLine("Enabled network overlay.");
|
||||
}
|
||||
else if(overlayMan.HasOverlay(nameof(NetGraphOverlay)))
|
||||
else if(overlayMan.HasOverlay(typeof(NetGraphOverlay)))
|
||||
{
|
||||
overlayMan.RemoveOverlay(nameof(NetGraphOverlay));
|
||||
overlayMan.RemoveOverlay(typeof(NetGraphOverlay));
|
||||
shell.WriteLine("Disabled network overlay.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -5,6 +6,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.GameStates
|
||||
@@ -18,7 +20,7 @@ namespace Robust.Client.GameStates
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
private readonly ShaderInstance _shader;
|
||||
|
||||
public NetInterpOverlay() : base(nameof(NetInterpOverlay))
|
||||
public NetInterpOverlay()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_shader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
||||
@@ -85,14 +87,14 @@ namespace Robust.Client.GameStates
|
||||
var bValue = iValue > 0;
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (bValue && !overlayMan.HasOverlay(nameof(NetInterpOverlay)))
|
||||
if (bValue && !overlayMan.HasOverlay<NetInterpOverlay>())
|
||||
{
|
||||
overlayMan.AddOverlay(new NetInterpOverlay());
|
||||
shell.WriteLine("Enabled network interp overlay.");
|
||||
}
|
||||
else if (overlayMan.HasOverlay(nameof(NetInterpOverlay)))
|
||||
else if (overlayMan.HasOverlay<NetInterpOverlay>())
|
||||
{
|
||||
overlayMan.RemoveOverlay(nameof(NetInterpOverlay));
|
||||
overlayMan.RemoveOverlay<NetInterpOverlay>();
|
||||
shell.WriteLine("Disabled network interp overlay.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JetBrains.Annotations;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
static Clyde()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
RuntimeInformation.ProcessArchitecture == Architecture.X64 &&
|
||||
Environment.GetEnvironmentVariable("ROBUST_INTEGRATED_GPU") != "1")
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@ using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
@@ -68,8 +70,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
RenderOverlays(OverlaySpace.ScreenSpaceBelowWorld);
|
||||
|
||||
_mainViewport.Eye = _eyeManager.CurrentEye;
|
||||
RenderViewport(_mainViewport);
|
||||
|
||||
RenderViewport(_mainViewport); //Worldspace overlays are rendered here.
|
||||
{
|
||||
var handle = _renderHandle.DrawingHandleScreen;
|
||||
var tex = _mainViewport.RenderTarget.Texture;
|
||||
@@ -107,25 +108,67 @@ namespace Robust.Client.Graphics.Clyde
|
||||
list.Add(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
list.Sort(OverlayComparer.Instance);
|
||||
|
||||
foreach (var overlay in list)
|
||||
{
|
||||
overlay.ClydeRender(_renderHandle, space);
|
||||
}
|
||||
|
||||
FlushRenderQueue();
|
||||
list.Sort(OverlayComparer.Instance);
|
||||
foreach (var overlay in list) {
|
||||
if (overlay.RequestScreenTexture) {
|
||||
FlushRenderQueue();
|
||||
UpdateOverlayScreenTexture(space, _mainViewport.RenderTarget);
|
||||
}
|
||||
if (overlay.OverwriteTargetFrameBuffer()) {
|
||||
ClearFramebuffer(default);
|
||||
}
|
||||
overlay.ClydeRender(_renderHandle, space);
|
||||
FlushRenderQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawEntitiesAndWorldOverlay(Viewport viewport, Box2 worldBounds)
|
||||
private ClydeTexture? ScreenBufferTexture;
|
||||
private GLHandle screenBufferHandle;
|
||||
private Vector2 lastFrameSize;
|
||||
/// <summary>
|
||||
/// Sends SCREEN_TEXTURE to all overlays in the given OverlaySpace that request it.
|
||||
/// </summary>
|
||||
private bool UpdateOverlayScreenTexture(OverlaySpace space, RenderTexture texture) {
|
||||
//This currently does NOT consider viewports and just grabs the current screen framebuffer. This will need to be improved upon in the future.
|
||||
List<Overlay> oTargets = new List<Overlay>();
|
||||
foreach (var overlay in _overlayManager.AllOverlays) {
|
||||
if (overlay.RequestScreenTexture && overlay.Space == space) {
|
||||
oTargets.Add(overlay);
|
||||
}
|
||||
}
|
||||
if (oTargets.Count > 0 && ScreenBufferTexture != null) {
|
||||
if (lastFrameSize != _framebufferSize) {
|
||||
GL.BindTexture(TextureTarget.Texture2D, screenBufferHandle.Handle);
|
||||
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Srgb8Alpha8, _framebufferSize.X, _framebufferSize.Y, 0,
|
||||
PixelFormat.Rgba, PixelType.UnsignedByte, IntPtr.Zero);
|
||||
}
|
||||
lastFrameSize = _framebufferSize;
|
||||
CopyRenderTextureToTexture(texture, ScreenBufferTexture);
|
||||
foreach (Overlay overlay in oTargets) {
|
||||
overlay.ScreenTexture = ScreenBufferTexture;
|
||||
}
|
||||
oTargets.Clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void DrawEntities(Viewport viewport, Box2 worldBounds)
|
||||
{
|
||||
if (_eyeManager.CurrentMap == MapId.Nullspace || !_mapManager.HasMapEntity(_eyeManager.CurrentMap))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RenderOverlays(OverlaySpace.WorldSpaceBelowEntities);
|
||||
|
||||
var screenSize = viewport.Size;
|
||||
|
||||
// So we could calculate the correct size of the entities based on the contents of their sprite...
|
||||
@@ -267,14 +310,6 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
_drawingSpriteList.Clear();
|
||||
FlushRenderQueue();
|
||||
|
||||
// Cleanup remainders
|
||||
foreach (var overlay in worldOverlays)
|
||||
{
|
||||
overlay.ClydeRender(_renderHandle, OverlaySpace.WorldSpace);
|
||||
}
|
||||
|
||||
FlushRenderQueue();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
@@ -368,12 +403,21 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// We will also render worldspace overlays here so we can do them under / above entities as necessary
|
||||
using (DebugGroup("Entities"))
|
||||
{
|
||||
DrawEntitiesAndWorldOverlay(viewport, worldBounds);
|
||||
DrawEntities(viewport, worldBounds);
|
||||
}
|
||||
|
||||
RenderOverlays(OverlaySpace.WorldSpaceBelowFOV);
|
||||
|
||||
if (_lightManager.Enabled && _lightManager.DrawHardFov && eye.DrawFov)
|
||||
{
|
||||
GL.Clear(ClearBufferMask.StencilBufferBit);
|
||||
GL.Enable(EnableCap.StencilTest);
|
||||
GL.StencilOp(OpenToolkit.Graphics.OpenGL4.StencilOp.Keep, OpenToolkit.Graphics.OpenGL4.StencilOp.Keep, OpenToolkit.Graphics.OpenGL4.StencilOp.Replace);
|
||||
GL.StencilFunc(StencilFunction.Always, 1, 0xFF);
|
||||
GL.StencilMask(0xFF);
|
||||
ApplyFovToBuffer(viewport, eye);
|
||||
GL.StencilMask(0x00);
|
||||
GL.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,6 +445,14 @@ namespace Robust.Client.Graphics.Clyde
|
||||
viewport.WallBleedIntermediateRenderTarget2.Texture,
|
||||
UIBox2.FromDimensions(Vector2.Zero, ScreenSize), new Color(1, 1, 1, 0.5f));
|
||||
}
|
||||
|
||||
|
||||
RenderOverlays(OverlaySpace.WorldSpace);
|
||||
|
||||
GL.StencilFunc(StencilFunction.Notequal, 1, 0xFF);
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
RenderOverlays(OverlaySpace.WorldSpaceFOVStencil);
|
||||
GL.Disable(EnableCap.StencilTest);
|
||||
}
|
||||
|
||||
PopRenderStateFull(state);
|
||||
|
||||
@@ -5,6 +5,7 @@ using OpenToolkit.Graphics.OpenGL4;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using ES20 = OpenToolkit.Graphics.ES20;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
@@ -31,6 +32,27 @@ namespace Robust.Client.Graphics.Clyde
|
||||
CheckGlError();
|
||||
GL.BindTexture(TextureTarget.Texture2D, glHandle.Handle);
|
||||
CheckGlError();
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
}
|
||||
|
||||
private void CopyRenderTextureToTexture(RenderTexture source, ClydeTexture target) {
|
||||
LoadedRenderTarget sourceLoaded = RtToLoaded(source);
|
||||
bool pause = sourceLoaded != _currentBoundRenderTarget;
|
||||
FullStoredRendererState? store = null;
|
||||
if (pause) {
|
||||
store = PushRenderStateFull();
|
||||
BindRenderTargetFull(source);
|
||||
CheckGlError();
|
||||
}
|
||||
|
||||
GL.BindTexture(TextureTarget.Texture2D, _loadedTextures[target.TextureId].OpenGLObject.Handle);
|
||||
CheckGlError();
|
||||
GL.CopyTexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, 0, 0, _framebufferSize.X, _framebufferSize.Y);
|
||||
CheckGlError();
|
||||
|
||||
if (pause && store != null) {
|
||||
PopRenderStateFull((FullStoredRendererState)store);
|
||||
}
|
||||
}
|
||||
|
||||
private static long EstPixelSize(PixelInternalFormat format)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@@ -280,7 +280,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush the render handle, processing and re-pooling all the command lists.
|
||||
/// Flushes the render handle, processing and re-pooling all the command lists.
|
||||
/// </summary>
|
||||
private void FlushRenderQueue()
|
||||
{
|
||||
@@ -371,6 +371,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
program.Use();
|
||||
|
||||
int textureUnitVal = 0;
|
||||
// Assign shader parameters to uniform since they may be dirty.
|
||||
foreach (var (name, value) in instance.Parameters)
|
||||
{
|
||||
@@ -413,6 +414,15 @@ namespace Robust.Client.Graphics.Clyde
|
||||
case Matrix4 matrix4:
|
||||
program.SetUniform(name, matrix4);
|
||||
break;
|
||||
case ClydeTexture clydeTexture:
|
||||
//It's important to start at Texture6 here since DrawCommandBatch uses Texture0 and Texture1 immediately after calling this
|
||||
//function! If passing in textures as uniforms ever stops working it might be since someone made it use all the way up to Texture6 too.
|
||||
//Might change this in the future?
|
||||
TextureUnit cTarget = TextureUnit.Texture6+textureUnitVal;
|
||||
SetTexture(cTarget, ((ClydeTexture)clydeTexture).TextureId);
|
||||
program.SetUniformTexture(name, cTarget);
|
||||
textureUnitVal++;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Unable to handle shader parameter {name}: {value}");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -428,7 +428,8 @@ namespace Robust.Client.Graphics.Clyde
|
||||
|
||||
private protected override void SetParameterImpl(string name, Texture value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var data = Parent._shaderInstances[Handle];
|
||||
data.Parameters[name] = value;
|
||||
}
|
||||
|
||||
private protected override void SetStencilOpImpl(StencilOp op)
|
||||
|
||||
@@ -56,19 +56,19 @@ namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
// Disable sRGB so stuff doesn't get interpreter wrong.
|
||||
actualParams.Srgb = false;
|
||||
var img = ApplyA8Swizzle((Image<A8>) (object) image);
|
||||
using var img = ApplyA8Swizzle((Image<A8>) (object) image);
|
||||
return LoadTextureFromImage(img, name, loadParams);
|
||||
}
|
||||
|
||||
if (pixelType == typeof(L8) && !actualParams.Srgb)
|
||||
{
|
||||
var img = ApplyL8Swizzle((Image<L8>) (object) image);
|
||||
using var img = ApplyL8Swizzle((Image<L8>) (object) image);
|
||||
return LoadTextureFromImage(img, name, loadParams);
|
||||
}
|
||||
}
|
||||
|
||||
// Flip image because OpenGL reads images upside down.
|
||||
var copy = FlipClone(image);
|
||||
using var copy = FlipClone(image);
|
||||
|
||||
var texture = CreateBaseTextureInternal<T>(image.Width, image.Height, actualParams, name);
|
||||
|
||||
|
||||
@@ -315,6 +315,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
UniformConstantsUBO = new GLUniformBuffer<UniformConstants>(this, BindingIndexUniformConstants, nameof(UniformConstantsUBO));
|
||||
|
||||
CreateMainViewport();
|
||||
|
||||
screenBufferHandle = new GLHandle(GL.GenTexture());
|
||||
GL.BindTexture(TextureTarget.Texture2D, screenBufferHandle.Handle);
|
||||
ApplySampleParameters(TextureSampleParameters.Default);
|
||||
ScreenBufferTexture = GenTexture(screenBufferHandle, _framebufferSize, true, null, TexturePixelType.Rgba32);
|
||||
}
|
||||
|
||||
private void CreateMainViewport()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OpenToolkit.Graphics.OpenGL4;
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
|
||||
[PublicAPI]
|
||||
public interface IOverlayManager
|
||||
{
|
||||
void AddOverlay(Overlay overlay);
|
||||
void RemoveOverlay(string id);
|
||||
bool HasOverlay(string id);
|
||||
bool AddOverlay(Overlay overlay);
|
||||
|
||||
Overlay GetOverlay(string id);
|
||||
T GetOverlay<T>(string id) where T : Overlay;
|
||||
bool RemoveOverlay(Overlay overlay);
|
||||
bool RemoveOverlay(Type overlayClass);
|
||||
bool RemoveOverlay<T>() where T : Overlay;
|
||||
|
||||
bool TryGetOverlay(string id, [NotNullWhen(true)] out Overlay? overlay);
|
||||
bool TryGetOverlay<T>(string id, [NotNullWhen(true)] out T? overlay) where T : Overlay;
|
||||
bool TryGetOverlay(Type overlayClass, out Overlay? overlay);
|
||||
bool TryGetOverlay<T>(out T? overlay) where T : Overlay;
|
||||
|
||||
Overlay GetOverlay(Type overlayClass);
|
||||
T GetOverlay<T>() where T : Overlay;
|
||||
|
||||
bool HasOverlay(Type overlayClass);
|
||||
bool HasOverlay<T>() where T : Overlay;
|
||||
|
||||
IEnumerable<Overlay> AllOverlays { get; }
|
||||
}
|
||||
|
||||
@@ -1,65 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Enums;
|
||||
using System;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
/// <summary>
|
||||
/// An overlay is used for fullscreen drawing in the game, for example parallax.
|
||||
/// An overlay is used for fullscreen drawing in the game. This can range from drawing parallax to a full screen shader.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public abstract class Overlay
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The ID of this overlay. This is used to identify it inside the <see cref="IOverlayManager"/>.
|
||||
/// Determines when this overlay is drawn in the rendering queue.
|
||||
/// </summary>
|
||||
public string ID { get; }
|
||||
|
||||
public virtual bool AlwaysDirty => false;
|
||||
public bool IsDirty => AlwaysDirty || _isDirty;
|
||||
public bool Drawing { get; private set; }
|
||||
|
||||
public virtual OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, <see cref="ScreenTexture"/> will be set to the current frame (at the moment before the overlay is rendered). This can be costly to performance, but
|
||||
/// some shaders will require it as a passed in uniform to operate.
|
||||
/// </summary>
|
||||
public virtual bool RequestScreenTexture => false;
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="RequestScreenTexture"> is true, then this will be set to the texture corresponding to the current frame. If false, it will always be null.
|
||||
/// </summary>
|
||||
public Texture? ScreenTexture = null;
|
||||
|
||||
/// <summary>
|
||||
/// Overlays on the same OverlaySpace will be drawn from lowest ZIndex to highest ZIndex. As an example, ZIndex -1 will be drawn before ZIndex 2.
|
||||
/// This value is 0 by default. Overlays with same ZIndex will be drawn in an random order.
|
||||
/// </summary>
|
||||
public int? ZIndex { get; set; }
|
||||
|
||||
protected IOverlayManager OverlayManager { get; }
|
||||
|
||||
public int? ZIndex { get; set; }
|
||||
private bool Disposed = false;
|
||||
|
||||
public virtual bool SubHandlesUseMainShader { get; } = true;
|
||||
|
||||
private bool _isDirty = true;
|
||||
|
||||
private readonly List<DrawingHandleBase> TempHandles = new();
|
||||
|
||||
private bool Disposed;
|
||||
|
||||
protected Overlay(string id)
|
||||
public Overlay()
|
||||
{
|
||||
OverlayManager = IoCManager.Resolve<IOverlayManager>();
|
||||
ID = id;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dispose(true);
|
||||
Disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~Overlay()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
/// <summary>
|
||||
/// If this function returns true, the target framebuffer will be wiped before applying this overlay to it.
|
||||
/// </summary>
|
||||
public virtual bool OverwriteTargetFrameBuffer(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,50 +58,34 @@ namespace Robust.Client.Graphics
|
||||
/// <param name="currentSpace">Current space that is being drawn. This function is called for every space that was set up in initialization.</param>
|
||||
protected abstract void Draw(DrawingHandleBase handle, OverlaySpace currentSpace);
|
||||
|
||||
public void Dirty()
|
||||
{
|
||||
_isDirty = true;
|
||||
protected internal virtual void FrameUpdate(FrameEventArgs args) { }
|
||||
|
||||
~Overlay() {
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
if (Disposed)
|
||||
return;
|
||||
else
|
||||
DisposeBehavior();
|
||||
}
|
||||
|
||||
protected virtual void DisposeBehavior(){
|
||||
Disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected internal virtual void FrameUpdate(FrameEventArgs args) { }
|
||||
|
||||
internal void ClydeRender(IRenderHandle renderHandle, OverlaySpace currentSpace)
|
||||
{
|
||||
DrawingHandleBase handle;
|
||||
if (currentSpace == OverlaySpace.WorldSpace)
|
||||
handle = renderHandle.DrawingHandleWorld;
|
||||
else
|
||||
if (currentSpace == OverlaySpace.ScreenSpace || currentSpace == OverlaySpace.ScreenSpaceBelowWorld)
|
||||
handle = renderHandle.DrawingHandleScreen;
|
||||
else
|
||||
handle = renderHandle.DrawingHandleWorld;
|
||||
|
||||
Draw(handle, currentSpace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines in which canvas layers an overlay gets drawn.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum OverlaySpace : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for matching bit flags.
|
||||
/// </summary>
|
||||
None = 0b0000,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn in the UI root, thus being in screen space.
|
||||
/// </summary>
|
||||
ScreenSpace = 0b0001,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn in the world root, thus being in world space.
|
||||
/// </summary>
|
||||
WorldSpace = 0b0010,
|
||||
|
||||
/// <summary>
|
||||
/// Drawn in screen coordinates, but behind the world.
|
||||
/// </summary>
|
||||
ScreenSpaceBelowWorld = 0b0100,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
internal class OverlayManager : IOverlayManagerInternal
|
||||
{
|
||||
private readonly Dictionary<string, Overlay> _overlays = new();
|
||||
private readonly Dictionary<Type, Overlay> _overlays = new Dictionary<Type, Overlay>();
|
||||
public IEnumerable<Overlay> AllOverlays => _overlays.Values;
|
||||
|
||||
public void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
@@ -17,59 +19,80 @@ namespace Robust.Client.Graphics
|
||||
}
|
||||
}
|
||||
|
||||
public void AddOverlay(Overlay overlay)
|
||||
public bool AddOverlay(Overlay overlay)
|
||||
{
|
||||
if (_overlays.ContainsKey(overlay.ID))
|
||||
{
|
||||
throw new InvalidOperationException($"We already have an overlay with ID '{overlay.ID}'");
|
||||
if(_overlays.ContainsKey(overlay.GetType()))
|
||||
return false;
|
||||
_overlays.Add(overlay.GetType(), overlay);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public bool RemoveOverlay(Type overlayClass)
|
||||
{
|
||||
if(!overlayClass.IsSubclassOf(typeof(Overlay))){
|
||||
Logger.Error("RemoveOverlay was called with arg: " + overlayClass.ToString() + ", which is not a subclass of Overlay!");
|
||||
return false;
|
||||
}
|
||||
|
||||
_overlays.Add(overlay.ID, overlay);
|
||||
return _overlays.Remove(overlayClass);
|
||||
}
|
||||
|
||||
public Overlay GetOverlay(string id)
|
||||
{
|
||||
return _overlays[id];
|
||||
public bool RemoveOverlay<T>() where T : Overlay{
|
||||
return RemoveOverlay(typeof(T));
|
||||
}
|
||||
|
||||
public T GetOverlay<T>(string id) where T : Overlay
|
||||
{
|
||||
return (T) GetOverlay(id);
|
||||
public bool RemoveOverlay(Overlay overlay) {
|
||||
return _overlays.Remove(overlay.GetType());
|
||||
}
|
||||
|
||||
public bool HasOverlay(string id)
|
||||
{
|
||||
return _overlays.ContainsKey(id);
|
||||
}
|
||||
|
||||
public void RemoveOverlay(string id)
|
||||
|
||||
|
||||
|
||||
public bool TryGetOverlay(Type overlayClass, [NotNullWhen(true)] out Overlay? overlay)
|
||||
{
|
||||
if (!_overlays.TryGetValue(id, out var overlay))
|
||||
{
|
||||
return;
|
||||
overlay = null;
|
||||
if (!overlayClass.IsSubclassOf(typeof(Overlay))){
|
||||
Logger.Error("TryGetOverlay was called with arg: " + overlayClass.ToString() + ", which is not a subclass of Overlay!");
|
||||
return false;
|
||||
}
|
||||
|
||||
overlay.Dispose();
|
||||
_overlays.Remove(id);
|
||||
return _overlays.TryGetValue(overlayClass, out overlay);
|
||||
}
|
||||
|
||||
public bool TryGetOverlay(string id, [NotNullWhen(true)] out Overlay? overlay)
|
||||
{
|
||||
return _overlays.TryGetValue(id, out overlay);
|
||||
}
|
||||
|
||||
public bool TryGetOverlay<T>(string id, [NotNullWhen(true)] out T? overlay) where T : Overlay
|
||||
{
|
||||
if (_overlays.TryGetValue(id, out var value))
|
||||
{
|
||||
overlay = (T) value;
|
||||
public bool TryGetOverlay<T>([NotNullWhen(true)] out T? overlay) where T : Overlay {
|
||||
overlay = null;
|
||||
if(_overlays.TryGetValue(typeof(T), out Overlay? toReturn)){
|
||||
overlay = (T)toReturn;
|
||||
return true;
|
||||
}
|
||||
|
||||
overlay = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<Overlay> AllOverlays => _overlays.Values;
|
||||
|
||||
|
||||
|
||||
public Overlay GetOverlay(Type overlayClass) {
|
||||
return _overlays[overlayClass];
|
||||
}
|
||||
|
||||
public T GetOverlay<T>() where T : Overlay {
|
||||
return (T)_overlays[typeof(T)];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool HasOverlay(Type overlayClass) {
|
||||
if (!overlayClass.IsSubclassOf(typeof(Overlay)))
|
||||
Logger.Error("HasOverlay was called with arg: " + overlayClass.ToString() + ", which is not a subclass of Overlay!");
|
||||
return _overlays.Remove(overlayClass);
|
||||
}
|
||||
|
||||
public bool HasOverlay<T>() where T : Overlay {
|
||||
return _overlays.Remove(typeof(T));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace Robust.Client.Graphics
|
||||
public sealed class State : IRsiStateLike
|
||||
{
|
||||
// List of delays for the frame to reach the next frame.
|
||||
private readonly float[] Delays;
|
||||
public readonly float[] Delays;
|
||||
|
||||
// 2D array for the texture to use for each animation frame at each direction.
|
||||
private readonly Texture[][] Icons;
|
||||
public readonly Texture[][] Icons;
|
||||
|
||||
internal State(Vector2i size, StateId stateId, DirectionType direction, float[] delays, Texture[][] icons)
|
||||
{
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "RSI Image Format Validation Schema V1",
|
||||
"description": "Robust Station Image",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"size": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"x": {"type": "integer", "minimum": 1},
|
||||
"y": {"type": "integer", "minimum": 1}
|
||||
},
|
||||
"required": ["x","y"]
|
||||
},
|
||||
"directions": {
|
||||
"type": "integer",
|
||||
"enum": [1,4,8]
|
||||
},
|
||||
"state": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"flags": {"type": "object"}, //To be de-serialized as a Dictionary
|
||||
"directions": {"$ref": "#/definitions/directions"},
|
||||
"delays": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {"type": "number", "minimum": 0, "exclusiveMinimum": true} //number == float
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["name"] //'delays' is marked as optional in the spec
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"version": {"type": "integer", "minimum": 1, "maximum": 1},
|
||||
"size": {"$ref": "#/definitions/size"},
|
||||
"states": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/state"},
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"required": ["version","size","states"]
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -67,7 +68,7 @@ namespace Robust.Client.Physics
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
IoCManager.Resolve<IOverlayManager>().RemoveOverlay(nameof(PhysicsIslandOverlay));
|
||||
IoCManager.Resolve<IOverlayManager>().RemoveOverlay(typeof(PhysicsIslandOverlay));
|
||||
}
|
||||
|
||||
private void HandleIslandSolveMessage(IslandSolveMessage message)
|
||||
@@ -94,7 +95,7 @@ namespace Robust.Client.Physics
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public PhysicsIslandOverlay() : base(nameof(PhysicsIslandOverlay))
|
||||
public PhysicsIslandOverlay()
|
||||
{
|
||||
_islandSystem = EntitySystem.Get<DebugPhysicsIslandSystem>();
|
||||
_eyeManager = IoCManager.Resolve<IEyeManager>();
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
|
||||
namespace Robust.Client.Placement
|
||||
{
|
||||
public partial class PlacementManager
|
||||
@@ -7,10 +9,9 @@ namespace Robust.Client.Placement
|
||||
internal class PlacementOverlay : Overlay
|
||||
{
|
||||
private readonly PlacementManager _manager;
|
||||
public override bool AlwaysDirty => true;
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public PlacementOverlay(PlacementManager manager) : base("placement")
|
||||
public PlacementOverlay(PlacementManager manager)
|
||||
{
|
||||
_manager = manager;
|
||||
ZIndex = 100;
|
||||
|
||||
@@ -10,5 +10,6 @@ namespace Robust.Client.ResourceManagement
|
||||
void RsiLoaded(RsiLoadedEventArgs eventArgs);
|
||||
|
||||
void MountLoaderApi(IFileApi api, string apiPrefix, ResourcePath? prefix=null);
|
||||
void PreloadTextures();
|
||||
}
|
||||
}
|
||||
|
||||
187
Robust.Client/ResourceManagement/ResourceCache.Preload.cs
Normal file
187
Robust.Client/ResourceManagement/ResourceCache.Preload.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
internal partial class ResourceCache
|
||||
{
|
||||
[Dependency] private readonly IClyde _clyde = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
public void PreloadTextures()
|
||||
{
|
||||
var sawmill = _logManager.GetSawmill("res.preload");
|
||||
|
||||
PreloadTextures(sawmill);
|
||||
PreloadRsis(sawmill);
|
||||
}
|
||||
|
||||
private void PreloadTextures(ISawmill sawmill)
|
||||
{
|
||||
sawmill.Debug("Preloading textures...");
|
||||
var sw = Stopwatch.StartNew();
|
||||
var resList = GetTypeDict<TextureResource>();
|
||||
|
||||
var texList = ContentFindFiles("/Textures/")
|
||||
// Skip PNG files inside RSIs.
|
||||
.Where(p => p.Extension == "png" && !p.ToString().Contains(".rsi/") && !resList.ContainsKey(p))
|
||||
.Select(p => new TextureResource.LoadStepData {Path = p})
|
||||
.ToArray();
|
||||
|
||||
Parallel.ForEach(texList, data =>
|
||||
{
|
||||
try
|
||||
{
|
||||
TextureResource.LoadPreTexture(this, data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Mark failed loads as bad and skip them in the next few stages.
|
||||
// Avoids any silly array resizing or similar.
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
data.Bad = true;
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var data in texList)
|
||||
{
|
||||
if (data.Bad)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
TextureResource.LoadTexture(_clyde, data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
data.Bad = true;
|
||||
}
|
||||
}
|
||||
|
||||
var errors = 0;
|
||||
foreach (var data in texList)
|
||||
{
|
||||
if (data.Bad)
|
||||
{
|
||||
errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var texResource = new TextureResource();
|
||||
texResource.LoadFinish(this, data);
|
||||
resList[data.Path] = texResource;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
data.Bad = true;
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
sawmill.Debug(
|
||||
"Preloaded {CountLoaded} textures ({CountErrored} errored) in {LoadTime}",
|
||||
texList.Length,
|
||||
errors,
|
||||
sw.Elapsed);
|
||||
}
|
||||
|
||||
private void PreloadRsis(ISawmill sawmill)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var resList = GetTypeDict<RSIResource>();
|
||||
|
||||
var rsiList = ContentFindFiles("/Textures/")
|
||||
.Where(p => p.ToString().EndsWith(".rsi/meta.json"))
|
||||
.Select(c => c.Directory)
|
||||
.Where(p => !resList.ContainsKey(p))
|
||||
.Select(p => new RSIResource.LoadStepData {Path = p})
|
||||
.ToArray();
|
||||
|
||||
Parallel.ForEach(rsiList, data =>
|
||||
{
|
||||
try
|
||||
{
|
||||
RSIResource.LoadPreTexture(this, data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Mark failed loads as bad and skip them in the next few stages.
|
||||
// Avoids any silly array resizing or similar.
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
data.Bad = true;
|
||||
}
|
||||
});
|
||||
|
||||
foreach (var data in rsiList)
|
||||
{
|
||||
if (data.Bad)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
RSIResource.LoadTexture(_clyde, data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
data.Bad = true;
|
||||
}
|
||||
}
|
||||
|
||||
Parallel.ForEach(rsiList, data =>
|
||||
{
|
||||
if (data.Bad)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
RSIResource.LoadPostTexture(data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
data.Bad = true;
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
}
|
||||
});
|
||||
|
||||
var errors = 0;
|
||||
foreach (var data in rsiList)
|
||||
{
|
||||
if (data.Bad)
|
||||
{
|
||||
errors += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var rsiRes = new RSIResource();
|
||||
rsiRes.LoadFinish(this, data);
|
||||
resList[data.Path] = rsiRes;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
sawmill.Error($"Exception while loading RSI {data.Path}:\n{e}");
|
||||
data.Bad = true;
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
sawmill.Debug(
|
||||
"Preloaded {CountLoaded} RSIs ({CountErrored} errored) in {LoadTime}",
|
||||
rsiList.Length,
|
||||
errors,
|
||||
sw.Elapsed);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,17 +2,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
#if DEBUG
|
||||
using NJsonSchema;
|
||||
#endif
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
@@ -24,6 +20,14 @@ namespace Robust.Client.ResourceManagement
|
||||
/// </summary>
|
||||
public sealed class RSIResource : BaseResource
|
||||
{
|
||||
private static readonly float[] OneArray = {1};
|
||||
|
||||
private static readonly JsonSerializerOptions SerializerOptions =
|
||||
new JsonSerializerOptions(JsonSerializerDefaults.Web)
|
||||
{
|
||||
AllowTrailingCommas = true
|
||||
};
|
||||
|
||||
public RSI RSI { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
@@ -38,64 +42,59 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
public override void Load(IResourceCache cache, ResourcePath path)
|
||||
{
|
||||
var manifestPath = path / "meta.json";
|
||||
string manifestContents;
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
using (var manifestFile = cache.ContentFileRead(manifestPath))
|
||||
using (var reader = new StreamReader(manifestFile))
|
||||
{
|
||||
manifestContents = reader.ReadToEnd();
|
||||
}
|
||||
var loadStepData = new LoadStepData {Path = path};
|
||||
LoadPreTexture(cache, loadStepData);
|
||||
|
||||
#if DEBUG
|
||||
if (RSISchema != null)
|
||||
{
|
||||
var errors = RSISchema.Validate(manifestContents);
|
||||
if (errors.Count != 0)
|
||||
{
|
||||
Logger.Error($"Unable to load RSI from '{path}', {errors.Count} errors:");
|
||||
// Load atlas.
|
||||
LoadTexture(clyde, loadStepData);
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
Logger.Error("{0}", error.ToString());
|
||||
}
|
||||
LoadPostTexture(loadStepData);
|
||||
LoadFinish(cache, loadStepData);
|
||||
|
||||
throw new RSILoadException($"{errors.Count} errors while loading RSI. See console.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
loadStepData.AtlasSheet.Dispose();
|
||||
}
|
||||
|
||||
// Ok schema validated just fine.
|
||||
var manifestJson = JObject.Parse(manifestContents);
|
||||
internal static void LoadTexture(IClyde clyde, LoadStepData loadStepData)
|
||||
{
|
||||
loadStepData.AtlasTexture = clyde.LoadTextureFromImage(
|
||||
loadStepData.AtlasSheet,
|
||||
loadStepData.Path.ToString());
|
||||
}
|
||||
|
||||
var toAtlas = new List<(Image<Rgba32> src, Texture[][] output, int[][] indices, Vector2i[][] offsets, int totalFrameCount)>();
|
||||
internal static void LoadPreTexture(IResourceCache cache, LoadStepData data)
|
||||
{
|
||||
var metadata = LoadRsiMetadata(cache, data.Path);
|
||||
|
||||
var metaData = ParseMetaData(manifestJson);
|
||||
var frameSize = metaData.Size;
|
||||
var rsi = new RSI(frameSize, path);
|
||||
var stateCount = metadata.States.Length;
|
||||
var toAtlas = new StateReg[stateCount];
|
||||
|
||||
var callbackOffsets = new Dictionary<RSI.StateId, Vector2i[][]>();
|
||||
var frameSize = metadata.Size;
|
||||
var rsi = new RSI(frameSize, data.Path);
|
||||
|
||||
var callbackOffsets = new Dictionary<RSI.StateId, Vector2i[][]>(stateCount);
|
||||
|
||||
// Do every state.
|
||||
foreach (var stateObject in metaData.States)
|
||||
for (var index = 0; index < metadata.States.Length; index++)
|
||||
{
|
||||
// Load image from disk.
|
||||
var texPath = path / (stateObject.StateId + ".png");
|
||||
var stream = cache.ContentFileRead(texPath);
|
||||
Image<Rgba32> image;
|
||||
using (stream)
|
||||
{
|
||||
image = Image.Load<Rgba32>(stream);
|
||||
}
|
||||
var sheetSize = new Vector2i(image.Width, image.Height);
|
||||
ref var reg = ref toAtlas[index];
|
||||
|
||||
if (sheetSize.X % frameSize.X != 0 || sheetSize.Y % frameSize.Y != 0)
|
||||
var stateObject = metadata.States[index];
|
||||
// Load image from disk.
|
||||
var texPath = data.Path / (stateObject.StateId + ".png");
|
||||
using (var stream = cache.ContentFileRead(texPath))
|
||||
{
|
||||
reg.Src = Image.Load<Rgba32>(stream);
|
||||
}
|
||||
|
||||
if (reg.Src.Width % frameSize.X != 0 || reg.Src.Height % frameSize.Y != 0)
|
||||
{
|
||||
throw new RSILoadException("State image size is not a multiple of the icon size.");
|
||||
}
|
||||
|
||||
// Load all frames into a list so we can operate on it more sanely.
|
||||
var frameCount = stateObject.Delays.Sum(delayList => delayList.Length);
|
||||
reg.TotalFrameCount = stateObject.Delays.Sum(delayList => delayList.Length);
|
||||
|
||||
var (foldedDelays, foldedIndices) = FoldDelays(stateObject.Delays);
|
||||
|
||||
@@ -108,29 +107,34 @@ namespace Robust.Client.ResourceManagement
|
||||
callbackOffset[i] = new Vector2i[foldedIndices[0].Length];
|
||||
}
|
||||
|
||||
var state = new RSI.State(frameSize, stateObject.StateId, stateObject.DirType, foldedDelays, textures);
|
||||
reg.Output = textures;
|
||||
reg.Indices = foldedIndices;
|
||||
reg.Offsets = callbackOffset;
|
||||
|
||||
var state = new RSI.State(frameSize, stateObject.StateId, stateObject.DirType, foldedDelays,
|
||||
textures);
|
||||
rsi.AddState(state);
|
||||
|
||||
toAtlas.Add((image, textures, foldedIndices, callbackOffset, frameCount));
|
||||
callbackOffsets[stateObject.StateId] = callbackOffset;
|
||||
}
|
||||
|
||||
// Poorly hacked in texture atlas support here.
|
||||
var totalFrameCount = toAtlas.Sum(p => p.totalFrameCount);
|
||||
var totalFrameCount = toAtlas.Sum(p => p.TotalFrameCount);
|
||||
|
||||
// Generate atlas.
|
||||
var dimensionX = (int) MathF.Ceiling(MathF.Sqrt(totalFrameCount));
|
||||
var dimensionY = (int) MathF.Ceiling((float) totalFrameCount / dimensionX);
|
||||
|
||||
using var sheet = new Image<Rgba32>(dimensionX * frameSize.X, dimensionY * frameSize.Y);
|
||||
var sheet = new Image<Rgba32>(dimensionX * frameSize.X, dimensionY * frameSize.Y);
|
||||
|
||||
var sheetIndex = 0;
|
||||
foreach (var (src, _, _, _, frameCount) in toAtlas)
|
||||
for (var index = 0; index < toAtlas.Length; index++)
|
||||
{
|
||||
ref var reg = ref toAtlas[index];
|
||||
// Blit all the frames over.
|
||||
for (var i = 0; i < frameCount; i++)
|
||||
for (var i = 0; i < reg.TotalFrameCount; i++)
|
||||
{
|
||||
var srcWidth = (src.Width / frameSize.X);
|
||||
var srcWidth = (reg.Src.Width / frameSize.X);
|
||||
var srcColumn = i % srcWidth;
|
||||
var srcRow = i / srcWidth;
|
||||
var srcPos = (srcColumn * frameSize.X, srcRow * frameSize.Y);
|
||||
@@ -141,30 +145,49 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
var srcBox = UIBox2i.FromDimensions(srcPos, frameSize);
|
||||
|
||||
src.Blit(srcBox, sheet, sheetPos);
|
||||
reg.Src.Blit(srcBox, sheet, sheetPos);
|
||||
}
|
||||
|
||||
sheetIndex += frameCount;
|
||||
sheetIndex += reg.TotalFrameCount;
|
||||
}
|
||||
|
||||
// Load atlas.
|
||||
var texture = Texture.LoadFromImage(sheet, path.ToString());
|
||||
for (var i = 0; i < toAtlas.Length; i++)
|
||||
{
|
||||
ref var reg = ref toAtlas[i];
|
||||
reg.Src.Dispose();
|
||||
}
|
||||
|
||||
data.Rsi = rsi;
|
||||
data.AtlasSheet = sheet;
|
||||
data.AtlasList = toAtlas;
|
||||
data.FrameSize = frameSize;
|
||||
data.DimX = dimensionX;
|
||||
data.CallbackOffsets = callbackOffsets;
|
||||
}
|
||||
|
||||
internal static void LoadPostTexture(LoadStepData data)
|
||||
{
|
||||
var dimX = data.DimX;
|
||||
var toAtlas = data.AtlasList;
|
||||
var frameSize = data.FrameSize;
|
||||
var texture = data.AtlasTexture;
|
||||
|
||||
var sheetOffset = 0;
|
||||
foreach (var (_, output, indices, offsets, frameCount) in toAtlas)
|
||||
for (var toAtlasIndex = 0; toAtlasIndex < toAtlas.Length; toAtlasIndex++)
|
||||
{
|
||||
for (var i = 0; i < indices.Length; i++)
|
||||
ref var reg = ref toAtlas[toAtlasIndex];
|
||||
for (var i = 0; i < reg.Indices.Length; i++)
|
||||
{
|
||||
var dirIndices = indices[i];
|
||||
var dirOutput = output[i];
|
||||
var dirOffsets = offsets[i];
|
||||
var dirIndices = reg.Indices[i];
|
||||
var dirOutput = reg.Output[i];
|
||||
var dirOffsets = reg.Offsets[i];
|
||||
|
||||
for (var j = 0; j < dirIndices.Length; j++)
|
||||
{
|
||||
var index = sheetOffset + dirIndices[j];
|
||||
|
||||
var sheetColumn = index % dimensionX;
|
||||
var sheetRow = index / dimensionX;
|
||||
var sheetColumn = index % dimX;
|
||||
var sheetRow = index / dimX;
|
||||
var sheetPos = (sheetColumn * frameSize.X, sheetRow * frameSize.Y);
|
||||
|
||||
dirOffsets[j] = sheetPos;
|
||||
@@ -172,22 +195,104 @@ namespace Robust.Client.ResourceManagement
|
||||
}
|
||||
}
|
||||
|
||||
sheetOffset += frameCount;
|
||||
sheetOffset += reg.TotalFrameCount;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (image, _, _, _, _) in toAtlas)
|
||||
{
|
||||
image.Dispose();
|
||||
}
|
||||
|
||||
RSI = rsi;
|
||||
internal void LoadFinish(IResourceCache cache, LoadStepData data)
|
||||
{
|
||||
RSI = data.Rsi;
|
||||
|
||||
if (cache is IResourceCacheInternal cacheInternal)
|
||||
{
|
||||
cacheInternal.RsiLoaded(new RsiLoadedEventArgs(path, this, sheet, callbackOffsets));
|
||||
cacheInternal.RsiLoaded(new RsiLoadedEventArgs(data.Path, this, data.AtlasSheet, data.CallbackOffsets));
|
||||
}
|
||||
}
|
||||
|
||||
private static RsiMetadata LoadRsiMetadata(IResourceCache cache, ResourcePath path)
|
||||
{
|
||||
var manifestPath = path / "meta.json";
|
||||
string manifestContents;
|
||||
|
||||
using (var manifestFile = cache.ContentFileRead(manifestPath))
|
||||
using (var reader = new StreamReader(manifestFile))
|
||||
{
|
||||
manifestContents = reader.ReadToEnd();
|
||||
}
|
||||
|
||||
// Ok schema validated just fine.
|
||||
var manifestJson = JsonSerializer.Deserialize<RsiJsonMetadata>(manifestContents, SerializerOptions);
|
||||
|
||||
if (manifestJson == null)
|
||||
throw new RSILoadException("Manifest JSON was null!");
|
||||
|
||||
var size = manifestJson.Size;
|
||||
var states = new StateMetadata[manifestJson.States.Length];
|
||||
|
||||
for (var stateI = 0; stateI < manifestJson.States.Length; stateI++)
|
||||
{
|
||||
var stateObject = manifestJson.States[stateI];
|
||||
var stateName = stateObject.Name;
|
||||
RSI.State.DirectionType directions;
|
||||
int dirValue;
|
||||
|
||||
if (stateObject.Directions is { } dirVal)
|
||||
{
|
||||
dirValue = dirVal;
|
||||
directions = dirVal switch
|
||||
{
|
||||
1 => RSI.State.DirectionType.Dir1,
|
||||
4 => RSI.State.DirectionType.Dir4,
|
||||
8 => RSI.State.DirectionType.Dir8,
|
||||
_ => throw new RSILoadException($"Invalid direction: {dirValue} expected 1, 4 or 8")
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
dirValue = 1;
|
||||
directions = RSI.State.DirectionType.Dir1;
|
||||
}
|
||||
|
||||
// We can ignore selectors and flags for now,
|
||||
// because they're not used yet!
|
||||
|
||||
// Get the lists of delays.
|
||||
float[][] delays;
|
||||
if (stateObject.Delays != null)
|
||||
{
|
||||
delays = stateObject.Delays;
|
||||
|
||||
if (delays.Length != dirValue)
|
||||
{
|
||||
throw new RSILoadException(
|
||||
"DirectionsdirectionFramesList count does not match amount of delays specified.");
|
||||
}
|
||||
|
||||
for (var i = 0; i < delays.Length; i++)
|
||||
{
|
||||
var delayList = delays[i];
|
||||
if (delayList.Length == 0)
|
||||
{
|
||||
delays[i] = OneArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delays = new float[dirValue][];
|
||||
// No delays specified, default to 1 frame per dir.
|
||||
for (var i = 0; i < dirValue; i++)
|
||||
{
|
||||
delays[i] = OneArray;
|
||||
}
|
||||
}
|
||||
|
||||
states[stateI] = new StateMetadata(new RSI.StateId(stateName), directions, delays);
|
||||
}
|
||||
|
||||
return new RsiMetadata(size, states);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Folds a per-directional sets of animation delays
|
||||
/// into an equivalent set of animation delays and indices that works for every direction.
|
||||
@@ -336,109 +441,38 @@ namespace Robust.Client.ResourceManagement
|
||||
return (floatDelays, arrayIndices);
|
||||
}
|
||||
|
||||
internal static RsiMetadata ParseMetaData(JObject manifestJson)
|
||||
internal sealed class LoadStepData
|
||||
{
|
||||
var size = manifestJson["size"]!.ToObject<Vector2i>();
|
||||
var states = new List<StateMetadata>();
|
||||
|
||||
foreach (var stateObject in manifestJson["states"]!.Cast<JObject>())
|
||||
{
|
||||
var stateName = stateObject["name"]!.ToObject<string>()!;
|
||||
RSI.State.DirectionType directions;
|
||||
int dirValue;
|
||||
|
||||
if (stateObject.TryGetValue("directions", out var dirJToken))
|
||||
{
|
||||
dirValue= dirJToken.ToObject<int>();
|
||||
directions = dirValue switch
|
||||
{
|
||||
1 => RSI.State.DirectionType.Dir1,
|
||||
4 => RSI.State.DirectionType.Dir4,
|
||||
8 => RSI.State.DirectionType.Dir8,
|
||||
_ => throw new RSILoadException($"Invalid direction: {dirValue} expected 1, 4 or 8")
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
dirValue = 1;
|
||||
directions = RSI.State.DirectionType.Dir1;
|
||||
}
|
||||
|
||||
// We can ignore selectors and flags for now,
|
||||
// because they're not used yet!
|
||||
|
||||
// Get the lists of delays.
|
||||
float[][] delays;
|
||||
if (stateObject.TryGetValue("delays", out var delayToken))
|
||||
{
|
||||
delays = delayToken.ToObject<float[][]>()!;
|
||||
|
||||
if (delays.Length != dirValue)
|
||||
{
|
||||
throw new RSILoadException(
|
||||
"DirectionsdirectionFramesList count does not match amount of delays specified.");
|
||||
}
|
||||
|
||||
for (var i = 0; i < delays.Length; i++)
|
||||
{
|
||||
var delayList = delays[i];
|
||||
if (delayList.Length == 0)
|
||||
{
|
||||
delays[i] = new float[] {1};
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delays = new float[dirValue][];
|
||||
// No delays specified, default to 1 frame per dir.
|
||||
for (var i = 0; i < dirValue; i++)
|
||||
{
|
||||
delays[i] = new float[] {1};
|
||||
}
|
||||
}
|
||||
|
||||
states.Add(new StateMetadata(new RSI.StateId(stateName), directions, delays));
|
||||
}
|
||||
|
||||
return new RsiMetadata(size, states);
|
||||
public bool Bad;
|
||||
public ResourcePath Path = default!;
|
||||
public Image<Rgba32> AtlasSheet = default!;
|
||||
public int DimX;
|
||||
public StateReg[] AtlasList = default!;
|
||||
public Vector2i FrameSize;
|
||||
public Dictionary<RSI.StateId, Vector2i[][]> CallbackOffsets = default!;
|
||||
public Texture AtlasTexture = default!;
|
||||
public RSI Rsi = default!;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private static readonly JsonSchema? RSISchema = GetSchema();
|
||||
|
||||
private static JsonSchema? GetSchema()
|
||||
internal struct StateReg
|
||||
{
|
||||
try
|
||||
{
|
||||
string schema;
|
||||
using (var schemaStream = Assembly.GetExecutingAssembly()
|
||||
.GetManifestResourceStream("Robust.Client.Graphics.RSI.RSISchema.json")!)
|
||||
using (var schemaReader = new StreamReader(schemaStream))
|
||||
{
|
||||
schema = schemaReader.ReadToEnd();
|
||||
}
|
||||
|
||||
return JsonSchema.FromJsonAsync(schema).Result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Console.WriteLine("Failed to load RSI JSON Schema!\n{0}", e);
|
||||
return null;
|
||||
}
|
||||
public Image<Rgba32> Src;
|
||||
public Texture[][] Output;
|
||||
public int[][] Indices;
|
||||
public Vector2i[][] Offsets;
|
||||
public int TotalFrameCount;
|
||||
}
|
||||
#endif
|
||||
|
||||
internal sealed class RsiMetadata
|
||||
{
|
||||
public RsiMetadata(Vector2i size, List<StateMetadata> states)
|
||||
public RsiMetadata(Vector2i size, StateMetadata[] states)
|
||||
{
|
||||
Size = size;
|
||||
States = states;
|
||||
}
|
||||
|
||||
public Vector2i Size { get; }
|
||||
public List<StateMetadata> States { get; }
|
||||
public StateMetadata[] States { get; }
|
||||
}
|
||||
|
||||
internal sealed class StateMetadata
|
||||
@@ -466,6 +500,17 @@ namespace Robust.Client.ResourceManagement
|
||||
|
||||
public float[][] Delays { get; }
|
||||
}
|
||||
|
||||
// To be directly deserialized.
|
||||
[UsedImplicitly]
|
||||
private sealed record RsiJsonMetadata(Vector2i Size, StateJsonMetadata[] States)
|
||||
{
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
private sealed record StateJsonMetadata(string Name, int? Directions, float[][]? Delays)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.IO;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
@@ -9,41 +8,50 @@ using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Robust.Client.ResourceManagement
|
||||
{
|
||||
public class TextureResource : BaseResource
|
||||
public sealed class TextureResource : BaseResource
|
||||
{
|
||||
public const float ClickThreshold = 0.25f;
|
||||
|
||||
public override ResourcePath? Fallback => new("/Textures/noSprite.png");
|
||||
public override ResourcePath Fallback => new("/Textures/noSprite.png");
|
||||
public Texture Texture { get; private set; } = default!;
|
||||
|
||||
public override void Load(IResourceCache cache, ResourcePath path)
|
||||
{
|
||||
if (!cache.TryContentFileRead(path, out var stream))
|
||||
{
|
||||
throw new FileNotFoundException("Content file does not exist for texture");
|
||||
}
|
||||
var clyde = IoCManager.Resolve<IClyde>();
|
||||
|
||||
using (stream)
|
||||
{
|
||||
// Primarily for tracking down iCCP sRGB errors in the image files.
|
||||
Logger.DebugS("res.tex", $"Loading texture {path}.");
|
||||
var data = new LoadStepData {Path = path};
|
||||
|
||||
var loadParameters = _tryLoadTextureParameters(cache, path) ?? TextureLoadParameters.Default;
|
||||
|
||||
var manager = IoCManager.Resolve<IClyde>();
|
||||
|
||||
using var image = Image.Load<Rgba32>(stream);
|
||||
|
||||
Texture = manager.LoadTextureFromImage(image, path.ToString(), loadParameters);
|
||||
|
||||
if (cache is IResourceCacheInternal cacheInternal)
|
||||
{
|
||||
cacheInternal.TextureLoaded(new TextureLoadedEventArgs(path, image, this));
|
||||
}
|
||||
}
|
||||
LoadPreTexture(cache, data);
|
||||
LoadTexture(clyde, data);
|
||||
LoadFinish(cache, data);
|
||||
}
|
||||
|
||||
private static TextureLoadParameters? _tryLoadTextureParameters(IResourceCache cache, ResourcePath path)
|
||||
internal static void LoadPreTexture(IResourceCache cache, LoadStepData data)
|
||||
{
|
||||
using (var stream = cache.ContentFileRead(data.Path))
|
||||
{
|
||||
data.Image = Image.Load<Rgba32>(stream);
|
||||
}
|
||||
|
||||
data.LoadParameters = TryLoadTextureParameters(cache, data.Path) ?? TextureLoadParameters.Default;
|
||||
}
|
||||
|
||||
internal static void LoadTexture(IClyde clyde, LoadStepData data)
|
||||
{
|
||||
data.Texture = clyde.LoadTextureFromImage(data.Image, data.Path.ToString(), data.LoadParameters);
|
||||
}
|
||||
|
||||
internal void LoadFinish(IResourceCache cache, LoadStepData data)
|
||||
{
|
||||
Texture = data.Texture;
|
||||
|
||||
if (cache is IResourceCacheInternal cacheInternal)
|
||||
{
|
||||
cacheInternal.TextureLoaded(new TextureLoadedEventArgs(data.Path, data.Image, this));
|
||||
}
|
||||
|
||||
data.Image.Dispose();
|
||||
}
|
||||
|
||||
private static TextureLoadParameters? TryLoadTextureParameters(IResourceCache cache, ResourcePath path)
|
||||
{
|
||||
var metaPath = path.WithName(path.Filename + ".yml");
|
||||
if (cache.TryContentFileRead(metaPath, out var stream))
|
||||
@@ -70,6 +78,15 @@ namespace Robust.Client.ResourceManagement
|
||||
return null;
|
||||
}
|
||||
|
||||
internal sealed class LoadStepData
|
||||
{
|
||||
public ResourcePath Path = default!;
|
||||
public Image<Rgba32> Image = default!;
|
||||
public TextureLoadParameters LoadParameters;
|
||||
public Texture Texture = default!;
|
||||
public bool Bad;
|
||||
}
|
||||
|
||||
// TODO: Due to a bug in Roslyn, NotNullIfNotNullAttribute doesn't work.
|
||||
// So this can't work with both nullables and non-nullables at the same time.
|
||||
// I decided to only have it work with non-nullables as such.
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" PrivateAssets="All" />
|
||||
<PackageReference Include="nfluidsynth" Version="0.3.1" />
|
||||
<PackageReference Include="NJsonSchema" Version="10.3.8" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.3" />
|
||||
@@ -39,9 +38,6 @@
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Graphics\RSI\RSISchema.json" Condition="'$(Configuration)' == 'Debug'">
|
||||
<LogicalName>Robust.Client.Graphics.RSI.RSISchema.json</LogicalName>
|
||||
</EmbeddedResource>
|
||||
|
||||
<EmbeddedResource Include="Graphics\Clyde\Shaders\*" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
@@ -67,6 +67,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
|
||||
Title = Loc.GetString("Entity Spawn Panel");
|
||||
|
||||
|
||||
SetSize = (250, 300);
|
||||
MinSize = (250, 200);
|
||||
|
||||
|
||||
@@ -42,19 +42,20 @@ namespace Robust.Client.Utility
|
||||
{
|
||||
var dstSpan = destination.GetPixelSpan();
|
||||
var dstWidth = destination.Width;
|
||||
var srcHeight = sourceRect.Height;
|
||||
var srcWidth = sourceRect.Width;
|
||||
|
||||
var (ox, oy) = destinationOffset;
|
||||
|
||||
for (var y = 0; y < sourceRect.Height; y++)
|
||||
for (var y = 0; y < srcHeight; y++)
|
||||
{
|
||||
var sourceRowOffset = sourceWidth * (y + sourceRect.Top) + sourceRect.Left;
|
||||
var destRowOffset = dstWidth * (y + oy) + ox;
|
||||
|
||||
for (var x = 0; x < sourceRect.Width; x++)
|
||||
{
|
||||
var pixel = source[x + sourceRowOffset];
|
||||
dstSpan[x + destRowOffset] = pixel;
|
||||
}
|
||||
var srcRow = source[sourceRowOffset..(sourceRowOffset + srcWidth)];
|
||||
var dstRow = dstSpan[destRowOffset..(destRowOffset + srcWidth)];
|
||||
|
||||
srcRow.CopyTo(dstRow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
@@ -41,6 +41,40 @@ namespace Robust.Client.Utility
|
||||
return specifier.RsiStateLike().Default;
|
||||
}
|
||||
|
||||
public static float[] FrameDelays(this SpriteSpecifier specifier) {
|
||||
var resc = IoCManager.Resolve<IResourceCache>();
|
||||
switch (specifier) {
|
||||
case SpriteSpecifier.Rsi rsi:
|
||||
if (resc.TryGetResource<RSIResource>(SpriteComponent.TextureRoot / rsi.RsiPath, out var theRsi)) {
|
||||
if (theRsi.RSI.TryGetState(rsi.RsiState, out var state)) {
|
||||
return state.Delays;
|
||||
}
|
||||
}
|
||||
Logger.Error("Failed to load RSI {0}", rsi.RsiPath);
|
||||
return new float[0];
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static Texture[] FrameArr(this SpriteSpecifier specifier) {
|
||||
var resc = IoCManager.Resolve<IResourceCache>();
|
||||
switch (specifier) {
|
||||
case SpriteSpecifier.Rsi rsi:
|
||||
if (resc.TryGetResource<RSIResource>(SpriteComponent.TextureRoot / rsi.RsiPath, out var theRsi)) {
|
||||
if (theRsi.RSI.TryGetState(rsi.RsiState, out var state)) {
|
||||
return state.Icons[0];
|
||||
}
|
||||
}
|
||||
Logger.Error("Failed to load RSI {0}", rsi.RsiPath);
|
||||
return new Texture[0];
|
||||
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public static IDirectionalTextureProvider DirFrame0(this SpriteSpecifier specifier)
|
||||
{
|
||||
return specifier.RsiStateLike();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Prometheus;
|
||||
@@ -141,6 +142,10 @@ namespace Robust.Server
|
||||
/// <inheritdoc />
|
||||
public bool Start(Func<ILogHandler>? logHandlerFactory = null)
|
||||
{
|
||||
var profilePath = Path.Join(Environment.CurrentDirectory, "AAAAAAAA");
|
||||
ProfileOptimization.SetProfileRoot(profilePath);
|
||||
ProfileOptimization.StartProfile("AAAAAAAAAA");
|
||||
|
||||
_config.Initialize(true);
|
||||
|
||||
if (LoadConfigAndUserData)
|
||||
@@ -178,6 +183,8 @@ namespace Robust.Server
|
||||
_config.OverrideConVars(_commandLineArgs.CVars);
|
||||
}
|
||||
|
||||
ProfileOptSetup.Setup(_config);
|
||||
|
||||
//Sets up Logging
|
||||
_logHandlerFactory = logHandlerFactory;
|
||||
|
||||
@@ -327,6 +334,8 @@ namespace Robust.Server
|
||||
WindowsTickPeriod.TimeBeginPeriod((uint) _config.GetCVar(CVars.SysWinTickPeriod));
|
||||
}
|
||||
|
||||
GC.Collect();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerAttachSystemMessage : EntitySystemMessage
|
||||
public class PlayerAttachSystemMessage : EntityEventArgs
|
||||
{
|
||||
public PlayerAttachSystemMessage(IEntity entity, IPlayerSession newPlayer)
|
||||
{
|
||||
@@ -63,7 +63,7 @@ namespace Robust.Server.GameObjects
|
||||
public IPlayerSession NewPlayer { get; }
|
||||
}
|
||||
|
||||
public class PlayerDetachedSystemMessage : EntitySystemMessage
|
||||
public class PlayerDetachedSystemMessage : EntityEventArgs
|
||||
{
|
||||
public PlayerDetachedSystemMessage(IEntity entity)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Server.GameObjects
|
||||
{
|
||||
public sealed class EntityDeletedMessage : EntitySystemMessage
|
||||
public sealed class EntityDeletedMessage : EntityEventArgs
|
||||
{
|
||||
public IEntity Entity { get; }
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Server.GameObjects
|
||||
/// <remarks>
|
||||
/// List is empty if it's no longer intersecting any.
|
||||
/// </remarks>
|
||||
public sealed class TileLookupUpdateMessage : EntitySystemMessage
|
||||
public sealed class TileLookupUpdateMessage : EntityEventArgs
|
||||
{
|
||||
public Dictionary<GridId, List<Vector2i>>? NewIndices { get; }
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message)
|
||||
{
|
||||
var newMsg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
newMsg.Type = EntityMessageType.SystemMessage;
|
||||
@@ -98,7 +98,7 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SendSystemNetworkMessage(EntitySystemMessage message, INetChannel targetConnection)
|
||||
public void SendSystemNetworkMessage(EntityEventArgs message, INetChannel targetConnection)
|
||||
{
|
||||
var newMsg = _networkManager.CreateNetMessage<MsgEntity>();
|
||||
newMsg.Type = EntityMessageType.SystemMessage;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
@@ -16,12 +17,12 @@ namespace Robust.Shared.Maths
|
||||
/// <summary>
|
||||
/// The X component of the Vector2i.
|
||||
/// </summary>
|
||||
public int X;
|
||||
[JsonInclude] public int X;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component of the Vector2i.
|
||||
/// </summary>
|
||||
public int Y;
|
||||
[JsonInclude] public int Y;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a vector from its coordinates.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json.Serialization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
@@ -12,12 +13,12 @@ namespace Robust.Shared.Maths
|
||||
/// <summary>
|
||||
/// The X component of the Vector2i.
|
||||
/// </summary>
|
||||
public uint X;
|
||||
[JsonInclude] public uint X;
|
||||
|
||||
/// <summary>
|
||||
/// The Y component of the Vector2i.
|
||||
/// </summary>
|
||||
public uint Y;
|
||||
[JsonInclude] public uint Y;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a vector from its coordinates.
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace Robust.Shared.Audio
|
||||
return GetAudio()?.Play(playerFilter, filename, coordinates, audioParams);
|
||||
}
|
||||
|
||||
internal class QueryAudioSystem : EntitySystemMessage
|
||||
internal class QueryAudioSystem : EntityEventArgs
|
||||
{
|
||||
public IAudioSystem? Audio { get; set; }
|
||||
}
|
||||
|
||||
@@ -93,6 +93,10 @@ namespace Robust.Shared
|
||||
public static readonly CVarDef<int> SysWinTickPeriod =
|
||||
CVarDef.Create("sys.win_tick_period", 3, CVar.SERVERONLY);
|
||||
|
||||
// On non-FULL_RELEASE builds, use ProfileOptimization/tiered JIT to speed up game startup.
|
||||
public static readonly CVarDef<bool> SysProfileOpt =
|
||||
CVarDef.Create("sys.profile_opt", true);
|
||||
|
||||
#if DEBUG
|
||||
public static readonly CVarDef<float> NetFakeLoss = CVarDef.Create("net.fakeloss", 0f, CVar.CHEAT);
|
||||
public static readonly CVarDef<float> NetFakeLagMin = CVarDef.Create("net.fakelagmin", 0f, CVar.CHEAT);
|
||||
@@ -372,8 +376,9 @@ namespace Robust.Shared
|
||||
|
||||
// - Maximums
|
||||
// Squared
|
||||
// 35 m/s, AKA half a tile per frame allowed. Divide this by frametime to get units per second.
|
||||
public static readonly CVarDef<float> MaxLinVelocity =
|
||||
CVarDef.Create("physics.maxlinvelocity", 4.0f);
|
||||
CVarDef.Create("physics.maxlinvelocity", 0.56f);
|
||||
|
||||
// Squared
|
||||
public static readonly CVarDef<float> MaxAngVelocity =
|
||||
@@ -385,5 +390,12 @@ namespace Robust.Shared
|
||||
|
||||
public static readonly CVarDef<bool> DiscordEnabled =
|
||||
CVarDef.Create("discord.enabled", true, CVar.CLIENTONLY);
|
||||
|
||||
/*
|
||||
* RES
|
||||
*/
|
||||
|
||||
public static readonly CVarDef<bool> ResCheckPathCasing =
|
||||
CVarDef.Create("res.checkpathcasing", false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Robust.Shared.Containers
|
||||
/// Raised when the contents of a container have been modified.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public abstract class ContainerModifiedMessage : EntitySystemMessage
|
||||
public abstract class ContainerModifiedMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The container being acted upon.
|
||||
|
||||
@@ -18,16 +18,19 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
private readonly DirectoryInfo _directory;
|
||||
private readonly ISawmill _sawmill;
|
||||
private readonly bool _checkCasing;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="directory">Directory to mount.</param>
|
||||
/// <param name="sawmill"></param>
|
||||
public DirLoader(DirectoryInfo directory, ISawmill sawmill)
|
||||
/// <param name="checkCasing"></param>
|
||||
public DirLoader(DirectoryInfo directory, ISawmill sawmill, bool checkCasing)
|
||||
{
|
||||
_directory = directory;
|
||||
_sawmill = sawmill;
|
||||
_checkCasing = checkCasing;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -80,6 +83,9 @@ namespace Robust.Shared.ContentPack
|
||||
[Conditional("DEBUG")]
|
||||
private void CheckPathCasing(ResourcePath path)
|
||||
{
|
||||
if (!_checkCasing)
|
||||
return;
|
||||
|
||||
// Run this inside the thread pool due to overhead.
|
||||
Task.Run(() =>
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Shared.ContentPack
|
||||
/// <summary>
|
||||
/// Get the full directory path that the executable is located in.
|
||||
/// </summary>
|
||||
private static string GetExecutableDirectory()
|
||||
internal static string GetExecutableDirectory()
|
||||
{
|
||||
// TODO: remove this shitty hack, either through making it less hardcoded into shared,
|
||||
// or by making our file structure less spaghetti somehow.
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace Robust.Shared.ContentPack
|
||||
throw new DirectoryNotFoundException("Specified directory does not exist: " + pathInfo.FullName);
|
||||
}
|
||||
|
||||
var loader = new DirLoader(pathInfo, Logger.GetSawmill("res"));
|
||||
var loader = new DirLoader(pathInfo, Logger.GetSawmill("res"), _config.GetCVar(CVars.ResCheckPathCasing));
|
||||
AddRoot(prefix, loader);
|
||||
}
|
||||
|
||||
|
||||
47
Robust.Shared/Enums/OverlaySpaces.cs
Normal file
47
Robust.Shared/Enums/OverlaySpaces.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Robust.Shared.Enums {
|
||||
/// <summary>
|
||||
/// Determines in which canvas layers an overlay gets drawn.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum OverlaySpace {
|
||||
/// <summary>
|
||||
/// Used for matching bit flags.
|
||||
/// </summary>
|
||||
None = 0b000000,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn in screen coordinates in the UI space above the world.
|
||||
/// </summary>
|
||||
ScreenSpace = 0b000001,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn directly above normal worldspace, but a stencil equivalent to the FOV will be applied.
|
||||
/// You likely want to use <see cref="WorldSpaceBelowFOV"/>. This space should only be used if you fully understand why you need this.
|
||||
/// </summary>
|
||||
WorldSpaceFOVStencil = 0b000010,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn above entities, lighting, and FOV.
|
||||
/// </summary>
|
||||
WorldSpace = 0b000100,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn beneath FOV; above lighting and entities.
|
||||
/// </summary>
|
||||
WorldSpaceBelowFOV = 0b001000,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn beneath entities, lighting, and FOV; above grids.
|
||||
/// </summary>
|
||||
WorldSpaceBelowEntities = 0b010000,
|
||||
|
||||
/// <summary>
|
||||
/// This overlay will be drawn in screen coordinates behind the world.
|
||||
/// </summary>
|
||||
ScreenSpaceBelowWorld = 0b100000,
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -12,6 +12,7 @@ namespace Robust.Shared.GameObjects
|
||||
public class ComponentDependencyManager : IComponentDependencyManager
|
||||
{
|
||||
[IoC.Dependency] private readonly IComponentFactory _componentFactory = null!;
|
||||
[IoC.Dependency] private readonly IComponentManager _componentManager = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Cache of queries and their corresponding field offsets
|
||||
@@ -22,18 +23,16 @@ namespace Robust.Shared.GameObjects
|
||||
new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnComponentAdd(IEntity entity, IComponent newComp)
|
||||
public void OnComponentAdd(EntityUid eUid, IComponent newComp)
|
||||
{
|
||||
SetDependencyForEntityComponents(entity, newComp.GetType(), newComp);
|
||||
InjectIntoComponent(entity, newComp);
|
||||
SetDependencyForEntityComponents(eUid, newComp, newComp.GetType());
|
||||
InjectIntoComponent(eUid, newComp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filling the dependencies of newComp by iterating over entity's components
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="newComp"></param>
|
||||
public void InjectIntoComponent(IEntity entity, IComponent newComp)
|
||||
private void InjectIntoComponent(EntityUid eUid, IComponent newComp)
|
||||
{
|
||||
var queries = GetPointerQueries(newComp);
|
||||
|
||||
@@ -43,7 +42,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
//get all present components in entity
|
||||
foreach (var entityComp in entity.GetAllComponents())
|
||||
foreach (var entityComp in _componentManager.GetComponents(eUid))
|
||||
{
|
||||
var entityCompReg = _componentFactory.GetRegistration(entityComp);
|
||||
foreach (var reference in entityCompReg.References)
|
||||
@@ -61,24 +60,22 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnComponentRemove(IEntity entity, IComponent removedComp)
|
||||
public void OnComponentRemove(EntityUid eUid, IComponent removedComp)
|
||||
{
|
||||
ClearRemovedComponentDependencies(removedComp);
|
||||
SetDependencyForEntityComponents(entity, removedComp.GetType(), null);
|
||||
SetDependencyForEntityComponents(eUid, null, removedComp.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates all dependencies to type compType on the entity (in fields on components), to the value of comp
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="compType"></param>
|
||||
/// <param name="comp"></param>
|
||||
private void SetDependencyForEntityComponents(IEntity entity, Type compType, IComponent? comp)
|
||||
private void SetDependencyForEntityComponents(EntityUid eUid, IComponent? comp, Type compType)
|
||||
{
|
||||
var compReg = _componentFactory.GetRegistration(compType);
|
||||
|
||||
//check if any are requesting our component as a dependency
|
||||
foreach (var entityComponent in entity.GetAllComponents())
|
||||
var entityComponents = _componentManager.GetComponents(eUid);
|
||||
foreach (var entityComponent in entityComponents)
|
||||
{
|
||||
//get entry for out entityComponent
|
||||
var queries = GetPointerQueries(entityComponent);
|
||||
@@ -113,7 +110,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private void SetField(object o, int offset, object? value)
|
||||
private static void SetField(object o, int offset, object? value)
|
||||
{
|
||||
var asDummy = Unsafe.As<FieldOffsetDummy>(o);
|
||||
ref var @ref = ref Unsafe.Add(ref asDummy.A, offset);
|
||||
@@ -182,7 +179,7 @@ namespace Robust.Shared.GameObjects
|
||||
return queries;
|
||||
}
|
||||
|
||||
private Action<object>? GetEventMethod(MethodInfo[] methods, string methodName, MethodInfo getterMethod)
|
||||
private static Action<object>? GetEventMethod(MethodInfo[] methods, string methodName, MethodInfo getterMethod)
|
||||
{
|
||||
var method = methods.FirstOrDefault(m => m.Name == methodName);
|
||||
if (method == null) return null;
|
||||
@@ -196,7 +193,7 @@ namespace Robust.Shared.GameObjects
|
||||
return o => @delegate((T) o);
|
||||
}
|
||||
|
||||
private int GetFieldOffset(Type type, FieldInfo field)
|
||||
private static int GetFieldOffset(Type type, FieldInfo field)
|
||||
{
|
||||
var fieldOffsetField = typeof(FieldOffsetDummy).GetField("A")!;
|
||||
var dynamicMethod = new DynamicMethod(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -15,12 +15,21 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public IComponent Component { get; }
|
||||
|
||||
/// <summary>
|
||||
/// EntityUid of the entity this component belongs to.
|
||||
/// </summary>
|
||||
public EntityUid OwnerUid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="ComponentEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The relevant component</param>
|
||||
protected ComponentEventArgs(IComponent component) => Component = component;
|
||||
|
||||
/// <param name="ownerUid">EntityUid of the entity this component belongs to.</param>
|
||||
protected ComponentEventArgs(IComponent component, EntityUid ownerUid)
|
||||
{
|
||||
Component = component;
|
||||
OwnerUid = ownerUid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -32,7 +41,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// Constructs a new instance of <see cref="AddedComponentEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The relevant component</param>
|
||||
public AddedComponentEventArgs(IComponent component) : base(component) { }
|
||||
/// <param name="uid">EntityUid of the entity this component belongs to.</param>
|
||||
public AddedComponentEventArgs(IComponent component, EntityUid uid) : base(component, uid) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,7 +54,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// Constructs a new instance of <see cref="RemovedComponentEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The relevant component</param>
|
||||
public RemovedComponentEventArgs(IComponent component) : base(component) { }
|
||||
/// <param name="uid">EntityUid of the entity this component belongs to.</param>
|
||||
public RemovedComponentEventArgs(IComponent component, EntityUid uid) : base(component, uid) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -56,7 +67,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Constructs a new instance of <see cref="DeletedComponentEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="component">The relevant component</param>
|
||||
public DeletedComponentEventArgs(IComponent component) : base(component) { }
|
||||
/// <param name="uid">EntityUid of the entity this component belongs to.</param>
|
||||
public DeletedComponentEventArgs(IComponent component, EntityUid uid) : base(component, uid) { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,6 +64,15 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
private readonly HashSet<string> IgnoredComponentNames = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<IComponentRegistration>? ComponentAdded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<(IComponentRegistration, Type)>? ComponentReferenceAdded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event Action<string>? ComponentIgnoreAdded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<Type> AllRegisteredTypes => types.Keys;
|
||||
|
||||
@@ -137,6 +146,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
netIDs[netID.Value] = registration;
|
||||
}
|
||||
ComponentAdded?.Invoke(registration);
|
||||
}
|
||||
|
||||
[Obsolete("Use RegisterClass and Attributes instead of the Register/RegisterReference combo")]
|
||||
@@ -158,6 +168,7 @@ namespace Robust.Shared.GameObjects
|
||||
throw new InvalidOperationException($"Attempted to register a reference twice: {@interface}");
|
||||
}
|
||||
registration.References.Add(@interface);
|
||||
ComponentReferenceAdded?.Invoke((registration, @interface));
|
||||
}
|
||||
|
||||
public void RegisterIgnore(string name, bool overwrite = false)
|
||||
@@ -178,12 +189,13 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
IgnoredComponentNames.Add(name);
|
||||
ComponentIgnoreAdded?.Invoke(name);
|
||||
}
|
||||
|
||||
private void RemoveComponent(string name)
|
||||
{
|
||||
var registration = names[name];
|
||||
|
||||
|
||||
names.Remove(registration.Name);
|
||||
_lowerCaseNames.Remove(registration.Name.ToLowerInvariant());
|
||||
types.Remove(registration.Type);
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Robust.Shared.GameObjects
|
||||
public class ComponentManager : IComponentManager
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IComponentDependencyManager _componentDependencyManager = default!;
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
@@ -45,9 +44,17 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<ComponentEventArgs>? ComponentDeleted;
|
||||
|
||||
private bool initialized;
|
||||
public void Initialize()
|
||||
{
|
||||
if (initialized)
|
||||
throw new InvalidOperationException("Already initialized.");
|
||||
|
||||
initialized = true;
|
||||
|
||||
FillComponentDict();
|
||||
_componentFactory.ComponentAdded += OnComponentAdded;
|
||||
_componentFactory.ComponentReferenceAdded += OnComponentReferenceAdded;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -59,6 +66,26 @@ namespace Robust.Shared.GameObjects
|
||||
_deleteSet.Clear();
|
||||
FillComponentDict();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_componentFactory.ComponentAdded -= OnComponentAdded;
|
||||
_componentFactory.ComponentReferenceAdded -= OnComponentReferenceAdded;
|
||||
}
|
||||
|
||||
private void OnComponentAdded(IComponentRegistration obj)
|
||||
{
|
||||
_entTraitDict.Add(obj.Type, new Dictionary<EntityUid, Component>());
|
||||
|
||||
var netID = obj.NetID;
|
||||
if (netID.HasValue)
|
||||
_entNetIdDict.Add(netID.Value, new Dictionary<EntityUid, Component>());
|
||||
}
|
||||
|
||||
private void OnComponentReferenceAdded((IComponentRegistration, Type) obj)
|
||||
{
|
||||
_entTraitDict.Add(obj.Item2, new Dictionary<EntityUid, Component>());
|
||||
}
|
||||
|
||||
#region Component Management
|
||||
|
||||
@@ -120,17 +147,16 @@ namespace Robust.Shared.GameObjects
|
||||
if (component.NetID != null)
|
||||
{
|
||||
// the main comp grid keeps this in sync
|
||||
|
||||
var netId = component.NetID.Value;
|
||||
_entNetIdDict[netId].Add(uid, component);
|
||||
|
||||
// mark the component as dirty for networking
|
||||
component.Dirty();
|
||||
|
||||
ComponentAdded?.Invoke(this, new AddedComponentEventArgs(component));
|
||||
ComponentAdded?.Invoke(this, new AddedComponentEventArgs(component, uid));
|
||||
}
|
||||
|
||||
_componentDependencyManager.OnComponentAdd(entity, component);
|
||||
_componentDependencyManager.OnComponentAdd(entity.Uid, component);
|
||||
|
||||
component.OnAdd();
|
||||
|
||||
@@ -243,8 +269,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
component.Running = false;
|
||||
component.OnRemove();
|
||||
_componentDependencyManager.OnComponentRemove(_entityManager.GetEntity(uid), component);
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component));
|
||||
_componentDependencyManager.OnComponentRemove(uid, component);
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, uid));
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -270,7 +296,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
component.Running = false;
|
||||
component.OnRemove(); // Sets delete
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component));
|
||||
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, component.Owner.Uid));
|
||||
|
||||
}
|
||||
|
||||
@@ -308,7 +334,7 @@ namespace Robust.Shared.GameObjects
|
||||
// mark the owning entity as dirty for networking
|
||||
component.Owner.Dirty();
|
||||
|
||||
ComponentDeleted?.Invoke(this, new DeletedComponentEventArgs(component));
|
||||
ComponentDeleted?.Invoke(this, new DeletedComponentEventArgs(component, entityUid));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -354,8 +380,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
var ent = _entityManager.GetEntity(uid);
|
||||
throw new KeyNotFoundException($"Entity {ent} does not have a component of type {type}");
|
||||
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {type}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -371,8 +396,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
var ent = _entityManager.GetEntity(uid);
|
||||
throw new KeyNotFoundException($"Entity {ent} does not have a component of NetID {netId}");
|
||||
throw new KeyNotFoundException($"Entity {uid} does not have a component of NetID {netId}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public class CollisionChangeMessage : EntitySystemMessage
|
||||
public class CollisionChangeMessage : EntityEventArgs
|
||||
{
|
||||
public PhysicsComponent Body { get; }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Called every tick for colliding bodies. Called once per pair.
|
||||
/// </summary>
|
||||
public sealed class CollisionMessage : EntitySystemMessage
|
||||
public sealed class CollisionMessage : EntityEventArgs
|
||||
{
|
||||
public readonly IPhysBody BodyA;
|
||||
public readonly IPhysBody BodyB;
|
||||
@@ -65,7 +65,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Sent whenever a <see cref="IPhysBody"/> is changed.
|
||||
/// </summary>
|
||||
public sealed class PhysicsUpdateMessage : EntitySystemMessage
|
||||
public sealed class PhysicsUpdateMessage : EntityEventArgs
|
||||
{
|
||||
public PhysicsComponent Component { get; }
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FixtureUpdateMessage : EntitySystemMessage
|
||||
public sealed class FixtureUpdateMessage : EntityEventArgs
|
||||
{
|
||||
public PhysicsComponent Body { get; }
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (_bodyType == value)
|
||||
return;
|
||||
|
||||
var oldAnchored = _bodyType == BodyType.Static;
|
||||
_bodyType = value;
|
||||
|
||||
ResetMassData();
|
||||
@@ -120,8 +121,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
RegenerateContacts();
|
||||
|
||||
var oldAnchored = _bodyType == BodyType.Static;
|
||||
var anchored = _bodyType == BodyType.Static;
|
||||
var anchored = value == BodyType.Static;
|
||||
|
||||
if (oldAnchored != anchored)
|
||||
{
|
||||
@@ -952,7 +952,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void ApplyLinearImpulse(in Vector2 impulse)
|
||||
{
|
||||
if (_bodyType != BodyType.Dynamic) return;
|
||||
if ((_bodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0) return;
|
||||
Awake = true;
|
||||
|
||||
LinearVelocity += impulse * _invMass;
|
||||
@@ -960,7 +960,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public void ApplyAngularImpulse(float impulse)
|
||||
{
|
||||
if (_bodyType != BodyType.Dynamic) return;
|
||||
if ((_bodyType & (BodyType.Dynamic | BodyType.KinematicController)) == 0x0) return;
|
||||
Awake = true;
|
||||
|
||||
AngularVelocity += impulse * InvI;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public interface IComponentDependencyManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when newComp has just been added into entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity in which newComp was added</param>
|
||||
/// <param name="eUid">Entity in which newComp was added</param>
|
||||
/// <param name="newComp">Added component</param>
|
||||
public void OnComponentAdd(IEntity entity, IComponent newComp);
|
||||
public void OnComponentAdd(EntityUid eUid, IComponent newComp);
|
||||
|
||||
/// <summary>
|
||||
/// Called when newComp has just been removed from entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity out of which newComp was removed</param>
|
||||
/// <param name="eUid">Entity out of which newComp was removed</param>
|
||||
/// <param name="removedComp">Removed component</param>
|
||||
public void OnComponentRemove(IEntity entity, IComponent removedComp); }
|
||||
public void OnComponentRemove(EntityUid eUid, IComponent removedComp); }
|
||||
}
|
||||
|
||||
@@ -129,5 +129,6 @@ namespace Robust.Shared.GameObjects
|
||||
IEnumerable<EntityUid> ChildEntityUids { get; }
|
||||
Matrix3 GetLocalMatrix();
|
||||
Matrix3 GetLocalMatrixInv();
|
||||
void DetachParentToNull();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@ namespace Robust.Shared.GameObjects
|
||||
EntitySystem.Get<OccluderSystem>().AddOrUpdateEntity(Owner, Owner.Transform.Coordinates);
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
protected override void Shutdown()
|
||||
{
|
||||
base.OnRemove();
|
||||
base.Shutdown();
|
||||
|
||||
var transform = Owner.Transform;
|
||||
var map = transform.MapID;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -51,20 +51,6 @@ namespace Robust.Shared.GameObjects
|
||||
_gridIndex = GridId.Invalid;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if(GridIndex != GridId.Invalid)
|
||||
{
|
||||
if(_mapManager.GridExists(_gridIndex))
|
||||
{
|
||||
Logger.DebugS("map", $"Entity {Owner.Uid} removed grid component, removing bound grid {_gridIndex}");
|
||||
_mapManager.DeleteGrid(_gridIndex);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
/// <param name="player"></param>
|
||||
/// <inheritdoc />
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
|
||||
@@ -241,7 +241,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
if (!_parent.IsValid())
|
||||
{
|
||||
DebugTools.Assert("Tried to move root node.");
|
||||
DebugTools.Assert("Parent is invalid while attempting to set WorldPosition - did you try to move root node?");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -433,25 +433,6 @@ namespace Robust.Shared.GameObjects
|
||||
UpdateEntityTree();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnRemove()
|
||||
{
|
||||
// DeleteEntity modifies our _children collection, we must cache the collection to iterate properly
|
||||
foreach (var childUid in _children.ToArray())
|
||||
{
|
||||
// Recursion: DeleteEntity calls the Transform.OnRemove function of child entities.
|
||||
Owner.EntityManager.DeleteEntity(childUid);
|
||||
}
|
||||
|
||||
// map does not have a parent node
|
||||
if (Parent != null)
|
||||
{
|
||||
DetachParentToNull();
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public void RunDeferred(Box2 worldAABB)
|
||||
{
|
||||
// if we resolved to (close enough) to the OG position then no update.
|
||||
@@ -525,7 +506,8 @@ namespace Robust.Shared.GameObjects
|
||||
Dirty();
|
||||
}
|
||||
|
||||
private void DetachParentToNull()
|
||||
/// <inheritdoc />
|
||||
public void DetachParentToNull()
|
||||
{
|
||||
var oldParent = Parent;
|
||||
if (oldParent == null)
|
||||
@@ -872,7 +854,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Raised whenever an entity moves.
|
||||
/// There is no guarantee it will be raised if they move in worldspace, only when moved relative to their parent.
|
||||
/// </summary>
|
||||
public class MoveEvent : EntitySystemMessage
|
||||
public class MoveEvent : HandledEntityEventArgs
|
||||
{
|
||||
public MoveEvent(IEntity sender, EntityCoordinates oldPos, EntityCoordinates newPos, Box2? worldAABB = null)
|
||||
{
|
||||
@@ -885,7 +867,6 @@ namespace Robust.Shared.GameObjects
|
||||
public IEntity Sender { get; }
|
||||
public EntityCoordinates OldPosition { get; }
|
||||
public EntityCoordinates NewPosition { get; }
|
||||
public bool Handled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// New AABB of the entity.
|
||||
@@ -896,7 +877,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Raised whenever this entity rotates in relation to their parent.
|
||||
/// </summary>
|
||||
public class RotateEvent : EntitySystemMessage
|
||||
public class RotateEvent : EntityEventArgs
|
||||
{
|
||||
public RotateEvent(IEntity sender, Angle oldRotation, Angle newRotation, Box2? worldAABB = null)
|
||||
{
|
||||
|
||||
@@ -310,8 +310,6 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void Shutdown()
|
||||
{
|
||||
EntityManager.ComponentManager.DisposeComponents(Uid);
|
||||
|
||||
// Entity manager culls us because we're set to Deleted.
|
||||
Deleted = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -12,7 +12,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// EntitySystems communicate with each other.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public interface IEventBus
|
||||
public interface IBroadcastEventBus
|
||||
{
|
||||
/// <summary>
|
||||
/// Subscribes an event handler for a event type.
|
||||
@@ -73,7 +73,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal interface IEntityEventBus : IEventBus
|
||||
internal interface IBroadcastEventBusInternal : IBroadcastEventBus
|
||||
{
|
||||
/// <summary>
|
||||
/// Raises all queued events onto the event bus. This needs to be called often.
|
||||
@@ -91,8 +91,10 @@ namespace Robust.Shared.GameObjects
|
||||
All = Local | Network,
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
internal class EntityEventBus : IEntityEventBus
|
||||
/// <summary>
|
||||
/// Implements the event broadcast functions.
|
||||
/// </summary>
|
||||
internal partial class EntityEventBus : IBroadcastEventBusInternal
|
||||
{
|
||||
private delegate void EventHandler(object ev);
|
||||
|
||||
263
Robust.Shared/GameObjects/EntityEventBus.Directed.cs
Normal file
263
Robust.Shared/GameObjects/EntityEventBus.Directed.cs
Normal file
@@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public interface IEventBus : IDirectedEventBus, IBroadcastEventBus { }
|
||||
|
||||
public interface IDirectedEventBus
|
||||
{
|
||||
void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = true)
|
||||
where TEvent:EntityEventArgs;
|
||||
|
||||
void SubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs;
|
||||
|
||||
void UnsubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs;
|
||||
}
|
||||
|
||||
internal partial class EntityEventBus : IDirectedEventBus, IEventBus, IDisposable
|
||||
{
|
||||
private delegate void DirectedEventHandler(EntityUid uid, IComponent comp, EntityEventArgs args);
|
||||
|
||||
private IEntityManager _entMan;
|
||||
private EventTables _eventTables;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="EntityEventBus"/>.
|
||||
/// </summary>
|
||||
/// <param name="entMan">The entity manager to watch for entity/component events.</param>
|
||||
public EntityEventBus(IEntityManager entMan)
|
||||
{
|
||||
_entMan = entMan;
|
||||
_eventTables = new EventTables(_entMan);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = true)
|
||||
where TEvent:EntityEventArgs
|
||||
{
|
||||
_eventTables.Dispatch(uid, typeof(TEvent), args);
|
||||
|
||||
// we also broadcast it so the call site does not have to.
|
||||
if(broadcast)
|
||||
RaiseEvent(EventSource.Local, args);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
void EventHandler(EntityUid uid, IComponent comp, EntityEventArgs args)
|
||||
=> handler(uid, (TComp) comp, (TEvent) args);
|
||||
|
||||
_eventTables.Subscribe(typeof(TComp), typeof(TEvent), EventHandler);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void UnsubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
_eventTables.Unsubscribe(typeof(TComp), typeof(TEvent));
|
||||
}
|
||||
|
||||
private class EventTables : IDisposable
|
||||
{
|
||||
private IEntityManager _entMan;
|
||||
|
||||
// eUid -> EventType -> { CompType1, ... CompTypeN }
|
||||
private Dictionary<EntityUid, Dictionary<Type, HashSet<Type>>> _eventTables;
|
||||
|
||||
// EventType -> CompType -> Handler
|
||||
private Dictionary<Type, Dictionary<Type, DirectedEventHandler>> _subscriptions;
|
||||
|
||||
// prevents shitcode, get your subscriptions figured out before you start spawning entities
|
||||
private bool _subscriptionLock;
|
||||
|
||||
public EventTables(IEntityManager entMan)
|
||||
{
|
||||
_entMan = entMan;
|
||||
|
||||
_entMan.EntityAdded += OnEntityAdded;
|
||||
_entMan.EntityDeleted += OnEntityDeleted;
|
||||
|
||||
_entMan.ComponentManager.ComponentAdded += OnComponentAdded;
|
||||
_entMan.ComponentManager.ComponentRemoved += OnComponentRemoved;
|
||||
|
||||
_eventTables = new();
|
||||
_subscriptions = new();
|
||||
_subscriptionLock = false;
|
||||
}
|
||||
|
||||
private void OnEntityAdded(object? sender, EntityUid e)
|
||||
{
|
||||
AddEntity(e);
|
||||
}
|
||||
|
||||
private void OnEntityDeleted(object? sender, EntityUid e)
|
||||
{
|
||||
RemoveEntity(e);
|
||||
}
|
||||
|
||||
private void OnComponentAdded(object? sender, ComponentEventArgs e)
|
||||
{
|
||||
_subscriptionLock = true;
|
||||
|
||||
AddComponent(e.OwnerUid, e.Component.GetType());
|
||||
}
|
||||
|
||||
private void OnComponentRemoved(object? sender, ComponentEventArgs e)
|
||||
{
|
||||
RemoveComponent(e.OwnerUid, e.Component.GetType());
|
||||
}
|
||||
|
||||
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>();
|
||||
_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}");
|
||||
|
||||
compSubs.Add(eventType, handler);
|
||||
}
|
||||
}
|
||||
|
||||
public void Unsubscribe(Type compType, Type eventType)
|
||||
{
|
||||
if (_subscriptionLock)
|
||||
throw new InvalidOperationException("Subscription locked.");
|
||||
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
compSubs.Remove(eventType);
|
||||
}
|
||||
|
||||
private void AddEntity(EntityUid euid)
|
||||
{
|
||||
// odds are at least 1 component will subscribe to an event on the entity, so just
|
||||
// preallocate the table now. Dispatch does not need to check this later.
|
||||
_eventTables.Add(euid, new Dictionary<Type, HashSet<Type>>());
|
||||
}
|
||||
|
||||
private void RemoveEntity(EntityUid euid)
|
||||
{
|
||||
_eventTables.Remove(euid);
|
||||
}
|
||||
|
||||
private void AddComponent(EntityUid euid, Type compType)
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if(!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
{
|
||||
subscribedComps = new HashSet<Type>();
|
||||
eventTable.Add(kvSub.Key, subscribedComps);
|
||||
}
|
||||
|
||||
subscribedComps.Add(compType);
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveComponent(EntityUid euid, Type compType)
|
||||
{
|
||||
var eventTable = _eventTables[euid];
|
||||
|
||||
if (!_subscriptions.TryGetValue(compType, out var compSubs))
|
||||
return;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
{
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
return;
|
||||
|
||||
subscribedComps.Remove(compType);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispatch(EntityUid euid, Type eventType, EntityEventArgs args)
|
||||
{
|
||||
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 handler))
|
||||
return;
|
||||
|
||||
var component = _entMan.ComponentManager.GetComponent(euid, compType);
|
||||
handler(euid, component, args);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearEntities()
|
||||
{
|
||||
_eventTables = new();
|
||||
_subscriptionLock = false;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
ClearEntities();
|
||||
_subscriptions = new();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_entMan.EntityAdded -= OnEntityAdded;
|
||||
_entMan.EntityDeleted -= OnEntityDeleted;
|
||||
|
||||
_entMan.ComponentManager.ComponentAdded -= OnComponentAdded;
|
||||
_entMan.ComponentManager.ComponentRemoved -= OnComponentRemoved;
|
||||
|
||||
// punishment for use-after-free
|
||||
_entMan = null!;
|
||||
_eventTables = null!;
|
||||
_subscriptions = null!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ClearEventTables()
|
||||
{
|
||||
_eventTables.ClearEntities();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_eventTables.Dispose();
|
||||
_eventTables = null!;
|
||||
_entMan = null!;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void ComponentEventHandler<in TComp, in TEvent>(EntityUid uid, TComp component, TEvent args)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs;
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -11,7 +11,16 @@ namespace Robust.Shared.GameObjects
|
||||
public delegate void EntitySessionEventHandler<in T>(T msg, EntitySessionEventArgs args);
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class EntityEventArgs : EventArgs { }
|
||||
public abstract class EntityEventArgs { }
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public abstract class HandledEntityEventArgs : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// If this message has already been "handled" by a previous system.
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
}
|
||||
|
||||
public readonly struct EntitySessionEventArgs
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Prometheus;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
@@ -47,15 +48,28 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
protected readonly List<Entity> AllEntities = new();
|
||||
|
||||
private readonly EntityEventBus _eventBus = new();
|
||||
private EntityEventBus _eventBus = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEventBus EventBus => _eventBus;
|
||||
|
||||
public event EventHandler<EntityUid>? EntityAdded;
|
||||
public event EventHandler<EntityUid>? EntityInitialized;
|
||||
public event EventHandler<EntityUid>? EntityDeleted;
|
||||
|
||||
public bool Started { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of <see cref="EntityManager"/>.
|
||||
/// </summary>
|
||||
public EntityManager()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void Initialize()
|
||||
{
|
||||
_eventBus = new EntityEventBus(this);
|
||||
|
||||
EntityNetworkManager.SetupNetworking();
|
||||
EntityNetworkManager.ReceivedComponentMessage += (sender, compMsg) => DispatchComponentMessage(compMsg);
|
||||
EntityNetworkManager.ReceivedSystemMessage += (sender, systemMsg) => EventBus.RaiseEvent(EventSource.Network, systemMsg);
|
||||
@@ -71,6 +85,7 @@ namespace Robust.Shared.GameObjects
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
FlushEntities();
|
||||
_eventBus.ClearEventTables();
|
||||
EntitySystemManager.Shutdown();
|
||||
Started = false;
|
||||
_componentManager.Clear();
|
||||
@@ -215,9 +230,43 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="e">Entity to remove</param>
|
||||
public virtual void DeleteEntity(IEntity e)
|
||||
{
|
||||
e.Shutdown();
|
||||
// Networking blindly spams entities at this function, they can already be
|
||||
// deleted from being a child of a previously deleted entity
|
||||
// TODO: Why does networking need to send deletes for child entities?
|
||||
if (e.Deleted)
|
||||
return;
|
||||
|
||||
RecursiveDeleteEntity(e);
|
||||
}
|
||||
|
||||
private void RecursiveDeleteEntity(IEntity entity)
|
||||
{
|
||||
if(entity.Deleted) //TODO: Why was this still a child if it was already deleted?
|
||||
return;
|
||||
|
||||
var transform = entity.Transform;
|
||||
|
||||
// DeleteEntity modifies our _children collection, we must cache the collection to iterate properly
|
||||
foreach (var childTransform in transform.Children.ToArray())
|
||||
{
|
||||
// Recursion Alert
|
||||
RecursiveDeleteEntity(childTransform.Owner);
|
||||
}
|
||||
|
||||
// Dispose all my components, in a safe order so transform is available
|
||||
ComponentManager.DisposeComponents(entity.Uid);
|
||||
|
||||
// map does not have a parent node, everything else needs to be detached
|
||||
if (transform.ParentUid != EntityUid.Invalid)
|
||||
{
|
||||
// Detach from my parent, if any
|
||||
transform.DetachParentToNull();
|
||||
}
|
||||
|
||||
entity.Shutdown();
|
||||
EntityDeleted?.Invoke(this, entity.Uid);
|
||||
}
|
||||
|
||||
public void DeleteEntity(EntityUid uid)
|
||||
{
|
||||
if (TryGetEntity(uid, out var entity))
|
||||
@@ -239,7 +288,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
foreach (var e in GetEntities())
|
||||
{
|
||||
e.Shutdown();
|
||||
DeleteEntity(e);
|
||||
}
|
||||
|
||||
CullDeletedEntities();
|
||||
@@ -281,6 +330,9 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
var entity = new Entity(this, uid.Value);
|
||||
|
||||
// we want this called before adding components
|
||||
EntityAdded?.Invoke(this, entity.Uid);
|
||||
|
||||
// allocate the required MetaDataComponent
|
||||
_componentManager.AddComponent<MetaDataComponent>(entity);
|
||||
|
||||
@@ -326,6 +378,7 @@ namespace Robust.Shared.GameObjects
|
||||
try
|
||||
{
|
||||
InitializeEntity(entity);
|
||||
EntityInitialized?.Invoke(this, entity.Uid);
|
||||
StartEntity(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -367,7 +420,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Entity Management
|
||||
#endregion Entity Management
|
||||
|
||||
private void DispatchComponentMessage(NetworkComponentMessage netMsg)
|
||||
{
|
||||
@@ -393,7 +446,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
protected abstract EntityUid GenerateEntityUid();
|
||||
|
||||
#region Spatial Queries
|
||||
#region Spatial Queries
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AnyEntitiesIntersecting(MapId mapId, Box2 box, bool approximate = false)
|
||||
@@ -562,10 +615,10 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
|
||||
#region Entity DynamicTree
|
||||
#region Entity DynamicTree
|
||||
|
||||
private readonly Dictionary<MapId, DynamicTree<IEntity>> _entityTreesPerMap =
|
||||
new();
|
||||
@@ -651,7 +704,7 @@ namespace Robust.Shared.GameObjects
|
||||
return new Box2(pos, pos);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
public virtual void Update()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -96,27 +96,47 @@ namespace Robust.Shared.GameObjects
|
||||
EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
|
||||
}
|
||||
|
||||
protected void QueueLocalEvent(EntitySystemMessage message)
|
||||
protected void QueueLocalEvent(EntityEventArgs message)
|
||||
{
|
||||
EntityManager.EventBus.QueueEvent(EventSource.Local, message);
|
||||
}
|
||||
|
||||
protected void RaiseNetworkEvent(EntitySystemMessage message)
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message)
|
||||
{
|
||||
EntityNetworkManager.SendSystemNetworkMessage(message);
|
||||
}
|
||||
|
||||
protected void RaiseNetworkEvent(EntitySystemMessage message, INetChannel channel)
|
||||
protected void RaiseNetworkEvent(EntityEventArgs message, INetChannel channel)
|
||||
{
|
||||
EntityNetworkManager.SendSystemNetworkMessage(message, channel);
|
||||
}
|
||||
|
||||
protected Task<T> AwaitNetworkEvent<T>(CancellationToken cancellationToken)
|
||||
where T : EntitySystemMessage
|
||||
where T : EntityEventArgs
|
||||
{
|
||||
return EntityManager.EventBus.AwaitEvent<T>(EventSource.Network, cancellationToken);
|
||||
}
|
||||
|
||||
protected void SubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
EntityManager.EventBus.SubscribeLocalEvent(handler);
|
||||
}
|
||||
|
||||
protected void UnsubscribeLocalEvent<TComp, TEvent>(ComponentEventHandler<TComp, TEvent> handler)
|
||||
where TComp : IComponent
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
EntityManager.EventBus.UnsubscribeLocalEvent(handler);
|
||||
}
|
||||
|
||||
protected void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = true)
|
||||
where TEvent : EntityEventArgs
|
||||
{
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, args, broadcast);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Helpers
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class EntitySystemMessage : EntityEventArgs
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Shared.GameObjects
|
||||
// Sending bus names and file names as strings is expensive and can be optimized.
|
||||
// Also there's redundant fields in AudioParams in most cases.
|
||||
[Serializable, NetSerializable]
|
||||
public abstract class AudioMessage : EntitySystemMessage
|
||||
public abstract class AudioMessage : EntityEventArgs
|
||||
{
|
||||
public uint Identifier { get; set; }
|
||||
public string FileName { get; set; }
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class StopAudioMessageClient : EntitySystemMessage
|
||||
public class StopAudioMessageClient : EntityEventArgs
|
||||
{
|
||||
public uint Identifier {get; set;}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.Serialization;
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class EffectSystemMessage : EntitySystemMessage
|
||||
public class EffectSystemMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to the texture used for the effect.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Raised when an entity parent is changed.
|
||||
/// </summary>
|
||||
public class EntParentChangedMessage : EntitySystemMessage
|
||||
public class EntParentChangedMessage : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Entity that was adopted. The transform component has a property with the new parent.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
public sealed class EntityInitializedMessage : EntitySystemMessage
|
||||
public sealed class EntityInitializedMessage : EntityEventArgs
|
||||
{
|
||||
public IEntity Entity { get; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
internal sealed class TransformStartLerpMessage : EntitySystemMessage
|
||||
internal sealed class TransformStartLerpMessage : EntityEventArgs
|
||||
{
|
||||
public TransformStartLerpMessage(TransformComponent transform)
|
||||
{
|
||||
|
||||
@@ -49,6 +49,10 @@ namespace Robust.Shared.GameObjects
|
||||
/// <seealso cref="IComponent" />
|
||||
public interface IComponentFactory
|
||||
{
|
||||
event Action<IComponentRegistration> ComponentAdded;
|
||||
event Action<(IComponentRegistration, Type)> ComponentReferenceAdded;
|
||||
event Action<string> ComponentIgnoreAdded;
|
||||
|
||||
/// <summary>
|
||||
/// All IComponent types that are currently registered to this factory.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Holds a collection of ECS components that are attached to entities.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public interface IComponentManager
|
||||
public interface IComponentManager : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// A component was added to the manager.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Prometheus;
|
||||
using Robust.Shared.Map;
|
||||
@@ -31,6 +32,10 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
#region Entity Management
|
||||
|
||||
event EventHandler<EntityUid>? EntityAdded;
|
||||
event EventHandler<EntityUid>? EntityInitialized;
|
||||
event EventHandler<EntityUid>? EntityDeleted;
|
||||
|
||||
IEntity CreateEntityUninitialized(string? prototypeName);
|
||||
|
||||
IEntity CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates);
|
||||
|
||||
@@ -44,9 +44,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// Server: Use the alternative overload to send to a single client.
|
||||
/// </summary>
|
||||
/// <param name="message">Message that should be sent.</param>
|
||||
void SendSystemNetworkMessage(EntitySystemMessage message);
|
||||
void SendSystemNetworkMessage(EntityEventArgs message);
|
||||
|
||||
void SendSystemNetworkMessage(EntitySystemMessage message, uint sequence)
|
||||
void SendSystemNetworkMessage(EntityEventArgs message, uint sequence)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
@@ -60,7 +60,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// Thrown if called on the client.
|
||||
/// </exception>
|
||||
void SendSystemNetworkMessage(EntitySystemMessage message, INetChannel channel);
|
||||
void SendSystemNetworkMessage(EntityEventArgs message, INetChannel channel);
|
||||
|
||||
/// <summary>
|
||||
/// Sends out queued messages based on current tick.
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Robust.Shared.GameObjects
|
||||
grid.GridEntityId != entity.Uid)
|
||||
{
|
||||
// Also this may deparent if 2 entities are parented but not using containers so fix that
|
||||
if (grid.GridEntityId != transform.ParentUid)
|
||||
if (grid.Index != transform.GridID)
|
||||
{
|
||||
transform.AttachParent(EntityManager.GetEntity(grid.GridEntityId));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
{
|
||||
@@ -31,7 +31,6 @@ namespace Robust.Shared.GameObjects
|
||||
continue;
|
||||
|
||||
RaiseLocalEvent(ev);
|
||||
ev.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Robust.Shared.Input
|
||||
/// Abstract class that all Input Commands derive from.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public abstract class InputCmdMessage : EntitySystemMessage, IComparable<InputCmdMessage>
|
||||
public abstract class InputCmdMessage : EntityEventArgs, IComparable<InputCmdMessage>
|
||||
{
|
||||
/// <summary>
|
||||
/// Client tick this was created.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -253,7 +253,11 @@ namespace Robust.Shared.Map
|
||||
// remove the existing client entity.
|
||||
var cEntity = _entityManager.GetEntity(grid.GridEntityId);
|
||||
var cGridComp = cEntity.GetComponent<IMapGridComponent>();
|
||||
cGridComp.ClearGridId();
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
@@ -86,6 +86,8 @@ namespace Robust.Shared.Map
|
||||
|
||||
Logger.DebugS("map", "Starting...");
|
||||
|
||||
_entityManager.ComponentManager.ComponentRemoved += OnComponentRemoved;
|
||||
|
||||
if (!_maps.Contains(MapId.Nullspace))
|
||||
{
|
||||
CreateMap(MapId.Nullspace);
|
||||
@@ -104,6 +106,24 @@ namespace Robust.Shared.Map
|
||||
DebugTools.Assert(!GridExists(GridId.Invalid));
|
||||
}
|
||||
|
||||
private void OnComponentRemoved(object? sender, ComponentEventArgs e)
|
||||
{
|
||||
if(e.Component is not IMapGridComponent)
|
||||
return;
|
||||
|
||||
var gridComp = (IMapGridComponent)e.Component;
|
||||
|
||||
var gridIndex = gridComp.GridIndex;
|
||||
if (gridIndex != GridId.Invalid)
|
||||
{
|
||||
if (GridExists(gridIndex))
|
||||
{
|
||||
Logger.DebugS("map", $"Entity {e.OwnerUid} removed grid component, removing bound grid {gridIndex}");
|
||||
DeleteGrid(gridIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Shutdown()
|
||||
{
|
||||
@@ -112,6 +132,8 @@ namespace Robust.Shared.Map
|
||||
#endif
|
||||
Logger.DebugS("map", "Stopping...");
|
||||
|
||||
_entityManager.ComponentManager.ComponentRemoved -= OnComponentRemoved;
|
||||
|
||||
foreach (var map in _maps.ToArray())
|
||||
{
|
||||
if (map != MapId.Nullspace)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Robust.Shared.Network.Messages
|
||||
|
||||
public EntityMessageType Type { get; set; }
|
||||
|
||||
public EntitySystemMessage SystemMessage { get; set; }
|
||||
public EntityEventArgs SystemMessage { get; set; }
|
||||
public ComponentMessage ComponentMessage { get; set; }
|
||||
|
||||
public EntityUid EntityUid { get; set; }
|
||||
@@ -43,7 +43,7 @@ namespace Robust.Shared.Network.Messages
|
||||
var serializer = IoCManager.Resolve<IRobustSerializer>();
|
||||
int length = buffer.ReadVariableInt32();
|
||||
using var stream = buffer.ReadAlignedMemory(length);
|
||||
SystemMessage = serializer.Deserialize<EntitySystemMessage>(stream);
|
||||
SystemMessage = serializer.Deserialize<EntityEventArgs>(stream);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Physics
|
||||
{
|
||||
public sealed class DebugDrawRayMessage : EntitySystemMessage
|
||||
public sealed class DebugDrawRayMessage : EntityEventArgs
|
||||
{
|
||||
public DebugRayData Data { get; }
|
||||
|
||||
|
||||
@@ -531,11 +531,13 @@ namespace Robust.Shared.Physics.Dynamics
|
||||
var body = _stack[--stackCount];
|
||||
_island.Add(body);
|
||||
_islandSet.Add(body);
|
||||
body.Awake = true;
|
||||
|
||||
// Static bodies don't propagate islands
|
||||
if (body.BodyType == BodyType.Static) continue;
|
||||
|
||||
// As static bodies can never be awake (unlike Farseer) we'll set this after the check.
|
||||
body.Awake = true;
|
||||
|
||||
for (var contactEdge = body.ContactEdges; contactEdge != null; contactEdge = contactEdge.Next)
|
||||
{
|
||||
var contact = contactEdge.Contact!;
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Physics
|
||||
{
|
||||
internal class IslandSolveMessage : EntitySystemMessage
|
||||
internal class IslandSolveMessage : EntityEventArgs
|
||||
{
|
||||
public List<IPhysBody> Bodies { get; }
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.GameObjects;
|
||||
namespace Robust.Shared.Physics
|
||||
{
|
||||
// Real pros use the system messages
|
||||
public sealed class PhysicsWakeMessage : EntitySystemMessage
|
||||
public sealed class PhysicsWakeMessage : EntityEventArgs
|
||||
{
|
||||
public PhysicsComponent Body { get; }
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Shared.Physics
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PhysicsSleepMessage : EntitySystemMessage
|
||||
public sealed class PhysicsSleepMessage : EntityEventArgs
|
||||
{
|
||||
public PhysicsComponent Body { get; }
|
||||
|
||||
|
||||
23
Robust.Shared/ProfileOptSetup.cs
Normal file
23
Robust.Shared/ProfileOptSetup.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Runtime;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
|
||||
namespace Robust.Shared
|
||||
{
|
||||
internal static class ProfileOptSetup
|
||||
{
|
||||
public static void Setup(IConfigurationManager cfg)
|
||||
{
|
||||
// Disabled on non-release since I don't want this to start creating files in Steam's bin directory.
|
||||
#if FULL_RELEASE
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (!cfg.GetCVar(CVars.SysProfileOpt))
|
||||
return;
|
||||
|
||||
ProfileOptimization.SetProfileRoot(PathHelpers.GetExecutableDirectory());
|
||||
ProfileOptimization.StartProfile("profile_opt");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,7 +244,7 @@ namespace Robust.Shared.Prototypes
|
||||
var component = (Component) factory.GetComponent(name);
|
||||
CurrentDeserializingComponent = name;
|
||||
component.Owner = entity;
|
||||
componentDependencyManager.OnComponentAdd(entity, component);
|
||||
componentDependencyManager.OnComponentAdd(entity.Uid, component);
|
||||
entity.AddComponent(component);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,12 +53,8 @@ namespace Robust.UnitTesting.Client.GameObjects.Components
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var compMan = IoCManager.Resolve<IComponentManager>();
|
||||
compMan.Initialize();
|
||||
EntityManager = IoCManager.Resolve<IClientEntityManager>();
|
||||
MapManager = IoCManager.Resolve<IMapManager>();
|
||||
MapManager.Initialize();
|
||||
MapManager.Startup();
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
var manager = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -64,6 +65,18 @@ namespace Robust.UnitTesting
|
||||
configurationManager.LoadCVarsFromAssembly(assembly);
|
||||
}
|
||||
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if(entMan.EventBus == null)
|
||||
{
|
||||
entMan.Initialize();
|
||||
entMan.Startup();
|
||||
}
|
||||
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
mapMan.Initialize();
|
||||
mapMan.Startup();
|
||||
|
||||
IoCManager.Resolve<IReflectionManager>().LoadAssemblies(assemblies);
|
||||
|
||||
var modLoader = IoCManager.Resolve<TestingModLoader>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
@@ -62,14 +62,8 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var compMan = IoCManager.Resolve<IComponentManager>();
|
||||
compMan.Initialize();
|
||||
|
||||
EntityManager = IoCManager.Resolve<IServerEntityManagerInternal>();
|
||||
MapManager = IoCManager.Resolve<IMapManager>();
|
||||
MapManager.Initialize();
|
||||
MapManager.Startup();
|
||||
|
||||
MapManager.CreateMap();
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
@@ -41,14 +41,9 @@ namespace Robust.UnitTesting.Server.GameObjects
|
||||
_componentFactory.Register<ThrowsInAddComponent>();
|
||||
_componentFactory.Register<ThrowsInInitializeComponent>();
|
||||
_componentFactory.Register<ThrowsInStartupComponent>();
|
||||
|
||||
var compMan = IoCManager.Resolve<IComponentManager>();
|
||||
compMan.Initialize();
|
||||
|
||||
|
||||
EntityManager = IoCManager.Resolve<IServerEntityManager>();
|
||||
MapManager = IoCManager.Resolve<IMapManager>();
|
||||
MapManager.Initialize();
|
||||
MapManager.Startup();
|
||||
|
||||
MapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
|
||||
@@ -84,9 +84,6 @@ entities:
|
||||
{
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
compFactory.Register<MapDeserializeTestComponent>();
|
||||
|
||||
IoCManager.Resolve<IComponentManager>().Initialize();
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
|
||||
@@ -95,10 +92,6 @@ entities:
|
||||
resourceManager.MountString("/Prototypes/TestMapEntity.yml", Prototype);
|
||||
|
||||
IoCManager.Resolve<IPrototypeManager>().LoadDirectory(new ResourcePath("/Prototypes"));
|
||||
|
||||
var map = IoCManager.Resolve<IMapManager>();
|
||||
map.Initialize();
|
||||
map.Startup();
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -163,10 +163,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
componentFactory.Register<TestFiveComponent>();
|
||||
componentFactory.Register<TestSixComponent>();
|
||||
componentFactory.Register<TestSevenComponent>();
|
||||
|
||||
var componentManager = IoCManager.Resolve<IComponentManager>();
|
||||
componentManager.Initialize();
|
||||
|
||||
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
prototypeManager.LoadFromStream(new StringReader(PROTOTYPES));
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.GameObjects
|
||||
{
|
||||
public partial class EntityEventBusTests
|
||||
{
|
||||
[Test]
|
||||
public void SubscribeCompEvent()
|
||||
{
|
||||
// Arrange
|
||||
var entUid = new EntityUid(7);
|
||||
var compInstance = new MetaDataComponent();
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
|
||||
|
||||
var compManMock = new Mock<IComponentManager>();
|
||||
|
||||
IComponent? outIComponent = compInstance;
|
||||
compManMock.Setup(m => m.TryGetComponent(entUid, typeof(MetaDataComponent), out outIComponent))
|
||||
.Returns(true);
|
||||
|
||||
compManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent)))
|
||||
.Returns(compInstance);
|
||||
|
||||
entManMock.Setup(m => m.ComponentManager).Returns(compManMock.Object);
|
||||
var bus = new EntityEventBus(entManMock.Object);
|
||||
|
||||
// Subscribe
|
||||
int calledCount = 0;
|
||||
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
|
||||
|
||||
// add a component to the system
|
||||
entManMock.Raise(m=>m.EntityAdded += null, entManMock.Object, entUid);
|
||||
compManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(compInstance, entUid));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
bus.RaiseLocalEvent(entUid, evntArgs, true);
|
||||
|
||||
// Assert
|
||||
Assert.That(calledCount, Is.EqualTo(1));
|
||||
void HandleTestEvent(EntityUid uid, MetaDataComponent component, TestEvent args)
|
||||
{
|
||||
calledCount++;
|
||||
Assert.That(uid, Is.EqualTo(entUid));
|
||||
Assert.That(component, Is.EqualTo(compInstance));
|
||||
Assert.That(args.TestNumber, Is.EqualTo(5));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UnsubscribeCompEvent()
|
||||
{
|
||||
// Arrange
|
||||
var entUid = new EntityUid(7);
|
||||
var compInstance = new MetaDataComponent();
|
||||
|
||||
var entManMock = new Mock<IEntityManager>();
|
||||
|
||||
|
||||
var compManMock = new Mock<IComponentManager>();
|
||||
|
||||
IComponent? outIComponent = compInstance;
|
||||
compManMock.Setup(m => m.TryGetComponent(entUid, typeof(MetaDataComponent), out outIComponent))
|
||||
.Returns(true);
|
||||
|
||||
compManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent)))
|
||||
.Returns(compInstance);
|
||||
|
||||
entManMock.Setup(m => m.ComponentManager).Returns(compManMock.Object);
|
||||
var bus = new EntityEventBus(entManMock.Object);
|
||||
|
||||
// Subscribe
|
||||
int calledCount = 0;
|
||||
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
|
||||
bus.UnsubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
|
||||
|
||||
// add a component to the system
|
||||
entManMock.Raise(m => m.EntityAdded += null, entManMock.Object, entUid);
|
||||
compManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(compInstance, entUid));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
bus.RaiseLocalEvent(entUid, evntArgs, true);
|
||||
|
||||
// Assert
|
||||
Assert.That(calledCount, Is.EqualTo(0));
|
||||
void HandleTestEvent(EntityUid uid, MetaDataComponent component, TestEvent args)
|
||||
{
|
||||
calledCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class DummyComponent : Component
|
||||
{
|
||||
public override string Name => "Dummy";
|
||||
}
|
||||
|
||||
private class TestEvent : EntityEventArgs
|
||||
{
|
||||
public int TestNumber { get; }
|
||||
|
||||
public TestEvent(int testNumber)
|
||||
{
|
||||
TestNumber = testNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user