mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
31 Commits
v248.0.2-s
...
v250.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0011f48023 | ||
|
|
0e609dc22d | ||
|
|
4c37977eaa | ||
|
|
7023306c09 | ||
|
|
5a5f238d9a | ||
|
|
089224cd44 | ||
|
|
9f807f1ad2 | ||
|
|
4be95ea375 | ||
|
|
03010bf4be | ||
|
|
dacaa974d4 | ||
|
|
9f73e0398a | ||
|
|
ccc383b1bf | ||
|
|
ceb59402a1 | ||
|
|
5a6b29fcd2 | ||
|
|
6b87cd1e1c | ||
|
|
cf2d6a1dbf | ||
|
|
f8c838f425 | ||
|
|
7405904041 | ||
|
|
2eeebab275 | ||
|
|
2856bb3626 | ||
|
|
be0189748b | ||
|
|
4529a7569a | ||
|
|
2b16e4db96 | ||
|
|
64f2245194 | ||
|
|
1029047e2f | ||
|
|
45dc9ad80e | ||
|
|
54ad808eea | ||
|
|
37c75df6a2 | ||
|
|
e93c1fae61 | ||
|
|
cd0a35f542 | ||
|
|
80f2dc6dd3 |
@@ -57,7 +57,7 @@
|
||||
<PackageVersion Include="SharpZstd.Interop" Version="1.5.2-beta2" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageVersion Include="SpaceWizards.HttpListener" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.1.1" />
|
||||
<PackageVersion Include="SpaceWizards.NFluidsynth" Version="0.2.2" />
|
||||
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.0.2" />
|
||||
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
|
||||
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.1" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,67 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 250.0.2
|
||||
|
||||
|
||||
## 250.0.1
|
||||
|
||||
|
||||
## 250.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The default shader now interprets negative color modulation as a flag that indicates that the light map should be ignored.
|
||||
* This can be used to avoid having to change the light map texture, thus reducing draw batches.
|
||||
* Sprite layers that are set to use the "unshaded" shader prototype now use this.
|
||||
* Any fragment shaders that previously the `VtxModulate` colour modulation variable should instead use the new `MODULATE` variable, as the former may now contain negative values.
|
||||
|
||||
### New features
|
||||
|
||||
* Add OtherBody API to contacts.
|
||||
* Make FormattedMessages Equatable.
|
||||
* AnimationCompletionEvent now has the AnimationPlayerComponent.
|
||||
* Add entity description as a tooltip on the entity spawn panel.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix serialization source generator breaking if a class has two partial locations.
|
||||
* Fix map saving throwing a `DirectoryNotFoundException` when given a path with a non-existent directory. Now it once again creates any missing directories.
|
||||
* Fix map loading taking a significant time due to MappingDataNode.Equals calls being slow.
|
||||
|
||||
### Other
|
||||
|
||||
* Add Pure to some Angle methods.
|
||||
|
||||
### Internal
|
||||
|
||||
* Cleanup some warnings in classes.
|
||||
|
||||
|
||||
## 249.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Layer is now read-only on VisibilityComponent and isn't serialized.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a debug overlay for the linear and angular velocity of all entities on the screen. Use the `showvel` and `showangvel` commands to toggle it.
|
||||
* Add a GetWorldManifold overload that doesn't require a span of points.
|
||||
* Added a GetVisMaskEvent. Calling `RefreshVisibilityMask` will raise it and subscribers can update the vismask via the event rather than subscribers having to each manually try and handle the vismask directly.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* `BoxContainer` no longer causes stretching children to go below their minimum size.
|
||||
* Fix lights on other grids getting clipped due to ignoring the light range cvar.
|
||||
* Fix the `showvelocities` command.
|
||||
* Fix the DirtyFields overload not being sandbox safe for content.
|
||||
|
||||
### Internal
|
||||
|
||||
* Polygon vertices are now inlined with FixedArray8 and a separate SlimPolygon using FixedArray4 for hot paths rather than using pooled arrays.
|
||||
|
||||
|
||||
## 248.0.2
|
||||
|
||||
### Bugfixes
|
||||
|
||||
@@ -428,11 +428,20 @@ cmd-entfo-help = Usage: entfo <entityuid>
|
||||
The entity UID can be prefixed with 'c' to convert it to a client entity UID.
|
||||
|
||||
cmd-fuck-desc = Throws an exception
|
||||
cmd-fuck-help = Throws an exception
|
||||
cmd-fuck-help = Usage: fuck
|
||||
|
||||
cmd-showpos-desc = Enables debug drawing over all entity positions in the game.
|
||||
cmd-showpos-desc = Show the position of all entities on the screen.
|
||||
cmd-showpos-help = Usage: showpos
|
||||
|
||||
cmd-showrot-desc = Show the rotation of all entities on the screen.
|
||||
cmd-showrot-help = Usage: showrot
|
||||
|
||||
cmd-showvel-desc = Show the local velocity of all entites on the screen.
|
||||
cmd-showvel-help = Usage: showvel
|
||||
|
||||
cmd-showangvel-desc = Show the angular velocity of all entities on the screen.
|
||||
cmd-showangvel-help = Usage: showangvel
|
||||
|
||||
cmd-sggcell-desc = Lists entities on a snap grid cell.
|
||||
cmd-sggcell-help = Usage: sggcell <gridID> <vector2i>\nThat vector2i param is in the form x<int>,y<int>.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ using Xilium.CefGlue;
|
||||
|
||||
namespace Robust.Client.WebView.Cef
|
||||
{
|
||||
public static class Program
|
||||
internal static class Program
|
||||
{
|
||||
// This was supposed to be the main entry for the subprocess program... It doesn't work.
|
||||
public static int Main(string[] args)
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
[Dependency] private readonly IDependencyCollection _dependencyCollection = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameControllerInternal _gameController = default!;
|
||||
[Dependency] private readonly IResourceManagerInternal _resourceManager = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
@@ -61,7 +63,10 @@ namespace Robust.Client.WebView.Cef
|
||||
|
||||
var cachePath = "";
|
||||
if (_resourceManager.UserData is WritableDirProvider userData)
|
||||
cachePath = userData.GetFullPath(new ResPath("/cef_cache"));
|
||||
{
|
||||
var rootDir = UserDataDir.GetRootUserDataDir(_gameController);
|
||||
cachePath = Path.Combine(rootDir, "cef_cache", "0");
|
||||
}
|
||||
|
||||
var settings = new CefSettings()
|
||||
{
|
||||
|
||||
@@ -173,29 +173,51 @@ namespace Robust.Client.Console.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowPositionsCommand : LocalizedCommands
|
||||
internal sealed class ShowPositionsCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
|
||||
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
|
||||
|
||||
public override string Command => "showpos";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
|
||||
mgr.DebugPositions = !mgr.DebugPositions;
|
||||
_debugDrawing.DebugPositions = !_debugDrawing.DebugPositions;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowRotationsCommand : LocalizedCommands
|
||||
internal sealed class ShowRotationsCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
|
||||
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
|
||||
|
||||
public override string Command => "showrot";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var mgr = _entitySystems.GetEntitySystem<DebugDrawingSystem>();
|
||||
mgr.DebugRotations = !mgr.DebugRotations;
|
||||
_debugDrawing.DebugRotations = !_debugDrawing.DebugRotations;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowVelocitiesCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
|
||||
|
||||
public override string Command => "showvel";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_debugDrawing.DebugVelocities = !_debugDrawing.DebugVelocities;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowAngularVelocitiesCommand : LocalizedEntityCommands
|
||||
{
|
||||
[Dependency] private readonly DebugDrawingSystem _debugDrawing = default!;
|
||||
|
||||
public override string Command => "showangvel";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_debugDrawing.DebugAngularVelocities = !_debugDrawing.DebugAngularVelocities;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,15 @@ using Robust.Shared.IoC;
|
||||
|
||||
namespace Robust.Client.Console.Commands
|
||||
{
|
||||
public sealed class VelocitiesCommand : LocalizedCommands
|
||||
public sealed class ShowPlayerVelocityCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystems = default!;
|
||||
|
||||
public override string Command => "showvelocities";
|
||||
public override string Command => "showplayervelocity";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
_entitySystems.GetEntitySystem<VelocityDebugSystem>().Enabled ^= true;
|
||||
_entitySystems.GetEntitySystem<ShowPlayerVelocityDebugSystem>().Enabled ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +1,221 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Robust.Client.Debugging
|
||||
namespace Robust.Client.Debugging;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of visual debug overlays for the client game.
|
||||
/// </summary>
|
||||
public sealed class DebugDrawingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private bool _debugPositions;
|
||||
private bool _debugRotations;
|
||||
private bool _debugVelocities;
|
||||
private bool _debugAngularVelocities;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of visual debug overlays for the client game.
|
||||
/// Toggles the visual overlay of the local origin for each entity on screen.
|
||||
/// </summary>
|
||||
public sealed class DebugDrawingSystem : EntitySystem
|
||||
public bool DebugPositions
|
||||
{
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
|
||||
|
||||
private bool _debugPositions;
|
||||
private bool _debugRotations;
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the local origin for each entity on screen.
|
||||
/// </summary>
|
||||
public bool DebugPositions
|
||||
get => _debugPositions;
|
||||
set
|
||||
{
|
||||
get => _debugPositions;
|
||||
set
|
||||
if (value == DebugPositions)
|
||||
{
|
||||
if (value == DebugPositions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_debugPositions = value;
|
||||
_debugPositions = value;
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, EntityManager, _transform));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
|
||||
}
|
||||
if (value && !_overlayManager.HasOverlay<EntityPositionOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityPositionOverlay(_lookup, _transform));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay<EntityPositionOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the local rotation.
|
||||
/// </summary>
|
||||
public bool DebugRotations
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the rotation for each entity on screen.
|
||||
/// </summary>
|
||||
public bool DebugRotations
|
||||
{
|
||||
get => _debugRotations;
|
||||
set
|
||||
{
|
||||
get => _debugRotations;
|
||||
set
|
||||
if (value == DebugRotations)
|
||||
{
|
||||
if (value == DebugRotations)
|
||||
{
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_debugRotations = value;
|
||||
_debugRotations = value;
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityRotationOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityRotationOverlay(_lookup, EntityManager));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay<EntityRotationOverlay>();
|
||||
}
|
||||
if (value && !_overlayManager.HasOverlay<EntityRotationOverlay>())
|
||||
{
|
||||
_overlayManager.AddOverlay(new EntityRotationOverlay(_lookup, _transform));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay<EntityRotationOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntityPositionOverlay : Overlay
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the local velocity for each entity on screen.
|
||||
/// </summary>
|
||||
public bool DebugVelocities
|
||||
{
|
||||
get => _debugVelocities;
|
||||
set
|
||||
{
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityPositionOverlay(EntityLookupSystem lookup, IEntityManager entityManager, SharedTransformSystem transform)
|
||||
if (value == DebugVelocities)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_entityManager = entityManager;
|
||||
_transform = transform;
|
||||
return;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
_debugVelocities = value;
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityVelocityOverlay>())
|
||||
{
|
||||
const float stubLength = 0.25f;
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
|
||||
{
|
||||
var (center, worldRotation) = _transform.GetWorldPositionRotation(entity);
|
||||
|
||||
var xLine = worldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = worldRotation.RotateVec(Vector2.UnitY);
|
||||
|
||||
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
|
||||
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
|
||||
}
|
||||
_overlayManager.AddOverlay(new EntityVelocityOverlay(EntityManager, _lookup, _transform));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay<EntityVelocityOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntityRotationOverlay : Overlay
|
||||
/// <summary>
|
||||
/// Toggles the visual overlay of the angular velocity for each entity on screen.
|
||||
/// </summary>
|
||||
public bool DebugAngularVelocities
|
||||
{
|
||||
get => _debugAngularVelocities;
|
||||
set
|
||||
{
|
||||
private readonly EntityLookupSystem _lookup;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
public EntityRotationOverlay(EntityLookupSystem lookup, IEntityManager entityManager)
|
||||
if (value == DebugAngularVelocities)
|
||||
{
|
||||
_lookup = lookup;
|
||||
_entityManager = entityManager;
|
||||
return;
|
||||
}
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
_debugAngularVelocities = value;
|
||||
|
||||
if (value && !_overlayManager.HasOverlay<EntityAngularVelocityOverlay>())
|
||||
{
|
||||
const float stubLength = 0.25f;
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
_overlayManager.AddOverlay(new EntityAngularVelocityOverlay(EntityManager, _lookup, _transform));
|
||||
}
|
||||
else
|
||||
{
|
||||
_overlayManager.RemoveOverlay<EntityAngularVelocityOverlay>();
|
||||
}
|
||||
}
|
||||
}
|
||||
private sealed class EntityPositionOverlay(EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
|
||||
{
|
||||
var (center, worldRotation) = xformQuery.GetComponent(entity).GetWorldPositionRotation();
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
const float stubLength = 0.25f;
|
||||
|
||||
var drawLine = worldRotation.RotateVec(-Vector2.UnitY);
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
|
||||
worldHandle.DrawLine(center, center + drawLine * stubLength, Color.Red);
|
||||
}
|
||||
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
|
||||
{
|
||||
var (center, worldRotation) = _transform.GetWorldPositionRotation(uid);
|
||||
|
||||
var xLine = worldRotation.RotateVec(Vector2.UnitX);
|
||||
var yLine = worldRotation.RotateVec(Vector2.UnitY);
|
||||
|
||||
worldHandle.DrawLine(center, center + xLine * stubLength, Color.Red);
|
||||
worldHandle.DrawLine(center, center + yLine * stubLength, Color.Green);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntityRotationOverlay(EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
const float stubLength = 0.25f;
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
|
||||
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
|
||||
{
|
||||
var (center, worldRotation) = _transform.GetWorldPositionRotation(uid);
|
||||
|
||||
var drawLine = worldRotation.RotateVec(-Vector2.UnitY);
|
||||
|
||||
worldHandle.DrawLine(center, center + drawLine * stubLength, Color.Red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntityVelocityOverlay(IEntityManager _entityManager, EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
const float multiplier = 0.2f;
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
|
||||
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
|
||||
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
|
||||
{
|
||||
if(!physicsQuery.TryGetComponent(uid, out var physicsComp))
|
||||
continue;
|
||||
|
||||
var center = _transform.GetWorldPosition(uid);
|
||||
var localVelocity = physicsComp.LinearVelocity;
|
||||
|
||||
if (localVelocity != Vector2.Zero)
|
||||
worldHandle.DrawLine(center, center + localVelocity * multiplier, Color.Yellow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class EntityAngularVelocityOverlay(IEntityManager _entityManager, EntityLookupSystem _lookup, SharedTransformSystem _transform) : Overlay
|
||||
{
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpace;
|
||||
|
||||
protected internal override void Draw(in OverlayDrawArgs args)
|
||||
{
|
||||
const float multiplier = (float)(0.2 / (2 * System.Math.PI));
|
||||
|
||||
var worldHandle = (DrawingHandleWorld) args.DrawingHandle;
|
||||
|
||||
var physicsQuery = _entityManager.GetEntityQuery<PhysicsComponent>();
|
||||
foreach (var uid in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldBounds))
|
||||
{
|
||||
if(!physicsQuery.TryGetComponent(uid, out var physicsComp))
|
||||
continue;
|
||||
|
||||
var center = _transform.GetWorldPosition(uid);
|
||||
var angularVelocity = physicsComp.AngularVelocity;
|
||||
|
||||
if (angularVelocity != 0.0f)
|
||||
worldHandle.DrawCircle(center, angularVelocity * multiplier, angularVelocity > 0 ? Color.Magenta : Color.Blue, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,7 +384,7 @@ namespace Robust.Client
|
||||
|
||||
_prof.Initialize();
|
||||
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null);
|
||||
_resManager.Initialize(Options.LoadConfigAndUserData ? userDataDir : null, hideUserDataDir: true);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions)
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Robust.Client.GameObjects
|
||||
base.DirtyField(uid, comp, fieldName, metadata);
|
||||
}
|
||||
|
||||
public override void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
|
||||
public override void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
|
||||
{
|
||||
// TODO Prediction
|
||||
// does the client actually need to dirty the field?
|
||||
|
||||
@@ -30,6 +30,7 @@ using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using static Robust.Shared.Serialization.TypeSerializers.Implementations.SpriteSpecifierSerializer;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
using SysVec4 = System.Numerics.Vector4;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -753,12 +754,20 @@ namespace Robust.Client.GameObjects
|
||||
if (layerDatum.Shader == string.Empty)
|
||||
{
|
||||
layer.ShaderPrototype = null;
|
||||
layer.UnShaded = false;
|
||||
layer.Shader = null;
|
||||
}
|
||||
else if (layerDatum.Shader == SpriteSystem.UnshadedId.Id)
|
||||
{
|
||||
layer.ShaderPrototype = SpriteSystem.UnshadedId;
|
||||
layer.UnShaded = true;
|
||||
layer.Shader = null;
|
||||
}
|
||||
else if (prototypes.TryIndex<ShaderPrototype>(layerDatum.Shader, out var prototype))
|
||||
{
|
||||
layer.ShaderPrototype = layerDatum.Shader;
|
||||
layer.Shader = prototype.Instance();
|
||||
layer.UnShaded = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -835,11 +844,28 @@ namespace Robust.Client.GameObjects
|
||||
if (!TryGetLayer(layer, out var theLayer, true))
|
||||
return;
|
||||
|
||||
if (shader == null)
|
||||
{
|
||||
theLayer.UnShaded = false;
|
||||
theLayer.Shader = null;
|
||||
theLayer.ShaderPrototype = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prototype == SpriteSystem.UnshadedId.Id)
|
||||
{
|
||||
theLayer.UnShaded = true;
|
||||
theLayer.ShaderPrototype = SpriteSystem.UnshadedId;
|
||||
theLayer.Shader = null;
|
||||
return;
|
||||
}
|
||||
|
||||
theLayer.UnShaded = false;
|
||||
theLayer.Shader = shader;
|
||||
theLayer.ShaderPrototype = prototype;
|
||||
}
|
||||
|
||||
public void LayerSetShader(object layerKey, ShaderInstance shader, string? prototype = null)
|
||||
public void LayerSetShader(object layerKey, ShaderInstance? shader, string? prototype = null)
|
||||
{
|
||||
if (!LayerMapTryGet(layerKey, out var layer, true))
|
||||
return;
|
||||
@@ -1493,10 +1519,18 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
[ViewVariables] private readonly SpriteComponent _parent;
|
||||
|
||||
[ViewVariables] public string? ShaderPrototype;
|
||||
[ViewVariables] public ProtoId<ShaderPrototype>? ShaderPrototype;
|
||||
[ViewVariables] public ShaderInstance? Shader;
|
||||
[ViewVariables] public Texture? Texture;
|
||||
|
||||
/// <summary>
|
||||
/// If true, then this layer is drawn without lighting applied.
|
||||
/// Unshaded layers are given special treatment and don't just use the unshaded-shader to avoid having to
|
||||
/// unnecessarily swap out the light texture. This helps the number of batches that need to be sent to the
|
||||
/// GPU while drawing sprites.
|
||||
/// </summary>
|
||||
[ViewVariables] internal bool UnShaded;
|
||||
|
||||
private RSI? _rsi;
|
||||
[ViewVariables] public RSI? RSI
|
||||
{
|
||||
@@ -1663,6 +1697,7 @@ namespace Robust.Client.GameObjects
|
||||
if (toClone.Shader != null)
|
||||
{
|
||||
Shader = toClone.Shader.Mutable ? toClone.Shader.Duplicate() : toClone.Shader;
|
||||
UnShaded = toClone.UnShaded;
|
||||
ShaderPrototype = toClone.ShaderPrototype;
|
||||
}
|
||||
Texture = toClone.Texture;
|
||||
@@ -2078,6 +2113,20 @@ namespace Robust.Client.GameObjects
|
||||
drawingHandle.UseShader(Shader);
|
||||
|
||||
var layerColor = _parent.color * Color;
|
||||
|
||||
DebugTools.Assert(layerColor is {R: >= 0, G: >= 0, B: >= 0, A: >= 0}, "Negative colour modulation");
|
||||
|
||||
if (UnShaded)
|
||||
{
|
||||
DebugTools.AssertNull(Shader);
|
||||
|
||||
// Negative modulation values are used to disable light shading in the default shader.
|
||||
// Specifically we set colour = -1 - colour
|
||||
// This ensures that non-negative values become negative & is trivially invertible.
|
||||
// Alternatively we could just clamp the colour to [0,1] and subtract a constant.
|
||||
layerColor = new(new SysVec4(-1) - layerColor.RGBA);
|
||||
}
|
||||
|
||||
var textureSize = texture.Size / (float)EyeManager.PixelsPerMeter;
|
||||
var quad = Box2.FromDimensions(textureSize/-2, textureSize);
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Robust.Client.GameObjects
|
||||
foreach (var key in remie)
|
||||
{
|
||||
component.PlayingAnimations.Remove(key);
|
||||
var completedEvent = new AnimationCompletedEvent {Uid = uid, Key = key, Finished = true};
|
||||
var completedEvent = new AnimationCompletedEvent(uid, component, key, true);
|
||||
EntityManager.EventBus.RaiseLocalEvent(uid, completedEvent, true);
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
var completedEvent = new AnimationCompletedEvent {Uid = entity.Owner, Key = key, Finished = false};
|
||||
var completedEvent = new AnimationCompletedEvent(entity.Owner, entity.Comp, key, false);
|
||||
EntityManager.EventBus.RaiseLocalEvent(entity.Owner, completedEvent, true);
|
||||
}
|
||||
|
||||
@@ -202,13 +202,33 @@ namespace Robust.Client.GameObjects
|
||||
/// </summary>
|
||||
public sealed class AnimationCompletedEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity associated with the event.
|
||||
/// </summary>
|
||||
public EntityUid Uid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The animation player component associated with the entity this event was raised on.
|
||||
/// </summary>
|
||||
public AnimationPlayerComponent AnimationPlayer { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// The key associated with the animation that was completed.
|
||||
/// </summary>
|
||||
public string Key { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the animation finished by getting to its natural end.
|
||||
/// If false, it was removed prematurely via <see cref="AnimationPlayerSystem.Stop(Robust.Client.GameObjects.AnimationPlayerComponent,string)"/> or similar overloads.
|
||||
/// If false, it was removed prematurely via <see cref="AnimationPlayerSystem.Stop(EntityUid,AnimationPlayerComponent,string)"/> or similar overloads.
|
||||
/// </summary>
|
||||
public bool Finished { get; init; }
|
||||
|
||||
public AnimationCompletedEvent(EntityUid uid, AnimationPlayerComponent animationPlayer, string key, bool finished = true)
|
||||
{
|
||||
Uid = uid;
|
||||
AnimationPlayer = animationPlayer;
|
||||
Key = key;
|
||||
Finished = finished;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed class ShowPlayerVelocityDebugSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
|
||||
internal bool Enabled
|
||||
{
|
||||
get => _label.Parent != null;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
_uiManager.WindowRoot.AddChild(_label);
|
||||
}
|
||||
else
|
||||
{
|
||||
_label.Orphan();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Label _label = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_label = new Label();
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
if (!Enabled)
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var player = _playerManager.LocalEntity;
|
||||
|
||||
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var screenPos = _eyeManager.WorldToScreen(_transform.GetWorldPosition(Transform(player.Value)));
|
||||
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
|
||||
_label.Visible = true;
|
||||
|
||||
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xforms = default!;
|
||||
|
||||
public static readonly ProtoId<ShaderPrototype> UnshadedId = "unshaded";
|
||||
private readonly Queue<SpriteComponent> _inertUpdateQueue = new();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
public sealed class VelocityDebugSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
|
||||
internal bool Enabled { get; set; }
|
||||
|
||||
private Label _label = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_label = new Label();
|
||||
IoCManager.Resolve<IUserInterfaceManager>().StateRoot.AddChild(_label);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
if (!Enabled)
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var player = _playerManager.LocalEntity;
|
||||
|
||||
if (player == null || !EntityManager.TryGetComponent(player.Value, out PhysicsComponent? body))
|
||||
{
|
||||
_label.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var screenPos = _eyeManager.WorldToScreen(_transform.GetWorldPosition(Transform(player.Value)));
|
||||
LayoutContainer.SetPosition(_label, screenPos + new Vector2(0, 50));
|
||||
_label.Visible = true;
|
||||
|
||||
_label.Text = $"Speed: {body.LinearVelocity.Length():0.00}\nLinear: {body.LinearVelocity.X:0.00}, {body.LinearVelocity.Y:0.00}\nAngular: {body.AngularVelocity}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,7 +384,6 @@ namespace Robust.Client.GameStates
|
||||
_processor.UpdateFullRep(curState);
|
||||
}
|
||||
|
||||
IEnumerable<NetEntity> createdEntities;
|
||||
using (_prof.Group("ApplyGameState"))
|
||||
{
|
||||
if (_timing.LastProcessedTick < targetProcessedTick && nextState != null)
|
||||
@@ -699,8 +698,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
throw new KeyNotFoundException();
|
||||
#endif
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
}
|
||||
|
||||
var compData = _compDataPool.Get();
|
||||
@@ -961,8 +961,9 @@ namespace Robust.Client.GameStates
|
||||
RequestFullState();
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
throw;
|
||||
#endif
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (data.Created)
|
||||
@@ -980,8 +981,9 @@ namespace Robust.Client.GameStates
|
||||
RequestFullState();
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
throw;
|
||||
#endif
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private const string UniModelMatrix = "modelMatrix";
|
||||
private const string UniTexturePixelSize = "TEXTURE_PIXEL_SIZE";
|
||||
private const string UniMainTexture = "TEXTURE";
|
||||
private const string UniLightTexture = "lightMap";
|
||||
private const string UniLightTexture = "lightMap"; // TODO CLYDE consistent shader variable naming
|
||||
private const string UniProjViewMatrices = "projectionViewMatrices";
|
||||
private const string UniUniformConstants = "uniformConstants";
|
||||
|
||||
|
||||
@@ -98,9 +98,11 @@ namespace Robust.Client.Graphics.Clyde
|
||||
private LightCapacityComparer _lightCap = new();
|
||||
private ShadowCapacityComparer _shadowCap = new ShadowCapacityComparer();
|
||||
|
||||
private float _maxLightRadius;
|
||||
|
||||
private unsafe void InitLighting()
|
||||
{
|
||||
|
||||
_cfg.OnValueChanged(CVars.MaxLightRadius, val => { _maxLightRadius = val;}, true);
|
||||
|
||||
// Other...
|
||||
LoadLightingShaders();
|
||||
@@ -617,8 +619,9 @@ namespace Robust.Client.Graphics.Clyde
|
||||
// Use worldbounds for this one as we only care if the light intersects our actual bounds
|
||||
var xforms = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var state = (this, count: 0, shadowCastingCount: 0, xforms, worldAABB);
|
||||
var lightAabb = worldAABB.Enlarged(_maxLightRadius);
|
||||
|
||||
foreach (var (uid, comp) in _lightTreeSystem.GetIntersectingTrees(map, worldAABB))
|
||||
foreach (var (uid, comp) in _lightTreeSystem.GetIntersectingTrees(map, lightAabb))
|
||||
{
|
||||
var bounds = _transformSystem.GetInvWorldMatrix(uid, xforms).TransformBox(worldBounds);
|
||||
comp.Tree.QueryAabb(ref state, LightQuery, bounds);
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
// UV coordinates in texture-space. I.e., (0,0) is the corner of the texture currently being used to draw.
|
||||
// When drawing a sprite from a texture atlas, (0,0) is the corner of the atlas, not the specific sprite being drawn.
|
||||
varying highp vec2 UV;
|
||||
|
||||
// UV coordinates in quad-space. I.e., when drawing a sprite from a texture atlas (0,0) is the corner of the sprite
|
||||
// currently being drawn.
|
||||
varying highp vec2 UV2;
|
||||
|
||||
// TBH I'm not sure what this is for. I think it is scree UV coordiantes, i.e., FRAGCOORD.xy * SCREEN_PIXEL_SIZE ?
|
||||
// TODO CLYDE Is this still needed?
|
||||
varying highp vec2 Pos;
|
||||
|
||||
// Vertex colour modulation. Note that negative values imply that the LIGHTMAP should be ignored. This is used to avoid
|
||||
// having to set the texture to a white/blank texture for sprites that have no light shading applied.
|
||||
varying highp vec4 VtxModulate;
|
||||
|
||||
// The current light map. Unless disabled, this is automatically sampled to create the LIGHT vector, which is then used
|
||||
// to modulate the output colour.
|
||||
// TODO CLYDE consistent shader variable naming
|
||||
uniform sampler2D lightMap;
|
||||
|
||||
// [SHADER_HEADER_CODE]
|
||||
@@ -11,11 +25,37 @@ void main()
|
||||
{
|
||||
highp vec4 FRAGCOORD = gl_FragCoord;
|
||||
|
||||
// The output colour. This should get set by the shader code block.
|
||||
// This will get modified by the LIGHT and MODULATE vectors.
|
||||
lowp vec4 COLOR;
|
||||
|
||||
lowp vec3 lightSample = texture2D(lightMap, Pos).rgb;
|
||||
// The light colour, usually sampled from the LIGHTMAP
|
||||
lowp vec4 LIGHT;
|
||||
|
||||
// Colour modulation vector.
|
||||
highp vec4 MODULATE;
|
||||
|
||||
// Sample the texture outside of the branch / with uniform control flow.
|
||||
LIGHT = texture2D(lightMap, Pos);
|
||||
|
||||
if (VtxModulate.x < 0.0)
|
||||
{
|
||||
// Negative VtxModulate implies unshaded/no lighting.
|
||||
MODULATE = -1.0 - VtxModulate;
|
||||
LIGHT = vec4(1.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
MODULATE = VtxModulate;
|
||||
}
|
||||
|
||||
// TODO CLYDE consistent shader variable naming
|
||||
// Requires breaking changes.
|
||||
lowp vec3 lightSample = LIGHT.xyz;
|
||||
|
||||
// [SHADER_CODE]
|
||||
|
||||
gl_FragColor = zAdjustResult(COLOR * VtxModulate * vec4(lightSample, 1.0));
|
||||
LIGHT.xyz = lightSample;
|
||||
|
||||
gl_FragColor = zAdjustResult(COLOR * MODULATE * LIGHT);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ uniform mat3 modelMatrix;
|
||||
// Allows us to do texture atlassing with texture coordinates 0->1
|
||||
// Input texture coordinates get mapped to this range.
|
||||
uniform vec4 modifyUV;
|
||||
// TODO CLYDE Is this still needed?
|
||||
|
||||
// [SHADER_HEADER_CODE]
|
||||
|
||||
@@ -39,5 +40,15 @@ void main()
|
||||
Pos = (VERTEX + 1.0) / 2.0;
|
||||
UV = mix(modifyUV.xy, modifyUV.zw, tCoord);
|
||||
UV2 = tCoord2;
|
||||
VtxModulate = zFromSrgb(modulate);
|
||||
|
||||
// Negative modulation is being used as a hacky way to squeeze in lighting data.
|
||||
// I.e., negative modulation implies we ignore the lighting.
|
||||
if (modulate.x < 0.0)
|
||||
{
|
||||
VtxModulate = -1.0 - zFromSrgb(-1.0 - modulate);
|
||||
}
|
||||
else
|
||||
{
|
||||
VtxModulate = zFromSrgb(modulate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
varying highp vec2 UV;
|
||||
varying highp vec2 UV2;
|
||||
|
||||
// TODO CLYDE consistent shader variable naming
|
||||
uniform sampler2D lightMap;
|
||||
|
||||
// [SHADER_HEADER_CODE]
|
||||
|
||||
@@ -16,7 +16,7 @@ using Vector4 = Robust.Shared.Maths.Vector4;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
[Prototype("shader")]
|
||||
[Prototype]
|
||||
public sealed partial class ShaderPrototype : IPrototype, ISerializationHooks
|
||||
{
|
||||
[ViewVariables]
|
||||
|
||||
@@ -71,21 +71,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
|
||||
// First, we measure non-stretching children.
|
||||
var stretching = new List<Control>();
|
||||
float totalStretchRatio = 0;
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (!child.Visible)
|
||||
continue;
|
||||
|
||||
var stretch = Vertical ? child.VerticalExpand : child.HorizontalExpand;
|
||||
if (stretch)
|
||||
{
|
||||
totalStretchRatio += child.SizeFlagsStretchRatio;
|
||||
stretching.Add(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
child.Measure(availableSize);
|
||||
|
||||
if (Vertical)
|
||||
@@ -102,35 +92,6 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
if (stretching.Count == 0)
|
||||
return desiredSize;
|
||||
|
||||
// Then we measure stretching children
|
||||
foreach (var child in stretching)
|
||||
{
|
||||
var size = availableSize;
|
||||
if (Vertical)
|
||||
{
|
||||
size.Y *= child.SizeFlagsStretchRatio / totalStretchRatio;
|
||||
child.Measure(size);
|
||||
desiredSize.Y += child.DesiredSize.Y;
|
||||
desiredSize.X = Math.Max(desiredSize.X, child.DesiredSize.X);
|
||||
}
|
||||
else
|
||||
{
|
||||
size.X *= child.SizeFlagsStretchRatio / totalStretchRatio;
|
||||
child.Measure(size);
|
||||
desiredSize.X += child.DesiredSize.X;
|
||||
desiredSize.Y = Math.Max(desiredSize.Y, child.DesiredSize.Y);
|
||||
}
|
||||
|
||||
// TODO Maybe make BoxContainer.MeasureOverride more rigorous.
|
||||
// This should check if size < desired size. If it is, treat child as non-stretching (see the code in
|
||||
// ArrangeOverride). This requires remeasuring all stretching controls + the control that just became
|
||||
// non-stretching. But the re-measured controls might then become smaller (e.g. rich text wrapping),
|
||||
// leading to a recursion problem.
|
||||
}
|
||||
|
||||
return desiredSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace Robust.Client.UserInterface.CustomControls
|
||||
}
|
||||
|
||||
button.EntityLabel.Text = entityLabelText;
|
||||
button.ActualButton.ToolTip = prototype.Description;
|
||||
|
||||
if (prototype == SelectedPrototype)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.UserInterface.RichText;
|
||||
|
||||
[Prototype("font")]
|
||||
[Prototype]
|
||||
public sealed partial class FontPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PolySharpIncludeGeneratedTypes>System.Index;System.Diagnostics.CodeAnalysis.NotNullWhenAttribute;System.Runtime.CompilerServices.IsExternalInit;System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute</PolySharpIncludeGeneratedTypes>
|
||||
<NoWarn>RS2008</NoWarn>
|
||||
<NoWarn>RS2008;RS1038</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -35,13 +35,20 @@ public class Generator : IIncrementalGenerator
|
||||
.Where(static type => type != null);
|
||||
|
||||
initContext.RegisterSourceOutput(
|
||||
dataDefinitions,
|
||||
static (sourceContext, source) =>
|
||||
dataDefinitions.Collect(),
|
||||
static (sourceContext, sources) =>
|
||||
{
|
||||
// TODO: deduplicate based on name?
|
||||
var (name, code) = source!.Value;
|
||||
var done = new HashSet<string>();
|
||||
|
||||
sourceContext.AddSource(name, code);
|
||||
foreach (var source in sources)
|
||||
{
|
||||
var (name, code) = source!.Value;
|
||||
|
||||
if (!done.Add(name))
|
||||
continue;
|
||||
|
||||
sourceContext.AddSource(name, code);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ namespace Robust.Server
|
||||
: null;
|
||||
|
||||
// Set up the VFS
|
||||
_resources.Initialize(dataDir);
|
||||
_resources.Initialize(dataDir, hideUserDataDir: false);
|
||||
|
||||
var mountOptions = _commandLineArgs != null
|
||||
? MountOptions.Merge(_commandLineArgs.MountOptions, Options.MountOptions) : Options.MountOptions;
|
||||
|
||||
15
Robust.Server/ServerStatus/StatusExt.cs
Normal file
15
Robust.Server/ServerStatus/StatusExt.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Robust.Server.ServerStatus;
|
||||
|
||||
/// <summary>
|
||||
/// Helper functions for working with <see cref="IStatusHandlerContext"/>.
|
||||
/// </summary>
|
||||
public static class StatusExt
|
||||
{
|
||||
/// <summary>
|
||||
/// Add <c>Access-Control-Allow-Origin: *</c> to the response headers for this request.
|
||||
/// </summary>
|
||||
public static void AddAllowOriginAny(this IStatusHandlerContext context)
|
||||
{
|
||||
context.ResponseHeaders.Add("Access-Control-Allow-Origin", "*");
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
OnStatusRequest?.Invoke(jObject);
|
||||
|
||||
context.AddAllowOriginAny();
|
||||
await context.RespondJsonAsync(jObject);
|
||||
|
||||
return true;
|
||||
@@ -121,6 +122,7 @@ namespace Robust.Server.ServerStatus
|
||||
|
||||
OnInfoRequest?.Invoke(jObject);
|
||||
|
||||
context.AddAllowOriginAny();
|
||||
await context.RespondJsonAsync(jObject);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace Robust.Shared.Maths
|
||||
private const double CardinalSegment = 2 * Math.PI / 4.0; // Cut the circle into 4 pieces
|
||||
private const double CardinalOffset = CardinalSegment / 2.0; // offset the pieces by 1/2 their size
|
||||
|
||||
[Pure]
|
||||
public readonly Direction GetCardinalDir()
|
||||
{
|
||||
var ang = Theta % (2 * Math.PI);
|
||||
@@ -167,6 +168,7 @@ namespace Robust.Shared.Maths
|
||||
/// <summary>
|
||||
/// Removes revolutions from a positive or negative angle to make it as small as possible.
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public readonly Angle Reduced()
|
||||
{
|
||||
return new(Reduce(Theta));
|
||||
@@ -213,11 +215,13 @@ namespace Robust.Shared.Maths
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public readonly Angle Opposite()
|
||||
{
|
||||
return new Angle(FlipPositive(Theta-Math.PI));
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public readonly Angle FlipPositive()
|
||||
{
|
||||
return new(FlipPositive(Theta));
|
||||
|
||||
@@ -682,6 +682,7 @@ namespace Robust.Shared.Maths
|
||||
/// (which is copied to the output's Alpha value).
|
||||
/// Each has a range of 0.0 to 1.0.
|
||||
/// </param>
|
||||
[Obsolete("The HCY color space is mathematically incorrect and these functions are broken, use something else")]
|
||||
public static Color FromHcy(Vector4 hcy)
|
||||
{
|
||||
var hue = hcy.X * 360.0f;
|
||||
@@ -750,6 +751,7 @@ namespace Robust.Shared.Maths
|
||||
/// </returns>
|
||||
/// <param name="rgb">Color value to convert.</param>
|
||||
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
|
||||
[Obsolete("The HCY color space is mathematically incorrect and these functions are broken, use something else")]
|
||||
public static Vector4 ToHcy(Color rgb)
|
||||
{
|
||||
var max = MathF.Max(rgb.R, MathF.Max(rgb.G, rgb.B));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Robust.Shared.Maths
|
||||
@@ -238,6 +239,7 @@ namespace Robust.Shared.Maths
|
||||
/// </summary>
|
||||
/// <param name="dir"></param>
|
||||
/// <returns></returns>
|
||||
[Pure]
|
||||
public static Angle ToAngle(this Direction dir)
|
||||
{
|
||||
var ang = Segment * (int) dir;
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Robust.Shared.Audio;
|
||||
/// Contains audio defaults to set for sounds.
|
||||
/// This can be used by <see cref="Content.Shared.Audio.SharedContentAudioSystem"/> to apply an audio preset.
|
||||
/// </summary>
|
||||
[Prototype("audioPreset")]
|
||||
[Prototype]
|
||||
public sealed partial class AudioPresetPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.Audio;
|
||||
|
||||
[Prototype("soundCollection")]
|
||||
[Prototype]
|
||||
public sealed partial class SoundCollectionPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
|
||||
@@ -60,9 +60,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
internal string GetPath(ResPath relPath)
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(_directory.FullName, relPath.ToRelativeSystemPath()))
|
||||
// Sanitise platform-specific path and standardize it for engine use.
|
||||
.Replace(Path.DirectorySeparatorChar, '/');
|
||||
return PathHelpers.SafeGetResourcePath(_directory.FullName, relPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -14,7 +14,11 @@ namespace Robust.Shared.ContentPack
|
||||
/// The directory to use for user data.
|
||||
/// If null, a virtual temporary file system is used instead.
|
||||
/// </param>
|
||||
void Initialize(string? userData);
|
||||
/// <param name="hideUserDataDir">
|
||||
/// If true, <see cref="IWritableDirProvider.RootDir"/> will be hidden on
|
||||
/// <see cref="IResourceManager.UserData"/>.
|
||||
/// </param>
|
||||
void Initialize(string? userData, bool hideUserDataDir);
|
||||
|
||||
/// <summary>
|
||||
/// Mounts a single stream as a content file. Useful for unit testing.
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
/// <summary>
|
||||
/// The root path of this provider.
|
||||
/// Can be null if it's a virtual provider.
|
||||
/// Can be null if it's a virtual provider or the path is protected (e.g. on the client).
|
||||
/// </summary>
|
||||
string? RootDir { get; }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.ContentPack
|
||||
{
|
||||
@@ -63,5 +64,27 @@ namespace Robust.Shared.ContentPack
|
||||
!OperatingSystem.IsWindows()
|
||||
&& !OperatingSystem.IsMacOS();
|
||||
|
||||
|
||||
internal static string SafeGetResourcePath(string baseDir, ResPath path)
|
||||
{
|
||||
var relSysPath = path.ToRelativeSystemPath();
|
||||
if (relSysPath.Contains("\\..") || relSysPath.Contains("/.."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
var retPath = Path.GetFullPath(Path.Join(baseDir, relSysPath));
|
||||
// better safe than sorry check
|
||||
if (!retPath.StartsWith(baseDir))
|
||||
{
|
||||
// Allow path to match if it's just missing the directory separator at the end.
|
||||
if (retPath != baseDir.TrimEnd(Path.DirectorySeparatorChar))
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
return retPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,13 +41,13 @@ namespace Robust.Shared.ContentPack
|
||||
public IWritableDirProvider UserData { get; private set; } = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void Initialize(string? userData)
|
||||
public virtual void Initialize(string? userData, bool hideRootDir)
|
||||
{
|
||||
Sawmill = _logManager.GetSawmill("res");
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
UserData = new WritableDirProvider(Directory.CreateDirectory(userData));
|
||||
UserData = new WritableDirProvider(Directory.CreateDirectory(userData), hideRootDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -381,6 +381,10 @@ namespace Robust.Shared.ContentPack
|
||||
{
|
||||
var rootDir = loader.GetPath(new ResPath(@"/"));
|
||||
|
||||
// TODO: GET RID OF THIS.
|
||||
// This code shouldn't be passing OS disk paths through ResPath.
|
||||
rootDir = rootDir.Replace(Path.DirectorySeparatorChar, '/');
|
||||
|
||||
yield return new ResPath(rootDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,17 +10,22 @@ namespace Robust.Shared.ContentPack
|
||||
/// <inheritdoc />
|
||||
internal sealed class WritableDirProvider : IWritableDirProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
private readonly bool _hideRootDir;
|
||||
|
||||
public string RootDir { get; }
|
||||
|
||||
string? IWritableDirProvider.RootDir => _hideRootDir ? null : RootDir;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an instance of <see cref="WritableDirProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="rootDir">Root file system directory to allow writing.</param>
|
||||
public WritableDirProvider(DirectoryInfo rootDir)
|
||||
/// <param name="hideRootDir">If true, <see cref="IWritableDirProvider.RootDir"/> is reported as null.</param>
|
||||
public WritableDirProvider(DirectoryInfo rootDir, bool hideRootDir)
|
||||
{
|
||||
// FullName does not have a trailing separator, and we MUST have a separator.
|
||||
RootDir = rootDir.FullName + Path.DirectorySeparatorChar.ToString();
|
||||
_hideRootDir = hideRootDir;
|
||||
}
|
||||
|
||||
#region File Access
|
||||
@@ -119,7 +124,7 @@ namespace Robust.Shared.ContentPack
|
||||
throw new FileNotFoundException();
|
||||
|
||||
var dirInfo = new DirectoryInfo(GetFullPath(path));
|
||||
return new WritableDirProvider(dirInfo);
|
||||
return new WritableDirProvider(dirInfo, _hideRootDir);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -180,20 +185,7 @@ namespace Robust.Shared.ContentPack
|
||||
|
||||
path = path.Clean();
|
||||
|
||||
return GetFullPath(RootDir, path);
|
||||
}
|
||||
|
||||
private static string GetFullPath(string root, ResPath path)
|
||||
{
|
||||
var relPath = path.ToRelativeSystemPath();
|
||||
if (relPath.Contains("\\..") || relPath.Contains("/.."))
|
||||
{
|
||||
// Hard cap on any exploit smuggling a .. in there.
|
||||
// Since that could allow leaving sandbox.
|
||||
throw new InvalidOperationException($"This branch should never be reached. Path: {path}");
|
||||
}
|
||||
|
||||
return Path.GetFullPath(Path.Combine(root, relPath));
|
||||
return PathHelpers.SafeGetResourcePath(RootDir, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ public sealed partial class MapLoaderSystem : EntitySystem
|
||||
Log.Info($"Saving serialized results to {path}");
|
||||
path = path.ToRootedPath();
|
||||
var document = new YamlDocument(data.ToYaml());
|
||||
_resourceManager.UserData.CreateDir(path.Directory);
|
||||
using var writer = _resourceManager.UserData.OpenWriteText(path);
|
||||
{
|
||||
var stream = new YamlStream {document};
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
|
||||
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(SharedVisibilitySystem))]
|
||||
public sealed partial class VisibilityComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Controls PVS visibility of entities. THIS COMPONENT CONTROLS WHETHER ENTITIES ARE NETWORKED TO PLAYERS
|
||||
/// AND SHOULD NOT BE USED AS THE SOLE WAY TO HIDE AN ENTITY FROM A PLAYER.
|
||||
/// The visibility layer for the entity.
|
||||
/// Players whose visibility masks don't match this won't get state updates for it.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[Access(typeof(SharedVisibilitySystem))]
|
||||
public sealed partial class VisibilityComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The visibility layer for the entity.
|
||||
/// Players whose visibility masks don't match this won't get state updates for it.
|
||||
/// </summary>
|
||||
[DataField("layer")]
|
||||
public ushort Layer = 1;
|
||||
}
|
||||
/// <remarks>
|
||||
/// Not serialized as visibility is normally immediate (i.e. prior to MapInit) and content should be handling it as such.
|
||||
/// </remarks>
|
||||
[DataField(readOnly: true)]
|
||||
public ushort Layer = 1;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public abstract partial class EntityManager
|
||||
Dirty(uid, comp, metadata);
|
||||
}
|
||||
|
||||
public virtual void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
|
||||
public virtual void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
|
||||
where T : IComponentDelta
|
||||
{
|
||||
var compReg = ComponentFactory.GetRegistration(CompIdx.Index<T>());
|
||||
|
||||
@@ -172,12 +172,22 @@ public partial class EntitySystem
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params ReadOnlySpan<string> fields)
|
||||
protected void DirtyFields<T>(EntityUid uid, T comp, MetaDataComponent? meta, params string[] fields)
|
||||
where T : IComponentDelta
|
||||
{
|
||||
EntityManager.DirtyFields(uid, comp, meta, fields);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected void DirtyFields<T>(Entity<T?> ent, MetaDataComponent? meta, params string[] fields)
|
||||
where T : IComponentDelta
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
EntityManager.DirtyFields(ent, ent.Comp, meta, fields);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks a component as dirty. This also implicitly dirties the entity this component belongs to.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -406,7 +407,7 @@ public sealed partial class EntityLookupSystem
|
||||
var broadphaseInv = _transform.GetInvWorldMatrix(lookupUid);
|
||||
|
||||
var localBounds = broadphaseInv.TransformBounds(worldBounds);
|
||||
var polygon = _physics.GetPooled(localBounds);
|
||||
var polygon = new SlimPolygon(localBounds);
|
||||
var result = AnyEntitiesIntersecting(lookupUid,
|
||||
polygon,
|
||||
localBounds.CalcBoundingBox(),
|
||||
@@ -414,7 +415,6 @@ public sealed partial class EntityLookupSystem
|
||||
flags,
|
||||
ignored);
|
||||
|
||||
_physics.ReturnPooled(polygon);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -458,9 +458,8 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return false;
|
||||
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
var result = AnyEntitiesIntersecting(mapId, polygon, Physics.Transform.Empty, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -475,9 +474,8 @@ public sealed partial class EntityLookupSystem
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
AddEntitiesIntersecting(mapId, intersecting, polygon, Physics.Transform.Empty, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -487,18 +485,16 @@ public sealed partial class EntityLookupSystem
|
||||
public bool AnyEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
// Don't need to check contained entities as they have the same bounds as the parent.
|
||||
var polygon = _physics.GetPooled(worldBounds);
|
||||
var polygon = new SlimPolygon(worldBounds);
|
||||
var result = AnyEntitiesIntersecting(mapId, polygon, Physics.Transform.Empty, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
return result;
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetEntitiesIntersecting(MapId mapId, Box2Rotated worldBounds, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var intersecting = new HashSet<EntityUid>();
|
||||
var polygon = _physics.GetPooled(worldBounds);
|
||||
var polygon = new SlimPolygon(worldBounds);
|
||||
AddEntitiesIntersecting(mapId, intersecting, polygon, Physics.Transform.Empty, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
return intersecting;
|
||||
}
|
||||
|
||||
@@ -761,11 +757,10 @@ public sealed partial class EntityLookupSystem
|
||||
return;
|
||||
|
||||
var localAABB = _transform.GetInvWorldMatrix(gridId).TransformBox(worldAABB);
|
||||
var polygon = _physics.GetPooled(localAABB);
|
||||
var polygon = new SlimPolygon(localAABB);
|
||||
|
||||
AddEntitiesIntersecting(gridId, intersecting, polygon, localAABB, Physics.Transform.Empty, flags, lookup);
|
||||
AddContained(intersecting, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting(EntityUid gridId, Box2Rotated worldBounds, HashSet<EntityUid> intersecting, LookupFlags flags = DefaultFlags)
|
||||
@@ -774,11 +769,10 @@ public sealed partial class EntityLookupSystem
|
||||
return;
|
||||
|
||||
var localBounds = _transform.GetInvWorldMatrix(gridId).TransformBounds(worldBounds);
|
||||
var polygon = _physics.GetPooled(localBounds);
|
||||
var polygon = new SlimPolygon(localBounds);
|
||||
|
||||
AddEntitiesIntersecting(gridId, intersecting, polygon, localBounds.CalcBoundingBox(), Physics.Transform.Empty, flags, lookup);
|
||||
AddContained(intersecting, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -121,9 +121,8 @@ public sealed partial class EntityLookupSystem
|
||||
if (!_broadQuery.Resolve(lookupUid, ref lookup))
|
||||
return;
|
||||
|
||||
var polygon = _physics.GetPooled(localAABB);
|
||||
var polygon = new SlimPolygon(localAABB);
|
||||
AddEntitiesIntersecting(lookupUid, intersecting, polygon, localAABB, Physics.Transform.Empty, flags, query, lookup);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
private void AddEntitiesIntersecting<T, TShape>(
|
||||
@@ -252,11 +251,10 @@ public sealed partial class EntityLookupSystem
|
||||
if (!_broadQuery.Resolve(lookupUid, ref lookup))
|
||||
return false;
|
||||
|
||||
var polygon = _physics.GetPooled(localAABB);
|
||||
var polygon = new SlimPolygon(localAABB);
|
||||
var (lookupPos, lookupRot) = _transform.GetWorldPositionRotation(lookupUid);
|
||||
var transform = new Transform(lookupPos, lookupRot);
|
||||
var result = AnyComponentsIntersecting(lookupUid, polygon, localAABB, transform, flags, query, ignored, lookup);
|
||||
_physics.ReturnPooled(polygon);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -427,10 +425,9 @@ public sealed partial class EntityLookupSystem
|
||||
|
||||
public bool AnyComponentsIntersecting(Type type, MapId mapId, Box2 worldAABB, EntityUid? ignored = null, LookupFlags flags = DefaultFlags)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
var transform = Physics.Transform.Empty;
|
||||
var result = AnyComponentsIntersecting(type, mapId, polygon, transform, ignored, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -496,33 +493,30 @@ public sealed partial class EntityLookupSystem
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
var transform = Physics.Transform.Empty;
|
||||
|
||||
GetEntitiesIntersecting(type, mapId, polygon, transform, intersecting, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting<T>(MapId mapId, Box2Rotated worldBounds, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
var polygon = _physics.GetPooled(worldBounds);
|
||||
var polygon = new SlimPolygon(worldBounds);
|
||||
var shapeTransform = Physics.Transform.Empty;
|
||||
|
||||
GetEntitiesIntersecting(mapId, polygon, shapeTransform, entities, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void GetEntitiesIntersecting<T>(MapId mapId, Box2 worldAABB, HashSet<Entity<T>> entities, LookupFlags flags = DefaultFlags) where T : IComponent
|
||||
{
|
||||
if (mapId == MapId.Nullspace) return;
|
||||
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
var shapeTransform = Physics.Transform.Empty;
|
||||
|
||||
GetEntitiesIntersecting(mapId, polygon, shapeTransform, entities, flags);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -26,7 +26,7 @@ public sealed partial class EntityLookupSystem
|
||||
if (!_broadQuery.Resolve(lookupUid, ref lookup))
|
||||
return;
|
||||
|
||||
var lookupPoly = new Polygon(localAABB);
|
||||
var lookupPoly = new SlimPolygon(localAABB);
|
||||
AddEntitiesIntersecting(lookupUid, intersecting, lookupPoly, localAABB, Physics.Transform.Empty, flags, lookup);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed partial class EntityLookupSystem
|
||||
if (!_broadQuery.Resolve(lookupUid, ref lookup))
|
||||
return;
|
||||
|
||||
var shape = new Polygon(localBounds);
|
||||
var shape = new SlimPolygon(localBounds);
|
||||
var localAABB = localBounds.CalcBoundingBox();
|
||||
|
||||
AddEntitiesIntersecting(lookupUid, intersecting, shape, localAABB, Physics.Transform.Empty, flags);
|
||||
@@ -55,7 +55,7 @@ public sealed partial class EntityLookupSystem
|
||||
if (!_broadQuery.Resolve(lookupUid, ref lookup))
|
||||
return false;
|
||||
|
||||
var shape = new Polygon(localAABB);
|
||||
var shape = new SlimPolygon(localAABB);
|
||||
return AnyEntitiesIntersecting(lookupUid, shape, localAABB, Physics.Transform.Empty, flags, ignored, lookup);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +159,10 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
eye.Comp.PvsScale = Math.Clamp(scale, 0.1f, 100f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overwrites visibility mask of an entity's eye.
|
||||
/// If you wish for other systems to potentially change it consider raising <see cref="RefreshVisibilityMask"/>.
|
||||
/// </summary>
|
||||
public void SetVisibilityMask(EntityUid uid, int value, EyeComponent? eyeComponent = null)
|
||||
{
|
||||
if (!Resolve(uid, ref eyeComponent))
|
||||
@@ -170,4 +174,32 @@ public abstract class SharedEyeSystem : EntitySystem
|
||||
eyeComponent.VisibilityMask = value;
|
||||
DirtyField(uid, eyeComponent, nameof(EyeComponent.VisibilityMask));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the visibility mask for an entity by raising a <see cref="GetVisMaskEvent"/>
|
||||
/// </summary>
|
||||
public void RefreshVisibilityMask(Entity<EyeComponent?> entity)
|
||||
{
|
||||
if (!Resolve(entity.Owner, ref entity.Comp, false))
|
||||
return;
|
||||
|
||||
var ev = new GetVisMaskEvent()
|
||||
{
|
||||
Entity = entity.Owner,
|
||||
};
|
||||
RaiseLocalEvent(entity.Owner, ref ev, true);
|
||||
|
||||
SetVisibilityMask(entity.Owner, ev.VisibilityMask, entity.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event raised to update the vismask of an entity's eye.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct GetVisMaskEvent()
|
||||
{
|
||||
public EntityUid Entity;
|
||||
|
||||
public int VisibilityMask = EyeComponent.DefaultVisibilityMask;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.Map.Enumerators;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
|
||||
namespace Robust.Shared.Map;
|
||||
|
||||
@@ -224,48 +225,42 @@ internal partial class MapManager
|
||||
|
||||
public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, GridCallback callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, callback, approx, includeMap);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void FindGridsIntersecting<TState>(EntityUid mapEnt, Box2 worldAABB, ref TState state, GridCallback<TState> callback, bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, ref state, callback, approx, includeMap);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void FindGridsIntersecting(EntityUid mapEnt, Box2 worldAABB, ref List<Entity<MapGridComponent>> grids,
|
||||
bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldAABB);
|
||||
var polygon = new SlimPolygon(worldAABB);
|
||||
FindGridsIntersecting(mapEnt, polygon, worldAABB, Transform.Empty, ref grids, approx, includeMap);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, GridCallback callback, bool approx = IMapManager.Approximate,
|
||||
bool includeMap = IMapManager.IncludeMap)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldBounds);
|
||||
var polygon = new SlimPolygon(worldBounds);
|
||||
FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, callback, approx, includeMap);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void FindGridsIntersecting<TState>(EntityUid mapEnt, Box2Rotated worldBounds, ref TState state, GridCallback<TState> callback,
|
||||
bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldBounds);
|
||||
var polygon = new SlimPolygon(worldBounds);
|
||||
FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, ref state, callback, approx, includeMap);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
public void FindGridsIntersecting(EntityUid mapEnt, Box2Rotated worldBounds, ref List<Entity<MapGridComponent>> grids,
|
||||
bool approx = IMapManager.Approximate, bool includeMap = IMapManager.IncludeMap)
|
||||
{
|
||||
var polygon = _physics.GetPooled(worldBounds);
|
||||
var polygon = new SlimPolygon(worldBounds);
|
||||
FindGridsIntersecting(mapEnt, polygon, worldBounds.CalcBoundingBox(), Transform.Empty, ref grids, approx, includeMap);
|
||||
_physics.ReturnPooled(polygon);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -12,7 +12,9 @@ internal sealed partial class CollisionManager
|
||||
/// <param name="xfA">The transform for the first shape.</param>
|
||||
/// <param name="xfB">The transform for the seconds shape.</param>
|
||||
/// <returns></returns>
|
||||
public bool TestOverlap<T, U>(T shapeA, int indexA, U shapeB, int indexB, in Transform xfA, in Transform xfB) where T : IPhysShape where U : IPhysShape
|
||||
public bool TestOverlap<T, U>(T shapeA, int indexA, U shapeB, int indexB, in Transform xfA, in Transform xfB)
|
||||
where T : IPhysShape
|
||||
where U : IPhysShape
|
||||
{
|
||||
var input = new DistanceInput();
|
||||
|
||||
|
||||
@@ -21,12 +21,9 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
@@ -46,7 +43,7 @@ internal ref struct DistanceProxy
|
||||
|
||||
// GJK using Voronoi regions (Christer Ericson) and Barycentric coordinates.
|
||||
|
||||
internal DistanceProxy(Vector2[] vertices, float radius)
|
||||
internal DistanceProxy(ReadOnlySpan<Vector2> vertices, float radius)
|
||||
{
|
||||
Vertices = vertices;
|
||||
Radius = radius;
|
||||
@@ -71,9 +68,18 @@ internal ref struct DistanceProxy
|
||||
case ShapeType.Polygon:
|
||||
if (shape is Polygon poly)
|
||||
{
|
||||
Vertices = poly.Vertices.AsSpan()[..poly.VertexCount];
|
||||
Span<Vector2> verts = new Vector2[poly.VertexCount];
|
||||
poly._vertices.AsSpan[..poly.VertexCount].CopyTo(verts);
|
||||
Vertices = verts;
|
||||
Radius = poly.Radius;
|
||||
}
|
||||
else if (shape is SlimPolygon fast)
|
||||
{
|
||||
Span<Vector2> verts = new Vector2[fast.VertexCount];
|
||||
fast._vertices.AsSpan[..fast.VertexCount].CopyTo(verts);
|
||||
Vertices = verts;
|
||||
Radius = fast.Radius;
|
||||
}
|
||||
else
|
||||
{
|
||||
var polyShape = Unsafe.As<PolygonShape>(shape);
|
||||
@@ -151,7 +157,7 @@ internal ref struct DistanceProxy
|
||||
return Vertices[bestIndex];
|
||||
}
|
||||
|
||||
internal static DistanceProxy MakeProxy(Vector2[] vertices, int count, float radius )
|
||||
internal static DistanceProxy MakeProxy(ReadOnlySpan<Vector2> vertices, int count, float radius )
|
||||
{
|
||||
count = Math.Min(count, PhysicsConstants.MaxPolygonVertices);
|
||||
var proxy = new DistanceProxy(vertices[..count], radius);
|
||||
|
||||
@@ -173,10 +173,25 @@ namespace Robust.Shared.Physics.Collision.Shapes
|
||||
{
|
||||
}
|
||||
|
||||
internal PolygonShape(SlimPolygon poly)
|
||||
{
|
||||
Vertices = new Vector2[poly.VertexCount];
|
||||
Normals = new Vector2[poly.VertexCount];
|
||||
|
||||
poly._vertices.AsSpan[..VertexCount].CopyTo(Vertices);
|
||||
poly._normals.AsSpan[..VertexCount].CopyTo(Normals);
|
||||
|
||||
Centroid = poly.Centroid;
|
||||
}
|
||||
|
||||
internal PolygonShape(Polygon poly)
|
||||
{
|
||||
Vertices = poly.Vertices;
|
||||
Normals = poly.Normals;
|
||||
Vertices = new Vector2[poly.VertexCount];
|
||||
Normals = new Vector2[poly.VertexCount];
|
||||
|
||||
poly._vertices.AsSpan[..VertexCount].CopyTo(Vertices);
|
||||
poly._normals.AsSpan[..VertexCount].CopyTo(Normals);
|
||||
|
||||
Centroid = poly.Centroid;
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,15 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
Friction = MathF.Sqrt((FixtureA?.Friction ?? 0.0f) * (FixtureB?.Friction ?? 0.0f));
|
||||
}
|
||||
|
||||
public void GetWorldManifold(Transform transformA, Transform transformB, out Vector2 normal)
|
||||
{
|
||||
var shapeA = FixtureA?.Shape!;
|
||||
var shapeB = FixtureB?.Shape!;
|
||||
Span<Vector2> points = stackalloc Vector2[PhysicsConstants.MaxPolygonVertices];
|
||||
|
||||
SharedPhysicsSystem.InitializeManifold(ref Manifold, transformA, transformB, shapeA.Radius, shapeB.Radius, out normal, points);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the world manifold.
|
||||
/// </summary>
|
||||
@@ -408,6 +417,28 @@ namespace Robust.Shared.Physics.Dynamics.Contacts
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
[Pure, PublicAPI]
|
||||
public PhysicsComponent OurBody(EntityUid uid)
|
||||
{
|
||||
if (uid == EntityA)
|
||||
return BodyA!;
|
||||
else if (uid == EntityB)
|
||||
return BodyB!;
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
[Pure, PublicAPI]
|
||||
public PhysicsComponent OtherBody(EntityUid uid)
|
||||
{
|
||||
if (uid == EntityA)
|
||||
return BodyB!;
|
||||
else if (uid == EntityB)
|
||||
return BodyA!;
|
||||
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
||||
@@ -11,19 +11,28 @@ namespace Robust.Shared.Physics.Shapes;
|
||||
// Internal so people don't use it when it will have breaking changes very soon.
|
||||
internal record struct Polygon : IPhysShape
|
||||
{
|
||||
public static Polygon Empty = new(Box2.Empty);
|
||||
[DataField]
|
||||
public byte VertexCount { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Vertices associated with this polygon. Will be sliced to <see cref="VertexCount"/>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Consider using _vertices if doing engine work.
|
||||
/// </remarks>
|
||||
public Vector2[] Vertices => _vertices.AsSpan[..VertexCount].ToArray();
|
||||
|
||||
public Vector2[] Normals => _normals.AsSpan[..VertexCount].ToArray();
|
||||
|
||||
[DataField]
|
||||
public Vector2[] Vertices;
|
||||
internal FixedArray8<Vector2> _vertices;
|
||||
|
||||
public byte VertexCount;
|
||||
|
||||
public Vector2[] Normals;
|
||||
internal FixedArray8<Vector2> _normals;
|
||||
|
||||
public Vector2 Centroid;
|
||||
|
||||
public int ChildCount => 1;
|
||||
public float Radius { get; set; }
|
||||
public float Radius { get; set; } = PhysicsConstants.PolygonRadius;
|
||||
public ShapeType ShapeType => ShapeType.Polygon;
|
||||
|
||||
// Hopefully this one is short-lived for a few months
|
||||
@@ -35,67 +44,60 @@ internal record struct Polygon : IPhysShape
|
||||
public Polygon(PolygonShape polyShape)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Vertices = new Vector2[polyShape.VertexCount];
|
||||
Normals = new Vector2[polyShape.Normals.Length];
|
||||
Radius = polyShape.Radius;
|
||||
Centroid = polyShape.Centroid;
|
||||
VertexCount = (byte) polyShape.VertexCount;
|
||||
|
||||
Array.Copy(polyShape.Vertices, Vertices, Vertices.Length);
|
||||
Array.Copy(polyShape.Normals, Normals, Vertices.Length);
|
||||
VertexCount = (byte) Vertices.Length;
|
||||
polyShape.Vertices.AsSpan()[..VertexCount].CopyTo(_vertices.AsSpan);
|
||||
polyShape.Normals.AsSpan()[..VertexCount].CopyTo(_normals.AsSpan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually constructed polygon for internal use to take advantage of pooling.
|
||||
/// </summary>
|
||||
internal Polygon(Vector2[] vertices, Vector2[] normals, Vector2 centroid, byte count)
|
||||
public Polygon(Box2 box)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Vertices = vertices;
|
||||
Normals = normals;
|
||||
Centroid = centroid;
|
||||
VertexCount = count;
|
||||
}
|
||||
|
||||
public Polygon(Box2 aabb)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Vertices = new Vector2[4];
|
||||
Normals = new Vector2[4];
|
||||
Radius = 0f;
|
||||
|
||||
Vertices[0] = aabb.BottomLeft;
|
||||
Vertices[1] = aabb.BottomRight;
|
||||
Vertices[2] = aabb.TopRight;
|
||||
Vertices[3] = aabb.TopLeft;
|
||||
|
||||
Normals[0] = new Vector2(0.0f, -1.0f);
|
||||
Normals[1] = new Vector2(1.0f, 0.0f);
|
||||
Normals[2] = new Vector2(0.0f, 1.0f);
|
||||
Normals[3] = new Vector2(-1.0f, 0.0f);
|
||||
|
||||
VertexCount = 4;
|
||||
Centroid = aabb.Center;
|
||||
|
||||
_vertices._00 = box.BottomLeft;
|
||||
_vertices._01 = box.BottomRight;
|
||||
_vertices._02 = box.TopRight;
|
||||
_vertices._03 = box.TopLeft;
|
||||
|
||||
_normals._00 = new Vector2(0.0f, -1.0f);
|
||||
_normals._01 = new Vector2(1.0f, 0.0f);
|
||||
_normals._02 = new Vector2(0.0f, 1.0f);
|
||||
_normals._03 = new Vector2(-1.0f, 0.0f);
|
||||
}
|
||||
|
||||
public Polygon(Box2Rotated bounds)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Vertices = new Vector2[4];
|
||||
Normals = new Vector2[4];
|
||||
Radius = 0f;
|
||||
|
||||
Vertices[0] = bounds.BottomLeft;
|
||||
Vertices[1] = bounds.BottomRight;
|
||||
Vertices[2] = bounds.TopRight;
|
||||
Vertices[3] = bounds.TopLeft;
|
||||
|
||||
CalculateNormals(Normals, 4);
|
||||
|
||||
VertexCount = 4;
|
||||
|
||||
_vertices._00 = bounds.BottomLeft;
|
||||
_vertices._01 = bounds.BottomRight;
|
||||
_vertices._02 = bounds.TopRight;
|
||||
_vertices._03 = bounds.TopLeft;
|
||||
|
||||
CalculateNormals(_vertices.AsSpan, _normals.AsSpan, 4);
|
||||
|
||||
Centroid = bounds.Center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually constructed polygon for internal use to take advantage of pooling.
|
||||
/// </summary>
|
||||
internal Polygon(ReadOnlySpan<Vector2> vertices, ReadOnlySpan<Vector2> normals, Vector2 centroid, byte count)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
vertices[..VertexCount].CopyTo(_vertices.AsSpan);
|
||||
normals[..VertexCount].CopyTo(_normals.AsSpan);
|
||||
Centroid = centroid;
|
||||
VertexCount = count;
|
||||
Radius = 0f;
|
||||
}
|
||||
|
||||
public Polygon(Vector2[] vertices)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
@@ -104,16 +106,15 @@ internal record struct Polygon : IPhysShape
|
||||
if (hull.Count < 3)
|
||||
{
|
||||
VertexCount = 0;
|
||||
Vertices = [];
|
||||
Normals = [];
|
||||
return;
|
||||
}
|
||||
|
||||
VertexCount = (byte) vertices.Length;
|
||||
Vertices = vertices;
|
||||
Normals = new Vector2[vertices.Length];
|
||||
var vertSpan = _vertices.AsSpan;
|
||||
|
||||
vertices.AsSpan().CopyTo(vertSpan);
|
||||
Set(hull);
|
||||
Centroid = ComputeCentroid(Vertices);
|
||||
Centroid = ComputeCentroid(vertSpan);
|
||||
}
|
||||
|
||||
public static explicit operator Polygon(PolygonShape polyShape)
|
||||
@@ -125,32 +126,32 @@ internal record struct Polygon : IPhysShape
|
||||
{
|
||||
DebugTools.Assert(hull.Count >= 3);
|
||||
var vertexCount = hull.Count;
|
||||
Array.Resize(ref Vertices, vertexCount);
|
||||
Array.Resize(ref Normals, vertexCount);
|
||||
var verts = _vertices.AsSpan;
|
||||
var norms = _normals.AsSpan;
|
||||
|
||||
for (var i = 0; i < vertexCount; i++)
|
||||
{
|
||||
Vertices[i] = hull.Points[i];
|
||||
verts[i] = hull.Points[i];
|
||||
}
|
||||
|
||||
// Compute normals. Ensure the edges have non-zero length.
|
||||
CalculateNormals(Normals, vertexCount);
|
||||
CalculateNormals(verts, norms, vertexCount);
|
||||
}
|
||||
|
||||
internal void CalculateNormals(Span<Vector2> normals, int count)
|
||||
public static void CalculateNormals(ReadOnlySpan<Vector2> vertices, Span<Vector2> normals, int count)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var next = i + 1 < count ? i + 1 : 0;
|
||||
var edge = Vertices[next] - Vertices[i];
|
||||
var edge = vertices[next] - vertices[i];
|
||||
DebugTools.Assert(edge.LengthSquared() > float.Epsilon * float.Epsilon);
|
||||
|
||||
var temp = Vector2Helpers.Cross(edge, 1f);
|
||||
Normals[i] = temp.Normalized();
|
||||
normals[i] = temp.Normalized();
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector2 ComputeCentroid(Vector2[] vs)
|
||||
public static Vector2 ComputeCentroid(ReadOnlySpan<Vector2> vs)
|
||||
{
|
||||
var count = vs.Length;
|
||||
DebugTools.Assert(count >= 3);
|
||||
@@ -191,13 +192,15 @@ internal record struct Polygon : IPhysShape
|
||||
|
||||
public Box2 ComputeAABB(Transform transform, int childIndex)
|
||||
{
|
||||
DebugTools.Assert(VertexCount > 0);
|
||||
DebugTools.Assert(childIndex == 0);
|
||||
var lower = Transform.Mul(transform, Vertices[0]);
|
||||
var verts = _vertices.AsSpan;
|
||||
var lower = Transform.Mul(transform, verts[0]);
|
||||
var upper = lower;
|
||||
|
||||
for (var i = 1; i < VertexCount; ++i)
|
||||
{
|
||||
var v = Transform.Mul(transform, Vertices[i]);
|
||||
var v = Transform.Mul(transform, verts[i]);
|
||||
lower = Vector2.Min(lower, v);
|
||||
upper = Vector2.Max(upper, v);
|
||||
}
|
||||
@@ -208,12 +211,41 @@ internal record struct Polygon : IPhysShape
|
||||
|
||||
public bool Equals(IPhysShape? other)
|
||||
{
|
||||
if (other is not PolygonShape poly) return false;
|
||||
if (VertexCount != poly.VertexCount) return false;
|
||||
if (other is SlimPolygon slim)
|
||||
{
|
||||
return Equals(slim);
|
||||
}
|
||||
|
||||
return other is Polygon poly && Equals(poly);
|
||||
}
|
||||
|
||||
public bool Equals(Polygon other)
|
||||
{
|
||||
if (VertexCount != other.VertexCount) return false;
|
||||
|
||||
var ourVerts = _vertices.AsSpan;
|
||||
var otherVerts = other._vertices.AsSpan;
|
||||
|
||||
for (var i = 0; i < VertexCount; i++)
|
||||
{
|
||||
var vert = Vertices[i];
|
||||
if (!vert.Equals(poly.Vertices[i])) return false;
|
||||
var vert = ourVerts[i];
|
||||
if (!vert.Equals(otherVerts[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Equals(SlimPolygon other)
|
||||
{
|
||||
if (VertexCount != other.VertexCount) return false;
|
||||
|
||||
var ourVerts = _vertices.AsSpan;
|
||||
var otherVerts = other._vertices.AsSpan;
|
||||
|
||||
for (var i = 0; i < VertexCount; i++)
|
||||
{
|
||||
var vert = ourVerts[i];
|
||||
if (!vert.Equals(otherVerts[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -221,6 +253,6 @@ internal record struct Polygon : IPhysShape
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(VertexCount, Vertices.AsSpan(0, VertexCount).ToArray(), Radius);
|
||||
return HashCode.Combine(VertexCount, _vertices.AsSpan.ToArray(), Radius);
|
||||
}
|
||||
}
|
||||
|
||||
103
Robust.Shared/Physics/Shapes/SlimPolygon.cs
Normal file
103
Robust.Shared/Physics/Shapes/SlimPolygon.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Physics.Shapes;
|
||||
|
||||
/// <summary>
|
||||
/// Polygon backed by FixedArray4 to be smaller.
|
||||
/// Useful for internal ops where the inputs are boxes to avoid the additional padding.
|
||||
/// </summary>
|
||||
internal record struct SlimPolygon : IPhysShape
|
||||
{
|
||||
public Vector2[] Vertices => _vertices.AsSpan[..VertexCount].ToArray();
|
||||
|
||||
public Vector2[] Normals => _normals.AsSpan[..VertexCount].ToArray();
|
||||
|
||||
[DataField]
|
||||
public FixedArray4<Vector2> _vertices;
|
||||
|
||||
public FixedArray4<Vector2> _normals;
|
||||
|
||||
public Vector2 Centroid;
|
||||
|
||||
public byte VertexCount => 4;
|
||||
|
||||
public int ChildCount => 1;
|
||||
public float Radius { get; set; } = PhysicsConstants.PolygonRadius;
|
||||
public ShapeType ShapeType => ShapeType.Polygon;
|
||||
|
||||
public SlimPolygon(Box2 box)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Radius = 0f;
|
||||
|
||||
_vertices._00 = box.BottomLeft;
|
||||
_vertices._01 = box.BottomRight;
|
||||
_vertices._02 = box.TopRight;
|
||||
_vertices._03 = box.TopLeft;
|
||||
|
||||
_normals._00 = new Vector2(0.0f, -1.0f);
|
||||
_normals._01 = new Vector2(1.0f, 0.0f);
|
||||
_normals._02 = new Vector2(0.0f, 1.0f);
|
||||
_normals._03 = new Vector2(-1.0f, 0.0f);
|
||||
}
|
||||
|
||||
public SlimPolygon(Box2Rotated bounds)
|
||||
{
|
||||
Unsafe.SkipInit(out this);
|
||||
Radius = 0f;
|
||||
|
||||
_vertices._00 = bounds.BottomLeft;
|
||||
_vertices._01 = bounds.BottomRight;
|
||||
_vertices._02 = bounds.TopRight;
|
||||
_vertices._03 = bounds.TopLeft;
|
||||
|
||||
Polygon.CalculateNormals(_vertices.AsSpan, _normals.AsSpan, 4);
|
||||
|
||||
Centroid = bounds.Center;
|
||||
}
|
||||
|
||||
public Box2 ComputeAABB(Transform transform, int childIndex)
|
||||
{
|
||||
DebugTools.Assert(VertexCount > 0);
|
||||
DebugTools.Assert(childIndex == 0);
|
||||
var verts = _vertices.AsSpan;
|
||||
var lower = Transform.Mul(transform, verts[0]);
|
||||
var upper = lower;
|
||||
|
||||
for (var i = 1; i < VertexCount; ++i)
|
||||
{
|
||||
var v = Transform.Mul(transform, verts[i]);
|
||||
lower = Vector2.Min(lower, v);
|
||||
upper = Vector2.Max(upper, v);
|
||||
}
|
||||
|
||||
var r = new Vector2(Radius, Radius);
|
||||
return new Box2(lower - r, upper + r);
|
||||
}
|
||||
|
||||
public bool Equals(SlimPolygon other)
|
||||
{
|
||||
return Radius.Equals(other.Radius) && _vertices.AsSpan[..VertexCount].SequenceEqual(other._vertices.AsSpan[..VertexCount]);
|
||||
}
|
||||
|
||||
public readonly override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_vertices, _normals, Centroid, Radius);
|
||||
}
|
||||
|
||||
public bool Equals(IPhysShape? other)
|
||||
{
|
||||
if (other is Polygon poly)
|
||||
{
|
||||
return poly.Equals(this);
|
||||
}
|
||||
|
||||
return other is SlimPolygon slim && Equals(slim);
|
||||
}
|
||||
}
|
||||
@@ -40,13 +40,29 @@ namespace Robust.Shared.Physics.Systems
|
||||
|
||||
return true;
|
||||
}
|
||||
case SlimPolygon slim:
|
||||
{
|
||||
var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position);
|
||||
var norms = slim._normals.AsSpan;
|
||||
var verts = slim._vertices.AsSpan;
|
||||
|
||||
for (var i = 0; i < slim.VertexCount; i++)
|
||||
{
|
||||
var dot = Vector2.Dot(norms[i], pLocal - verts[i]);
|
||||
if (dot > 0f) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
case Polygon poly:
|
||||
{
|
||||
var pLocal = Physics.Transform.MulT(xform.Quaternion2D, worldPoint - xform.Position);
|
||||
var norms = poly._normals.AsSpan;
|
||||
var verts = poly._vertices.AsSpan;
|
||||
|
||||
for (var i = 0; i < poly.VertexCount; i++)
|
||||
{
|
||||
var dot = Vector2.Dot(poly.Normals[i], pLocal - poly.Vertices[i]);
|
||||
var dot = Vector2.Dot(norms[i], pLocal - verts[i]);
|
||||
if (dot > 0f) return false;
|
||||
}
|
||||
|
||||
@@ -86,6 +102,10 @@ namespace Robust.Shared.Physics.Systems
|
||||
var polygon = (PolygonShape) aabb;
|
||||
GetMassData(polygon, ref data, density);
|
||||
break;
|
||||
case Polygon fastPoly:
|
||||
return GetMassData(new PolygonShape(fastPoly), density);
|
||||
case SlimPolygon slim:
|
||||
return GetMassData(new PolygonShape(slim), density);
|
||||
case PolygonShape poly:
|
||||
// Polygon mass, centroid, and inertia.
|
||||
// Let rho be the polygon density in mass per unit area.
|
||||
@@ -195,6 +215,12 @@ namespace Robust.Shared.Physics.Systems
|
||||
var polygon = (PolygonShape) aabb;
|
||||
GetMassData(polygon, ref data, density);
|
||||
break;
|
||||
case Polygon fastPoly:
|
||||
GetMassData(new PolygonShape(fastPoly), ref data, density);
|
||||
break;
|
||||
case SlimPolygon slim:
|
||||
GetMassData(new PolygonShape(slim), ref data, density);
|
||||
break;
|
||||
case PolygonShape poly:
|
||||
// Polygon mass, centroid, and inertia.
|
||||
// Let rho be the polygon density in mass per unit area.
|
||||
|
||||
@@ -191,6 +191,12 @@ public sealed partial class RayCastSystem
|
||||
|
||||
private CastOutput RayCastPolygon(RayCastInput input, Polygon shape)
|
||||
{
|
||||
var verts = shape._vertices.AsSpan;
|
||||
var output = new CastOutput()
|
||||
{
|
||||
Fraction = 0f,
|
||||
};
|
||||
|
||||
if (shape.Radius == 0.0f)
|
||||
{
|
||||
// Put the ray into the polygon's frame of reference.
|
||||
@@ -201,18 +207,15 @@ public sealed partial class RayCastSystem
|
||||
|
||||
var index = -1;
|
||||
|
||||
var output = new CastOutput()
|
||||
{
|
||||
Fraction = 0f,
|
||||
};
|
||||
var norms = shape._normals.AsSpan;
|
||||
|
||||
for ( var i = 0; i < shape.VertexCount; ++i )
|
||||
{
|
||||
// p = p1 + a * d
|
||||
// dot(normal, p - v) = 0
|
||||
// dot(normal, p1 - v) + a * dot(normal, d) = 0
|
||||
float numerator = Vector2.Dot(shape.Normals[i], Vector2.Subtract( shape.Vertices[i], p1 ) );
|
||||
float denominator = Vector2.Dot(shape.Normals[i], d );
|
||||
float numerator = Vector2.Dot(norms[i], Vector2.Subtract( verts[i], p1 ) );
|
||||
float denominator = Vector2.Dot(norms[i], d );
|
||||
|
||||
if ( denominator == 0.0f )
|
||||
{
|
||||
@@ -257,7 +260,7 @@ public sealed partial class RayCastSystem
|
||||
if (index >= 0)
|
||||
{
|
||||
output.Fraction = lower;
|
||||
output.Normal = shape.Normals[index];
|
||||
output.Normal = norms[index];
|
||||
output.Point = Vector2.Add(p1, lower * d);
|
||||
output.Hit = true;
|
||||
}
|
||||
@@ -265,17 +268,24 @@ public sealed partial class RayCastSystem
|
||||
return output;
|
||||
}
|
||||
|
||||
// TODO_ERIN this is not working for ray vs box (zero radii)
|
||||
Span<Vector2> proxyBVerts = new Vector2[]
|
||||
{
|
||||
input.Origin,
|
||||
};
|
||||
|
||||
// TODO_ERIN this is not working for ray vs box (zero radii)
|
||||
var castInput = new ShapeCastPairInput
|
||||
{
|
||||
ProxyA = DistanceProxy.MakeProxy(shape.Vertices, shape.VertexCount, shape.Radius),
|
||||
ProxyB = DistanceProxy.MakeProxy([input.Origin], 1, 0.0f),
|
||||
ProxyA = DistanceProxy.MakeProxy(verts, shape.VertexCount, shape.Radius),
|
||||
ProxyB = DistanceProxy.MakeProxy(proxyBVerts, 1, 0.0f),
|
||||
TransformA = Physics.Transform.Empty,
|
||||
TransformB = Physics.Transform.Empty,
|
||||
TranslationB = input.Translation,
|
||||
MaxFraction = input.MaxFraction
|
||||
};
|
||||
return ShapeCast(castInput);
|
||||
|
||||
ShapeCast(ref output, castInput);
|
||||
return output;
|
||||
}
|
||||
|
||||
// Ray vs line segment
|
||||
@@ -436,12 +446,9 @@ public sealed partial class RayCastSystem
|
||||
// "Smooth Mesh Contacts with GJK" in Game Physics Pearls. 2010
|
||||
// todo this is failing when used to raycast a box
|
||||
// todo this converges slowly with a radius
|
||||
private CastOutput ShapeCast(ShapeCastPairInput input)
|
||||
private void ShapeCast(ref CastOutput output, in ShapeCastPairInput input)
|
||||
{
|
||||
var output = new CastOutput()
|
||||
{
|
||||
Fraction = input.MaxFraction,
|
||||
};
|
||||
output.Fraction = input.MaxFraction;
|
||||
|
||||
var proxyA = input.ProxyA;
|
||||
var count = input.ProxyB.Vertices.Length;
|
||||
@@ -513,15 +520,15 @@ public sealed partial class RayCastSystem
|
||||
if ( vr <= 0.0f )
|
||||
{
|
||||
// miss
|
||||
return output;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
lambda = ( vp - sigma ) / vr;
|
||||
if ( lambda > maxFraction )
|
||||
{
|
||||
// too far
|
||||
return output;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// reset the simplex
|
||||
simplex.Count = 0;
|
||||
@@ -562,7 +569,7 @@ public sealed partial class RayCastSystem
|
||||
{
|
||||
// Overlap
|
||||
// Yes this means you need to manually query for overlaps.
|
||||
return output;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get search direction.
|
||||
@@ -576,7 +583,7 @@ public sealed partial class RayCastSystem
|
||||
if ( iter == 0 || lambda == 0.0f )
|
||||
{
|
||||
// Initial overlap
|
||||
return output;
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare output.
|
||||
@@ -591,7 +598,6 @@ public sealed partial class RayCastSystem
|
||||
output.Fraction = lambda;
|
||||
output.Iterations = iter;
|
||||
output.Hit = true;
|
||||
return output;
|
||||
}
|
||||
|
||||
private int FindSupport(DistanceProxy proxy, Vector2 direction)
|
||||
@@ -613,9 +619,14 @@ public sealed partial class RayCastSystem
|
||||
|
||||
private CastOutput ShapeCastCircle(ShapeCastInput input, PhysShapeCircle shape)
|
||||
{
|
||||
Span<Vector2> proxyAVerts = new[]
|
||||
{
|
||||
shape.Position,
|
||||
};
|
||||
|
||||
var pairInput = new ShapeCastPairInput
|
||||
{
|
||||
ProxyA = DistanceProxy.MakeProxy([shape.Position], 1, shape.Radius ),
|
||||
ProxyA = DistanceProxy.MakeProxy(proxyAVerts, 1, shape.Radius ),
|
||||
ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius ),
|
||||
TransformA = Physics.Transform.Empty,
|
||||
TransformB = Physics.Transform.Empty,
|
||||
@@ -623,7 +634,8 @@ public sealed partial class RayCastSystem
|
||||
MaxFraction = input.MaxFraction
|
||||
};
|
||||
|
||||
var output = ShapeCast(pairInput);
|
||||
var output = new CastOutput();
|
||||
ShapeCast(ref output, pairInput);
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -631,7 +643,7 @@ public sealed partial class RayCastSystem
|
||||
{
|
||||
var pairInput = new ShapeCastPairInput
|
||||
{
|
||||
ProxyA = DistanceProxy.MakeProxy(shape.Vertices, shape.VertexCount, shape.Radius),
|
||||
ProxyA = DistanceProxy.MakeProxy(shape._vertices.AsSpan, shape.VertexCount, shape.Radius),
|
||||
ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius),
|
||||
TransformA = Physics.Transform.Empty,
|
||||
TransformB = Physics.Transform.Empty,
|
||||
@@ -639,21 +651,30 @@ public sealed partial class RayCastSystem
|
||||
MaxFraction = input.MaxFraction
|
||||
};
|
||||
|
||||
var output = ShapeCast(pairInput);
|
||||
var output = new CastOutput();
|
||||
ShapeCast(ref output, pairInput);
|
||||
return output;
|
||||
}
|
||||
|
||||
private CastOutput ShapeCastSegment(ShapeCastInput input, EdgeShape shape)
|
||||
{
|
||||
var pairInput = new ShapeCastPairInput();
|
||||
pairInput.ProxyA = DistanceProxy.MakeProxy([shape.Vertex0], 2, 0.0f);
|
||||
pairInput.ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius);
|
||||
pairInput.TransformA = Physics.Transform.Empty;
|
||||
pairInput.TransformB = Physics.Transform.Empty;
|
||||
pairInput.TranslationB = input.Translation;
|
||||
pairInput.MaxFraction = input.MaxFraction;
|
||||
Span<Vector2> proxyAVerts = new[]
|
||||
{
|
||||
shape.Vertex0,
|
||||
};
|
||||
|
||||
var output = ShapeCast(pairInput);
|
||||
var pairInput = new ShapeCastPairInput
|
||||
{
|
||||
ProxyA = DistanceProxy.MakeProxy(proxyAVerts, 2, 0.0f),
|
||||
ProxyB = DistanceProxy.MakeProxy(input.Points, input.Count, input.Radius),
|
||||
TransformA = Physics.Transform.Empty,
|
||||
TransformB = Physics.Transform.Empty,
|
||||
TranslationB = input.Translation,
|
||||
MaxFraction = input.MaxFraction
|
||||
};
|
||||
|
||||
var output = new CastOutput();
|
||||
ShapeCast(ref output, pairInput);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,6 +275,9 @@ public sealed partial class RayCastSystem : EntitySystem
|
||||
case PhysShapeCircle circle:
|
||||
CastCircle(entity, ref result, circle, originTransform, translation, filter, callback);
|
||||
break;
|
||||
case SlimPolygon slim:
|
||||
CastPolygon(entity, ref result, new PolygonShape(slim), originTransform, translation, filter, callback);
|
||||
break;
|
||||
case Polygon poly:
|
||||
CastPolygon(entity, ref result, new PolygonShape(poly), originTransform, translation, filter, callback);
|
||||
break;
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
using System.Buffers;
|
||||
using System.Numerics;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Shapes;
|
||||
|
||||
namespace Robust.Shared.Physics.Systems;
|
||||
|
||||
public abstract partial class SharedPhysicsSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a polygon with pooled arrays backing it.
|
||||
/// </summary>
|
||||
internal Polygon GetPooled(Box2 box)
|
||||
{
|
||||
var vertices = ArrayPool<Vector2>.Shared.Rent(4);
|
||||
var normals = ArrayPool<Vector2>.Shared.Rent(4);
|
||||
var centroid = box.Center;
|
||||
|
||||
vertices[0] = box.BottomLeft;
|
||||
vertices[1] = box.BottomRight;
|
||||
vertices[2] = box.TopRight;
|
||||
vertices[3] = box.TopLeft;
|
||||
|
||||
normals[0] = new Vector2(0.0f, -1.0f);
|
||||
normals[1] = new Vector2(1.0f, 0.0f);
|
||||
normals[2] = new Vector2(0.0f, 1.0f);
|
||||
normals[3] = new Vector2(-1.0f, 0.0f);
|
||||
|
||||
return new Polygon(vertices, normals, centroid, 4);
|
||||
}
|
||||
|
||||
internal Polygon GetPooled(Box2Rotated box)
|
||||
{
|
||||
var vertices = ArrayPool<Vector2>.Shared.Rent(4);
|
||||
var normals = ArrayPool<Vector2>.Shared.Rent(4);
|
||||
var centroid = box.Center;
|
||||
|
||||
vertices[0] = box.BottomLeft;
|
||||
vertices[1] = box.BottomRight;
|
||||
vertices[2] = box.TopRight;
|
||||
vertices[3] = box.TopLeft;
|
||||
|
||||
var polygon = new Polygon(vertices, normals, centroid, 4);
|
||||
polygon.CalculateNormals(normals, 4);
|
||||
|
||||
return polygon;
|
||||
}
|
||||
|
||||
internal void ReturnPooled(Polygon polygon)
|
||||
{
|
||||
ArrayPool<Vector2>.Shared.Return(polygon.Vertices);
|
||||
ArrayPool<Vector2>.Shared.Return(polygon.Normals);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.Prototypes;
|
||||
/// Prototype that represents some entity prototype category.
|
||||
/// Useful for sorting or grouping entity prototypes for mapping/spawning UIs.
|
||||
/// </summary>
|
||||
[Prototype("entityCategory")]
|
||||
[Prototype]
|
||||
public sealed partial class EntityCategoryPrototype : IPrototype
|
||||
{
|
||||
[IdDataField] public string ID { get; private set; } = default!;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Robust.Shared.Prototypes
|
||||
/// <summary>
|
||||
/// Prototype that represents game entities.
|
||||
/// </summary>
|
||||
[Prototype("entity", -1)]
|
||||
[Prototype(-1)]
|
||||
public sealed partial class EntityPrototype : IPrototype, IInheritingPrototype, ISerializationHooks
|
||||
{
|
||||
private ILocalizationManager _loc = default!;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Shared.Prototypes;
|
||||
/// <summary>
|
||||
/// Prototype that represents an alias from one tile ID to another. These are used when deserializing entities from yaml.
|
||||
/// </summary>
|
||||
[Prototype("tileAlias")]
|
||||
[Prototype]
|
||||
public sealed partial class TileAliasPrototype : IPrototype
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -384,9 +384,16 @@ namespace Robust.Shared.Serialization.Markdown.Mapping
|
||||
if (_children.Count != other._children.Count)
|
||||
return false;
|
||||
|
||||
// Given that keys are unique and we do not care about the ordering, we know that if removing identical
|
||||
// key-value pairs leaves us with an empty list then the mappings are equal.
|
||||
return Except(other) == null && Tag == other.Tag;
|
||||
foreach (var (key, otherValue) in other)
|
||||
{
|
||||
if (!_children.TryGetValue(key, out var ownValue) ||
|
||||
!otherValue.Equals(ownValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return Tag == other.Tag;
|
||||
}
|
||||
|
||||
public override MappingDataNode PushInheritance(MappingDataNode node)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
@@ -86,7 +87,7 @@ namespace Robust.Shared.Utility
|
||||
public Span<T> AsSpan => MemoryMarshal.CreateSpan(ref _00, 2);
|
||||
}
|
||||
|
||||
internal struct FixedArray4<T>
|
||||
internal struct FixedArray4<T> : IEquatable<FixedArray4<T>>
|
||||
{
|
||||
public T _00;
|
||||
public T _01;
|
||||
@@ -94,9 +95,27 @@ namespace Robust.Shared.Utility
|
||||
public T _03;
|
||||
|
||||
public Span<T> AsSpan => MemoryMarshal.CreateSpan(ref _00, 4);
|
||||
|
||||
public bool Equals(FixedArray4<T> other)
|
||||
{
|
||||
return _00?.Equals(other._00) == true &&
|
||||
_01?.Equals(other._01) == true &&
|
||||
_02?.Equals(other._02) == true &&
|
||||
_03?.Equals(other._03) == true;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is FixedArray4<T> other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_00, _01, _02, _03);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct FixedArray8<T>
|
||||
internal struct FixedArray8<T> : IEquatable<FixedArray8<T>>
|
||||
{
|
||||
public T _00;
|
||||
public T _01;
|
||||
@@ -108,6 +127,28 @@ namespace Robust.Shared.Utility
|
||||
public T _07;
|
||||
|
||||
public Span<T> AsSpan => MemoryMarshal.CreateSpan(ref _00, 8);
|
||||
|
||||
public bool Equals(FixedArray8<T> other)
|
||||
{
|
||||
return _00?.Equals(other._00) == true &&
|
||||
_01?.Equals(other._01) == true &&
|
||||
_02?.Equals(other._02) == true &&
|
||||
_03?.Equals(other._03) == true &&
|
||||
_04?.Equals(other._04) == true &&
|
||||
_05?.Equals(other._05) == true &&
|
||||
_06?.Equals(other._06) == true &&
|
||||
_07?.Equals(other._07) == true;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is FixedArray8<T> other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(_00, _01, _02, _03, _04, _05, _06, _07);
|
||||
}
|
||||
}
|
||||
|
||||
internal struct FixedArray16<T>
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Shared.Utility;
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class FormattedMessage : IReadOnlyList<MarkupNode>
|
||||
public sealed partial class FormattedMessage : IEquatable<FormattedMessage>, IReadOnlyList<MarkupNode>
|
||||
{
|
||||
public static FormattedMessage Empty => new();
|
||||
|
||||
@@ -278,6 +278,18 @@ public sealed partial class FormattedMessage : IReadOnlyList<MarkupNode>
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(FormattedMessage? other)
|
||||
{
|
||||
return other?.ToMarkup() == ToMarkup();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ToMarkup().GetHashCode();
|
||||
}
|
||||
|
||||
/// <returns>The string without markup tags.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
|
||||
@@ -123,5 +123,71 @@ namespace Robust.UnitTesting.Client.UserInterface.Controls
|
||||
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 65)));
|
||||
Assert.That(control2.Size, Is.EqualTo(new Vector2(30, 15)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTwoExpandRatio()
|
||||
{
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SetSize = new Vector2(100, 10),
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
MinWidth = 10,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 20,
|
||||
},
|
||||
new Control
|
||||
{
|
||||
MinWidth = 10,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 80
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(20));
|
||||
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(80));
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestTwoExpandOneSmall()
|
||||
{
|
||||
var boxContainer = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
SetSize = new Vector2(100, 10),
|
||||
Children =
|
||||
{
|
||||
new Control
|
||||
{
|
||||
MinWidth = 30,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 20,
|
||||
},
|
||||
new Control
|
||||
{
|
||||
MinWidth = 30,
|
||||
HorizontalExpand = true,
|
||||
SizeFlagsStretchRatio = 80
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(30));
|
||||
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(70));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ public sealed partial class ComponentMapInitTest
|
||||
|
||||
var sim = simFactory.InitializeInstance();
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
sim.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap(out var mapId);
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
mapSystem.CreateMap(out var mapId);
|
||||
|
||||
var ent = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
|
||||
Assert.That(entManager.GetComponent<MetaDataComponent>(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
@@ -36,7 +36,7 @@ public sealed partial class ComponentMapInitTest
|
||||
|
||||
Assert.That(comp.Count, Is.EqualTo(1));
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
[Reflect(false)]
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Robust.UnitTesting.Shared
|
||||
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
|
||||
|
||||
Assert.That(outcome, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(Box2Cases))]
|
||||
@@ -127,7 +127,7 @@ namespace Robust.UnitTesting.Shared
|
||||
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
|
||||
|
||||
Assert.That(outcome, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -156,7 +156,7 @@ namespace Robust.UnitTesting.Shared
|
||||
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryBounds, entities);
|
||||
|
||||
Assert.That(entities.Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -185,7 +185,7 @@ namespace Robust.UnitTesting.Shared
|
||||
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryTile, entities);
|
||||
|
||||
Assert.That(entities.Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -200,7 +200,7 @@ namespace Robust.UnitTesting.Shared
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
entManager.System<SharedMapSystem>().CreateMap(spawnPos.MapId);
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace Robust.UnitTesting.Shared
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(IntersectingCases))]
|
||||
@@ -236,7 +236,7 @@ namespace Robust.UnitTesting.Shared
|
||||
var bounds = new Box2Rotated(Box2.CenteredAround(queryPos.Position, new Vector2(range, range)));
|
||||
|
||||
Assert.That(lookup.GetEntitiesIntersecting(queryPos.MapId, bounds).Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(InRangeCases))]
|
||||
@@ -260,7 +260,7 @@ namespace Robust.UnitTesting.Shared
|
||||
|
||||
_ = entManager.SpawnEntity(null, spawnPos);
|
||||
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(InRangeCases))]
|
||||
@@ -271,7 +271,7 @@ namespace Robust.UnitTesting.Shared
|
||||
|
||||
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
entManager.System<SharedMapSystem>().CreateMap(spawnPos.MapId);
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace Robust.UnitTesting.Shared
|
||||
entManager.Spawn(null, spawnPos);
|
||||
|
||||
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -309,7 +309,7 @@ namespace Robust.UnitTesting.Shared
|
||||
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
|
||||
|
||||
Assert.That(outcome, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -338,7 +338,7 @@ namespace Robust.UnitTesting.Shared
|
||||
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryBounds, entities);
|
||||
|
||||
Assert.That(entities.Count > 0, Is.EqualTo(result));
|
||||
mapManager.DeleteMap(spawnPos.MapId);
|
||||
mapSystem.DeleteMap(spawnPos.MapId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -383,7 +383,7 @@ namespace Robust.UnitTesting.Shared
|
||||
|
||||
entManager.DeleteEntity(dummy);
|
||||
entManager.DeleteEntity(grid);
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(0));
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(1));
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public sealed class GridSplit_Tests
|
||||
mapSystem.SetTile(gridEnt, new Vector2i(2, 0), Tile.Empty);
|
||||
Assert.That(mapManager.GetAllGrids(mapId).Count(), Is.EqualTo(2));
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -75,7 +75,7 @@ public sealed class GridSplit_Tests
|
||||
mapSystem.SetTile(gridEnt, new Vector2i(1, 0), Tile.Empty);
|
||||
Assert.That(mapManager.GetAllGrids(mapId).Count(), Is.EqualTo(2));
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -106,7 +106,7 @@ public sealed class GridSplit_Tests
|
||||
mapSystem.SetTile(gridEnt, new Vector2i(1, 0), Tile.Empty);
|
||||
Assert.That(mapManager.GetAllGrids(mapId).Count(), Is.EqualTo(2));
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -130,7 +130,7 @@ public sealed class GridSplit_Tests
|
||||
mapSystem.SetTile(gridEnt, new Vector2i(1, 0), Tile.Empty);
|
||||
Assert.That(mapManager.GetAllGrids(mapId).Count(), Is.EqualTo(3));
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -181,6 +181,6 @@ public sealed class GridSplit_Tests
|
||||
Assert.That(dummyXform.GridUid, Is.EqualTo(newGrid.Owner));
|
||||
Assert.That(newGridXform._children, Does.Contain(dummy));
|
||||
});
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ public sealed class MapGridMap_Tests
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
mapManager.CreateGridEntity(mapId);
|
||||
@@ -49,6 +50,6 @@ public sealed class MapGridMap_Tests
|
||||
entManager.TickUpdate(0.016f, false);
|
||||
});
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,10 @@ public sealed class Fixtures_Test
|
||||
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var sysManager = sim.Resolve<IEntitySystemManager>();
|
||||
var fixturesSystem = sysManager.GetEntitySystem<FixtureSystem>();
|
||||
var physicsSystem = sysManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
var mapSystem = sysManager.GetEntitySystem<SharedMapSystem>();
|
||||
var map = sim.CreateMap().MapId;
|
||||
|
||||
var ent = sim.SpawnEntity(null, new MapCoordinates(Vector2.Zero, map));
|
||||
@@ -36,6 +36,6 @@ public sealed class Fixtures_Test
|
||||
Assert.That(fixture.Density, Is.EqualTo(10f));
|
||||
Assert.That(body.Mass, Is.EqualTo(10f));
|
||||
|
||||
mapManager.DeleteMap(map);
|
||||
mapSystem.DeleteMap(map);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ public sealed class Joints_Test
|
||||
var sim = factory.InitializeInstance();
|
||||
|
||||
var entManager = sim.Resolve<IEntityManager>();
|
||||
var mapManager = sim.Resolve<IMapManager>();
|
||||
var jointSystem = entManager.System<SharedJointSystem>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
var mapId = sim.CreateMap().MapId;
|
||||
|
||||
@@ -53,7 +53,7 @@ public sealed class Joints_Test
|
||||
Assert.That(entManager.GetComponent<JointRelayTargetComponent>(uidC).Relayed, Is.Empty);
|
||||
Assert.That(entManager.GetComponent<JointComponent>(uidA).Relay, Is.EqualTo(null));
|
||||
});
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,11 +65,11 @@ public sealed class Joints_Test
|
||||
var factory = RobustServerSimulation.NewSimulation();
|
||||
var server = factory.InitializeInstance();
|
||||
var entManager = server.Resolve<IEntityManager>();
|
||||
var mapManager = server.Resolve<IMapManager>();
|
||||
var fixtureSystem = entManager.EntitySysManager.GetEntitySystem<FixtureSystem>();
|
||||
var jointSystem = entManager.EntitySysManager.GetEntitySystem<JointSystem>();
|
||||
var broadphaseSystem = entManager.EntitySysManager.GetEntitySystem<SharedBroadphaseSystem>();
|
||||
var physicsSystem = server.Resolve<IEntitySystemManager>().GetEntitySystem<SharedPhysicsSystem>();
|
||||
var mapSystem = entManager.System<SharedMapSystem>();
|
||||
|
||||
var mapId = server.CreateMap().MapId;
|
||||
|
||||
@@ -106,6 +106,6 @@ public sealed class Joints_Test
|
||||
broadphaseSystem.FindNewContacts(mapId);
|
||||
Assert.That(body1.Contacts, Has.Count.EqualTo(1));
|
||||
|
||||
mapManager.DeleteMap(mapId);
|
||||
mapSystem.DeleteMap(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ public sealed class RayCast_Test
|
||||
|
||||
// Polygon
|
||||
// - Initial overlap, no shapecast
|
||||
new(new Polygon(Box2.UnitCentered), new Transform(Vector2.UnitY / 2f, Angle.Zero), Vector2.UnitY, null),
|
||||
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.UnitY / 2f, Angle.Zero), Vector2.UnitY, null),
|
||||
|
||||
// - Cast
|
||||
new(new Polygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), Vector2.UnitY, new Vector2(0.5f, 1f - PhysicsConstants.PolygonRadius)),
|
||||
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), Vector2.UnitY, new Vector2(0.5f, 1f - PhysicsConstants.PolygonRadius)),
|
||||
|
||||
// - Miss
|
||||
new(new Polygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), -Vector2.UnitY, null),
|
||||
new(new SlimPolygon(Box2.UnitCentered), new Transform(Vector2.Zero, Angle.Zero), -Vector2.UnitY, null),
|
||||
};
|
||||
|
||||
[Test, TestCaseSource(nameof(_rayCases))]
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Physics
|
||||
|
||||
@@ -9,10 +9,23 @@ namespace Robust.UnitTesting.Shared.Physics;
|
||||
[TestFixture]
|
||||
public sealed class Polygon_Test
|
||||
{
|
||||
/// <summary>
|
||||
/// Check that Slim and normal Polygon are equals
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSlim()
|
||||
{
|
||||
var slim = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
var poly = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
Assert.That(slim.Equals(poly));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAABB()
|
||||
{
|
||||
var shape = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
|
||||
Assert.That(shape.ComputeAABB(Transform.Empty, 0), Is.EqualTo(Box2.UnitCentered.Translated(Vector2.One)));
|
||||
}
|
||||
@@ -20,8 +33,8 @@ public sealed class Polygon_Test
|
||||
[Test]
|
||||
public void TestBox2()
|
||||
{
|
||||
var shape = new Polygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
Assert.That(shape.Vertices, Is.EqualTo(new Vector2[]
|
||||
var shape = new SlimPolygon(Box2.UnitCentered.Translated(Vector2.One));
|
||||
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
|
||||
{
|
||||
new Vector2(0.5f, 0.5f),
|
||||
new Vector2(1.5f, 0.5f),
|
||||
@@ -33,9 +46,9 @@ public sealed class Polygon_Test
|
||||
[Test]
|
||||
public void TestBox2Rotated()
|
||||
{
|
||||
var shape = new Polygon(new Box2Rotated(Box2.UnitCentered, Angle.FromDegrees(90)));
|
||||
var shape = new SlimPolygon(new Box2Rotated(Box2.UnitCentered, Angle.FromDegrees(90)));
|
||||
|
||||
Assert.That(shape.Vertices, Is.EqualTo(new Vector2[]
|
||||
Assert.That(shape._vertices.AsSpan.ToArray(), Is.EqualTo(new Vector2[]
|
||||
{
|
||||
new Vector2(0.5f, -0.5f),
|
||||
new Vector2(0.5f, 0.5f),
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Robust.UnitTesting.Shared.Resources
|
||||
_testDir = Directory.CreateDirectory(_testDirPath);
|
||||
var subDir = Path.Combine(_testDirPath, "writable");
|
||||
|
||||
_dirProvider = new WritableDirProvider(Directory.CreateDirectory(subDir));
|
||||
_dirProvider = new WritableDirProvider(Directory.CreateDirectory(subDir), hideRootDir: false);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
|
||||
@@ -20,10 +20,13 @@ public abstract partial class EntitySpawnHelpersTest : RobustIntegrationTest
|
||||
{
|
||||
protected ServerIntegrationInstance Server = default!;
|
||||
protected IEntityManager EntMan = default!;
|
||||
protected IMapManager MapMan = default!;
|
||||
protected SharedMapSystem MapSys = default!;
|
||||
protected SharedTransformSystem Xforms = default!;
|
||||
protected SharedContainerSystem Container = default!;
|
||||
|
||||
// Even if unused, content / downstream tests might use this class, so removal would be a breaking change?
|
||||
protected IMapManager MapMan = default!;
|
||||
|
||||
protected EntityUid Map;
|
||||
protected MapId MapId;
|
||||
protected EntityUid Parent; // entity parented to the map.
|
||||
@@ -43,6 +46,7 @@ public abstract partial class EntitySpawnHelpersTest : RobustIntegrationTest
|
||||
await Server.WaitIdleAsync();
|
||||
MapMan = Server.ResolveDependency<IMapManager>();
|
||||
EntMan = Server.ResolveDependency<IEntityManager>();
|
||||
MapSys = EntMan.System<SharedMapSystem>();
|
||||
Xforms = EntMan.System<SharedTransformSystem>();
|
||||
Container = EntMan.System<SharedContainerSystem>();
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ public sealed class SpawnInContainerOrDropTest : EntitySpawnHelpersTest
|
||||
Assert.That(xform.GridUid, Is.Null);
|
||||
});
|
||||
|
||||
await Server.WaitPost(() =>MapMan.DeleteMap(MapId));
|
||||
await Server.WaitPost(() => MapSys.DeleteMap(MapId));
|
||||
Server.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ public sealed class SpawnNextToOrDropTest : EntitySpawnHelpersTest
|
||||
Assert.That(EntMan.GetComponent<MetaDataComponent>(uid).EntityLifeStage, Is.LessThan(EntityLifeStage.MapInitialized));
|
||||
});
|
||||
|
||||
await Server.WaitPost(() =>MapMan.DeleteMap(MapId));
|
||||
await Server.WaitPost(() => MapSys.DeleteMap(MapId));
|
||||
Server.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class TrySpawnInContainerTest : EntitySpawnHelpersTest
|
||||
Assert.That(EntMan.EntityExists(uid), Is.False);
|
||||
});
|
||||
|
||||
await Server.WaitPost(() =>MapMan.DeleteMap(MapId));
|
||||
await Server.WaitPost(() => MapSys.DeleteMap(MapId));
|
||||
Server.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public sealed class TrySpawnNextToTest : EntitySpawnHelpersTest
|
||||
Assert.That(EntMan.EntityExists(uid), Is.False);
|
||||
});
|
||||
|
||||
await Server.WaitPost(() =>MapMan.DeleteMap(MapId));
|
||||
await Server.WaitPost(() => MapSys.DeleteMap(MapId));
|
||||
Server.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,21 +197,21 @@ public sealed class TestEnumerableCommand : ToolshedCommand
|
||||
public sealed class TestNestedArrayCommand : ToolshedCommand
|
||||
{
|
||||
[CommandImplementation]
|
||||
public ProtoId<EntityPrototype>[] Impl() => Array.Empty<ProtoId<EntityPrototype>>();
|
||||
public ProtoId<EntityCategoryPrototype>[] Impl() => [];
|
||||
}
|
||||
|
||||
[ToolshedCommand]
|
||||
public sealed class TestNestedListCommand : ToolshedCommand
|
||||
{
|
||||
[CommandImplementation]
|
||||
public List<ProtoId<EntityPrototype>> Impl() => new();
|
||||
public List<ProtoId<EntityCategoryPrototype>> Impl() => new();
|
||||
}
|
||||
|
||||
[ToolshedCommand]
|
||||
public sealed class TestNestedEnumerableCommand : ToolshedCommand
|
||||
{
|
||||
private static ProtoId<EntityPrototype>[] _arr = Array.Empty<ProtoId<EntityPrototype>>();
|
||||
private static ProtoId<EntityCategoryPrototype>[] _arr = [];
|
||||
|
||||
[CommandImplementation]
|
||||
public IEnumerable<ProtoId<EntityPrototype>> Impl() => _arr.OrderByDescending(x => x.Id);
|
||||
public IEnumerable<ProtoId<EntityCategoryPrototype>> Impl() => _arr.OrderByDescending(x => x.Id);
|
||||
}
|
||||
|
||||
@@ -383,11 +383,11 @@ public sealed class ToolshedTests : ToolshedTest
|
||||
AssertResult("testenumerable testenumerableinfer 1", typeof(int));
|
||||
|
||||
// Repeat but with nested types. i.e. extracting T when piping ProtoId<T> -> IEnumerable<ProtoId<T>>
|
||||
AssertResult("testnestedarray testnestedarrayinfer", typeof(EntityPrototype));
|
||||
AssertResult("testnestedlist testnestedlistinfer", typeof(EntityPrototype));
|
||||
AssertResult("testnestedarray testnestedenumerableinfer", typeof(EntityPrototype));
|
||||
AssertResult("testnestedlist testnestedenumerableinfer", typeof(EntityPrototype));
|
||||
AssertResult("testnestedenumerable testnestedenumerableinfer", typeof(EntityPrototype));
|
||||
AssertResult("testnestedarray testnestedarrayinfer", typeof(EntityCategoryPrototype));
|
||||
AssertResult("testnestedlist testnestedlistinfer", typeof(EntityCategoryPrototype));
|
||||
AssertResult("testnestedarray testnestedenumerableinfer", typeof(EntityCategoryPrototype));
|
||||
AssertResult("testnestedlist testnestedenumerableinfer", typeof(EntityCategoryPrototype));
|
||||
AssertResult("testnestedenumerable testnestedenumerableinfer", typeof(EntityCategoryPrototype));
|
||||
|
||||
// The map command used to work when the piped type was passed as an IEnumerable<T> directly, but would fail
|
||||
// when given a List<T> or something else that implemented the interface.
|
||||
|
||||
Reference in New Issue
Block a user