mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
18 Commits
v220.2.0
...
fix/bui-st
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
318548a4d6 | ||
|
|
dde4dd06f6 | ||
|
|
7cb3aeccc2 | ||
|
|
ae83e606d6 | ||
|
|
d9d5ef7471 | ||
|
|
0f97f366a6 | ||
|
|
35ab0b8cc8 | ||
|
|
5a14e939bf | ||
|
|
ccba6b5d1c | ||
|
|
254a5987c7 | ||
|
|
8550056e68 | ||
|
|
25211e3781 | ||
|
|
3500abfd47 | ||
|
|
7d1915096a | ||
|
|
4504731588 | ||
|
|
701fa95a82 | ||
|
|
40a9048704 | ||
|
|
cee8d42776 |
2
.github/workflows/build-test.yml
vendored
2
.github/workflows/build-test.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
os: [ubuntu-latest, windows-latest ] # , macos-latest] - temporarily disabled due to libfreetype.dll errors.
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,6 +54,47 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 221.2.0
|
||||
|
||||
### New features
|
||||
|
||||
* Add SetMapAudio helper to SharedAudioSystem to setup map-wide audio entities.
|
||||
* Add SetWorldRotNoLerp method to SharedTransformSystem to avoid client lerping.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* `SpriteComponent.CopyFrom` now copies `CopyToShaderParameters` configuration.
|
||||
|
||||
|
||||
## 221.1.0
|
||||
|
||||
|
||||
## 221.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `EntParentChangedMessage.OldMapId` is now an `EntityUid` instead of `MapId`
|
||||
* `TransformSystem.DetachParentToNull()` is being renamed to `DetachEntity`
|
||||
* The order in which `MoveEvent` handlers are invoked has been changed to prioritise engine subscriptions
|
||||
|
||||
### New features
|
||||
|
||||
* Added `UpdateHovered()` and `SetHovered()` to `IUserInterfaceManager`, for updating or modifying the currently hovered control.
|
||||
* Add SwapPositions to TransformSystem to swap two entity's transforms.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Improve client gamestate exception tolerance.
|
||||
|
||||
### Other
|
||||
|
||||
* If the currently hovered control is disposed, `UserInterfaceManager` will now look for a new control, rather than just setting the hovered control to null.
|
||||
|
||||
### Internal
|
||||
|
||||
* Use more `EntityQuery<T>` internally in EntityManager and PhysicsSystem.
|
||||
|
||||
|
||||
## 220.2.0
|
||||
|
||||
### New features
|
||||
@@ -79,7 +120,7 @@ END TEMPLATE-->
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Refactor UserInterfaceSystem.
|
||||
* Refactor UserInterfaceSystem.
|
||||
- The API has been significantly cleaned up and standardised, most noticeably callers don't need to worry about TryGetUi and can rely on either HasUi, SetUiState, CloseUi, or OpenUi to handle their code as appropriate.
|
||||
- Interface data is now stored via key rather than as a flat list which is a breaking change for YAML.
|
||||
- BoundUserInterfaces can now be completely handled via Shared code. Existing Server-side callers will behave similarly to before.
|
||||
|
||||
@@ -1681,6 +1681,8 @@ namespace Robust.Client.GameObjects
|
||||
DirOffset = toClone.DirOffset;
|
||||
_autoAnimated = toClone._autoAnimated;
|
||||
RenderingStrategy = toClone.RenderingStrategy;
|
||||
if (toClone.CopyToShaderParameters is { } copyToShaderParameters)
|
||||
CopyToShaderParameters = new CopyToShaderParameters(copyToShaderParameters);
|
||||
}
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
@@ -2155,6 +2157,12 @@ namespace Robust.Client.GameObjects
|
||||
public object LayerKey = layerKey;
|
||||
public string? ParameterTexture;
|
||||
public string? ParameterUV;
|
||||
|
||||
public CopyToShaderParameters(CopyToShaderParameters toClone) : this(toClone.LayerKey)
|
||||
{
|
||||
ParameterTexture = toClone.ParameterTexture;
|
||||
ParameterUV = toClone.ParameterUV;
|
||||
}
|
||||
}
|
||||
|
||||
void IAnimationProperties.SetAnimatableProperty(string name, object value)
|
||||
|
||||
@@ -125,6 +125,8 @@ namespace Robust.Client.GameStates
|
||||
#endif
|
||||
|
||||
private bool _resettingPredictedEntities;
|
||||
private readonly List<EntityUid> _brokenEnts = new();
|
||||
private readonly List<(EntityUid, NetEntity)> _toStart = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Initialize()
|
||||
@@ -667,7 +669,16 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var netEntity in createdEntities)
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
if (!_entityManager.TryGetEntityData(netEntity, out _, out var meta))
|
||||
{
|
||||
_sawmill.Error($"Encountered deleted entity while merging implicit data! NetEntity: {netEntity}");
|
||||
continue;
|
||||
}
|
||||
#else
|
||||
var (_, meta) = _entityManager.GetEntityData(netEntity);
|
||||
#endif
|
||||
|
||||
var compData = _compDataPool.Get();
|
||||
_outputData.Add(netEntity, compData);
|
||||
|
||||
@@ -876,9 +887,22 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var (entity, data) in _toApply)
|
||||
{
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
HandleEntityState(entity, data.NetEntity, data.Meta, _entities.EventBus, data.curState,
|
||||
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
|
||||
|
||||
data.nextState, data.LastApplied, curState.ToSequence, data.EnteringPvs);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Caught exception while applying entity state. Entity: {_entities.ToPrettyString(entity)}. Exception: {e}");
|
||||
_entityManager.DeleteEntity(entity);
|
||||
RequestFullState();
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!data.EnteringPvs)
|
||||
continue;
|
||||
|
||||
@@ -917,7 +941,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessDeletions(delSpan, xforms, xformSys);
|
||||
ProcessDeletions(delSpan, xforms, metas, xformSys);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -962,6 +986,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
_toDelete.Clear();
|
||||
@@ -990,12 +1015,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// This entity is going to get deleted, but maybe some if its children won't be, so lets detach them to
|
||||
// null. First we will detach the parent in order to reduce the number of broadphase/lookup updates.
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
xformSys.DetachEntity(ent, xform);
|
||||
|
||||
// Then detach all children.
|
||||
foreach (var child in xform._children)
|
||||
{
|
||||
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
|
||||
xformSys.DetachEntity(child, xforms.Get(child), metas.Get(child), xform);
|
||||
|
||||
if (deleteClientChildren
|
||||
&& !deleteClientEntities // don't add duplicates
|
||||
@@ -1014,9 +1039,9 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessDeletions(
|
||||
ReadOnlySpan<NetEntity> delSpan,
|
||||
private void ProcessDeletions(ReadOnlySpan<NetEntity> delSpan,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
SharedTransformSystem xformSys)
|
||||
{
|
||||
// Processing deletions is non-trivial, because by default deletions will also delete all child entities.
|
||||
@@ -1043,13 +1068,13 @@ namespace Robust.Client.GameStates
|
||||
continue; // Already deleted? or never sent to us?
|
||||
|
||||
// First, a single recursive map change
|
||||
xformSys.DetachParentToNull(id.Value, xform);
|
||||
xformSys.DetachEntity(id.Value, xform);
|
||||
|
||||
// Then detach all children.
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
xformSys.DetachParentToNull(child, xforms.GetComponent(child), xform);
|
||||
xformSys.DetachEntity(child, xforms.Get(child), metas.Get(child), xform);
|
||||
}
|
||||
|
||||
// Finally, delete the entity.
|
||||
@@ -1144,7 +1169,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
meta._flags |= MetaDataFlags.Detached;
|
||||
xformSys.DetachParentToNull(ent.Value, xform);
|
||||
xformSys.DetachEntity(ent.Value, xform);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
|
||||
if (container != null)
|
||||
@@ -1157,63 +1182,58 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private void InitializeAndStart(Dictionary<NetEntity, EntityState> toCreate)
|
||||
{
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
_toStart.Clear();
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
var brokenEnts = new List<EntityUid>();
|
||||
#endif
|
||||
using (_prof.Group("Initialize Entity"))
|
||||
{
|
||||
EntityUid entity = default;
|
||||
foreach (var netEntity in toCreate.Keys)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
_entities.InitializeEntity(entity, metaQuery.GetComponent(entity));
|
||||
#if EXCEPTION_TOLERANCE
|
||||
(entity, var meta) = _entityManager.GetEntityData(netEntity);
|
||||
_entities.InitializeEntity(entity, meta);
|
||||
_toStart.Add((entity, netEntity));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Server entity threw in Init: ent={_entities.ToPrettyString(entity)}");
|
||||
_sawmill.Error($"Server entity threw in Init: nent={netEntity}, ent={_entities.ToPrettyString(entity)}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
|
||||
brokenEnts.Add(entity);
|
||||
toCreate.Remove(netEntity);
|
||||
}
|
||||
_toCreate.Remove(netEntity);
|
||||
_brokenEnts.Add(entity);
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (_prof.Group("Start Entity"))
|
||||
{
|
||||
foreach (var netEntity in toCreate.Keys)
|
||||
foreach (var (entity, netEntity) in _toStart)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
_entities.StartEntity(entity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
_entities.StartEntity(entity);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}");
|
||||
_sawmill.Error($"Server entity threw in Start: nent={netEntity}, ent={_entityManager.ToPrettyString(entity)}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
|
||||
brokenEnts.Add(entity);
|
||||
toCreate.Remove(netEntity);
|
||||
}
|
||||
_toCreate.Remove(netEntity);
|
||||
_brokenEnts.Add(entity);
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
foreach (var entity in brokenEnts)
|
||||
foreach (var entity in _brokenEnts)
|
||||
{
|
||||
_entityManager.DeleteEntity(entity);
|
||||
}
|
||||
#endif
|
||||
_brokenEnts.Clear();
|
||||
}
|
||||
|
||||
private void HandleEntityState(EntityUid uid, NetEntity netEntity, MetaDataComponent meta, IEventBus bus, EntityState? curState,
|
||||
@@ -1402,7 +1422,7 @@ namespace Robust.Client.GameStates
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, uid, out container);
|
||||
}
|
||||
|
||||
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
|
||||
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachEntity(uid, xform);
|
||||
|
||||
if (container != null)
|
||||
containerSys.AddExpectedEntity(_entities.GetNetEntity(uid), container);
|
||||
|
||||
@@ -52,6 +52,10 @@ namespace Robust.Client.UserInterface
|
||||
[ViewVariables] public bool IsMeasureValid { get; private set; }
|
||||
[ViewVariables] public bool IsArrangeValid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls the amount of empty space in virtual pixels around the control.
|
||||
/// </summary>
|
||||
/// <remarks>Values can be provided as "All" or "Horizontal, Vertical" or "Left, Top, Right, Bottom"</remarks>
|
||||
[ViewVariables]
|
||||
public Thickness Margin
|
||||
{
|
||||
|
||||
@@ -135,6 +135,17 @@ namespace Robust.Client.UserInterface
|
||||
/// Plays the UI hover sound if relevant.
|
||||
/// </summary>
|
||||
void HoverSound();
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="CurrentlyHovered"/> to the given control.
|
||||
/// </summary>
|
||||
void SetHovered(Control? control);
|
||||
|
||||
/// <summary>
|
||||
/// Forces <see cref="CurrentlyHovered"/> to get updated. This is done automatically when the mouse is moved,
|
||||
/// but not necessarily a new or existing control is rearranged.
|
||||
/// </summary>
|
||||
void UpdateHovered();
|
||||
}
|
||||
|
||||
public readonly struct PostDrawUIRootEventArgs
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.UserInterface;
|
||||
@@ -20,9 +21,10 @@ internal partial class UserInterfaceManager
|
||||
private bool _needUpdateActiveCursor;
|
||||
[ViewVariables] public Control? KeyboardFocused { get; private set; }
|
||||
|
||||
[ViewVariables] public Control? CurrentlyHovered { get; private set; } = default!;
|
||||
[ViewVariables] public Control? CurrentlyHovered { get; private set; }
|
||||
|
||||
private Control? _controlFocused;
|
||||
|
||||
[ViewVariables]
|
||||
public Control? ControlFocused
|
||||
{
|
||||
@@ -100,6 +102,7 @@ internal partial class UserInterfaceManager
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,
|
||||
args.PointerLocation.Position / control.UIScale - control.GlobalPosition,
|
||||
args.PointerLocation.Position - control.GlobalPixelPosition);
|
||||
@@ -111,16 +114,18 @@ internal partial class UserInterfaceManager
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
// Attempt to ensure that keybind-up events only get raised after a single keybind-down.
|
||||
DebugTools.Assert(!_focusedControls.ContainsKey(args.Function));
|
||||
_focusedControls[args.Function] = control;
|
||||
|
||||
OnKeyBindDown?.Invoke(control);
|
||||
}
|
||||
|
||||
public void KeyBindUp(BoundKeyEventArgs args)
|
||||
{
|
||||
if (!_focusedControls.TryGetValue(args.Function, out var control))
|
||||
{
|
||||
// Only raise keybind-up for the control on which we previously raised keybind-down
|
||||
if (!_focusedControls.Remove(args.Function, out var control) || control.Disposed)
|
||||
return;
|
||||
}
|
||||
|
||||
var guiArgs = new GUIBoundKeyEventArgs(args.Function, args.State, args.PointerLocation, args.CanFocus,
|
||||
args.PointerLocation.Position / control.UIScale - control.GlobalPosition,
|
||||
@@ -131,7 +136,6 @@ internal partial class UserInterfaceManager
|
||||
// Always mark this as handled.
|
||||
// The only case it should not be is if we do not have a control to click on,
|
||||
// in which case we never reach this.
|
||||
_focusedControls.Remove(args.Function);
|
||||
args.Handle();
|
||||
}
|
||||
|
||||
@@ -140,23 +144,7 @@ internal partial class UserInterfaceManager
|
||||
_resetTooltipTimer();
|
||||
// Update which control is considered hovered.
|
||||
var newHovered = MouseGetControl(mouseMoveEventArgs.Position);
|
||||
if (newHovered != CurrentlyHovered)
|
||||
{
|
||||
_clearTooltip();
|
||||
CurrentlyHovered?.MouseExited();
|
||||
CurrentlyHovered = newHovered;
|
||||
CurrentlyHovered?.MouseEntered();
|
||||
if (CurrentlyHovered != null)
|
||||
{
|
||||
_tooltipDelay = CurrentlyHovered.TooltipDelay ?? TooltipDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tooltipDelay = null;
|
||||
}
|
||||
|
||||
_needUpdateActiveCursor = true;
|
||||
}
|
||||
SetHovered(newHovered);
|
||||
|
||||
var target = ControlFocused ?? newHovered;
|
||||
if (target != null)
|
||||
@@ -172,6 +160,33 @@ internal partial class UserInterfaceManager
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateHovered()
|
||||
{
|
||||
var ctrl = MouseGetControl(_inputManager.MouseScreenPosition);
|
||||
SetHovered(ctrl);
|
||||
}
|
||||
|
||||
public void SetHovered(Control? control)
|
||||
{
|
||||
if (control == CurrentlyHovered)
|
||||
return;
|
||||
|
||||
_clearTooltip();
|
||||
CurrentlyHovered?.MouseExited();
|
||||
CurrentlyHovered = control;
|
||||
CurrentlyHovered?.MouseEntered();
|
||||
if (CurrentlyHovered != null)
|
||||
{
|
||||
_tooltipDelay = CurrentlyHovered.TooltipDelay ?? TooltipDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tooltipDelay = null;
|
||||
}
|
||||
|
||||
_needUpdateActiveCursor = true;
|
||||
}
|
||||
|
||||
private void UpdateActiveCursor()
|
||||
{
|
||||
// Consider mouse input focus first so that dragging windows don't act up etc.
|
||||
|
||||
@@ -77,15 +77,12 @@ internal sealed partial class UserInterfaceManager
|
||||
|
||||
ReleaseKeyboardFocus(control);
|
||||
RemoveModal(control);
|
||||
if (control == CurrentlyHovered)
|
||||
{
|
||||
control.MouseExited();
|
||||
CurrentlyHovered = null;
|
||||
_clearTooltip();
|
||||
}
|
||||
|
||||
if (control != ControlFocused) return;
|
||||
ControlFocused = null;
|
||||
if (control == ControlFocused)
|
||||
ControlFocused = null;
|
||||
|
||||
if (control == CurrentlyHovered)
|
||||
UpdateHovered();
|
||||
}
|
||||
|
||||
public void PushModal(Control modal)
|
||||
|
||||
@@ -42,6 +42,17 @@ public sealed partial class AudioSystem : SharedAudioSystem
|
||||
component.Source = new DummyAudioSource();
|
||||
}
|
||||
|
||||
public override void SetMapAudio(Entity<AudioComponent>? audio)
|
||||
{
|
||||
if (audio == null)
|
||||
return;
|
||||
|
||||
base.SetMapAudio(audio);
|
||||
|
||||
// Also need a global override because clients not near 0,0 won't get the audio.
|
||||
_pvs.AddGlobalOverride(audio.Value);
|
||||
}
|
||||
|
||||
private void AddAudioFilter(EntityUid uid, AudioComponent component, Filter filter)
|
||||
{
|
||||
var count = filter.Count;
|
||||
|
||||
@@ -55,13 +55,10 @@ internal sealed partial class PvsSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var root = (xform.GridUid ?? xform.MapUid);
|
||||
DebugTools.AssertNotNull(root);
|
||||
|
||||
if (xform.ParentUid != root)
|
||||
if (xform.ParentUid != xform.GridUid && xform.ParentUid != xform.MapUid)
|
||||
return;
|
||||
|
||||
var location = new PvsChunkLocation(root.Value, GetChunkIndices(xform._localPosition));
|
||||
var location = new PvsChunkLocation(xform.ParentUid, GetChunkIndices(xform._localPosition));
|
||||
if (meta.LastPvsLocation == location)
|
||||
return;
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
SubscribeLocalEvent<TransformComponent, TransformStartupEvent>(OnTransformStartup);
|
||||
|
||||
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
_transform.OnGlobalMoveEvent += OnEntityMove;
|
||||
_transform.OnBeforeMoveEvent += OnEntityMove;
|
||||
EntityManager.EntityAdded += OnEntityAdded;
|
||||
EntityManager.EntityDeleted += OnEntityDeleted;
|
||||
EntityManager.AfterEntityFlush += AfterEntityFlush;
|
||||
@@ -159,7 +159,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
base.Shutdown();
|
||||
|
||||
_playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||
_transform.OnGlobalMoveEvent -= OnEntityMove;
|
||||
_transform.OnBeforeMoveEvent -= OnEntityMove;
|
||||
EntityManager.EntityAdded -= OnEntityAdded;
|
||||
EntityManager.EntityDeleted -= OnEntityDeleted;
|
||||
EntityManager.AfterEntityFlush -= AfterEntityFlush;
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -32,11 +33,12 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
[Dependency] protected readonly IPrototypeManager ProtoMan = default!;
|
||||
[Dependency] protected readonly IRobustRandom RandMan = default!;
|
||||
[Dependency] protected readonly MetaDataSystem MetadataSys = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Default max range at which the sound can be heard.
|
||||
/// </summary>
|
||||
public const float DefaultSoundRange = 20;
|
||||
public const float DefaultSoundRange = 15;
|
||||
|
||||
/// <summary>
|
||||
/// Used in the PAS to designate the physics collision mask of occluders.
|
||||
@@ -131,6 +133,18 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
return (float) (Timing.CurTime - (component.PauseTime ?? TimeSpan.Zero) - component.AudioStart).TotalSeconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks this audio as being map-based.
|
||||
/// </summary>
|
||||
public virtual void SetMapAudio(Entity<AudioComponent>? audio)
|
||||
{
|
||||
if (audio == null)
|
||||
return;
|
||||
|
||||
audio.Value.Comp.Global = true;
|
||||
MetadataSys.AddFlag(audio.Value.Owner, MetaDataFlags.Undetachable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the shared state for an audio entity.
|
||||
/// </summary>
|
||||
|
||||
@@ -71,6 +71,8 @@ public abstract class ComponentTreeSystem<TTreeComp, TComp> : EntitySystem
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO EXCEPTION TOLERANCE
|
||||
// Ensure lookup trees update before content code handles move events.
|
||||
SubscribeLocalEvent<TComp, MoveEvent>(HandleMove);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
public override void Shutdown()
|
||||
{
|
||||
if (_subscribed)
|
||||
_transform.OnGlobalMoveEvent -= AnythingMoved;
|
||||
_transform.OnBeforeMoveEvent -= AnythingMoved;
|
||||
|
||||
_subscribed = false;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ internal sealed class RecursiveMoveSystem : EntitySystem
|
||||
return;
|
||||
|
||||
_subscribed = true;
|
||||
_transform.OnGlobalMoveEvent += AnythingMoved;
|
||||
_transform.OnBeforeMoveEvent += AnythingMoved;
|
||||
}
|
||||
|
||||
private void AnythingMoved(ref MoveEvent args)
|
||||
|
||||
@@ -770,7 +770,7 @@ Types:
|
||||
Array:
|
||||
Methods:
|
||||
- "!!0 Find<>(!!0[], System.Predicate`1<!!0>)"
|
||||
- "!!0 Resize<>(!!0[], int)"
|
||||
- "void Resize<>(ref !!0[], int)"
|
||||
- "!!1 ConvertAll<,>(!!0[], System.Converter`2<!!0, !!1>)"
|
||||
- "!!0[] Empty<>()"
|
||||
- "!!0[] FindAll<>(!!0[], System.Predicate`1<!!0>)"
|
||||
|
||||
@@ -157,9 +157,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (!Initialized)
|
||||
return;
|
||||
|
||||
var moveEvent = new MoveEvent((Owner, this, meta), Coordinates, Coordinates, oldRotation, _localRotation, _gameTiming.ApplyingState);
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, ref moveEvent);
|
||||
_entMan.System<SharedTransformSystem>().InvokeGlobalMoveEvent(ref moveEvent);
|
||||
_entMan.System<SharedTransformSystem>().RaiseMoveEvent((Owner, this, meta), _parent, _localPosition, oldRotation, MapUid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +332,9 @@ namespace Robust.Shared.GameObjects
|
||||
if (_localPosition.EqualsApprox(value))
|
||||
return;
|
||||
|
||||
var oldGridPos = Coordinates;
|
||||
var oldParent = _parent;
|
||||
var oldPos = _localPosition;
|
||||
|
||||
_localPosition = value;
|
||||
var meta = _entMan.GetComponent<MetaDataComponent>(Owner);
|
||||
_entMan.Dirty(Owner, this, meta);
|
||||
@@ -343,9 +343,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (!Initialized)
|
||||
return;
|
||||
|
||||
var moveEvent = new MoveEvent((Owner, this, meta), oldGridPos, Coordinates, _localRotation, _localRotation, _gameTiming.ApplyingState);
|
||||
_entMan.EventBus.RaiseLocalEvent(Owner, ref moveEvent);
|
||||
_entMan.System<SharedTransformSystem>().InvokeGlobalMoveEvent(ref moveEvent);
|
||||
_entMan.System<SharedTransformSystem>().RaiseMoveEvent((Owner, this, meta), oldParent, oldPos, _localRotation, MapUid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,8 +600,12 @@ namespace Robust.Shared.GameObjects
|
||||
/// move events, subscribe to the <see cref="SharedTransformSystem.OnGlobalMoveEvent"/>.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly struct MoveEvent(Entity<TransformComponent, MetaDataComponent> entity, EntityCoordinates oldPos,
|
||||
EntityCoordinates newPos, Angle oldRotation, Angle newRotation, bool stateHandling = false)
|
||||
public readonly struct MoveEvent(
|
||||
Entity<TransformComponent, MetaDataComponent> entity,
|
||||
EntityCoordinates oldPos,
|
||||
EntityCoordinates newPos,
|
||||
Angle oldRotation,
|
||||
Angle newRotation)
|
||||
{
|
||||
public readonly Entity<TransformComponent, MetaDataComponent> Entity = entity;
|
||||
public readonly EntityCoordinates OldPosition = oldPos;
|
||||
@@ -615,15 +617,6 @@ namespace Robust.Shared.GameObjects
|
||||
public TransformComponent Component => Entity.Comp1;
|
||||
|
||||
public bool ParentChanged => NewPosition.EntityId != OldPosition.EntityId;
|
||||
|
||||
[Obsolete("Check IGameTiming.ApplyingState")]
|
||||
public readonly bool FromStateHandling = stateHandling;
|
||||
|
||||
[Obsolete]
|
||||
public MoveEvent(EntityUid uid, EntityCoordinates oldPos, EntityCoordinates newPos, Angle oldRot, Angle newRot, TransformComponent xform, bool state)
|
||||
: this((uid, xform, default!), oldPos, newPos, oldRot, newRot)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public struct TransformChildrenEnumerator : IDisposable
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// The last received state object sent from the server.
|
||||
/// </summary>
|
||||
protected BoundUserInterfaceState? State { get; private set; }
|
||||
protected internal BoundUserInterfaceState? State { get; internal set; }
|
||||
|
||||
protected BoundUserInterface(EntityUid owner, Enum uiKey)
|
||||
{
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Robust.Shared.GameObjects
|
||||
public void InitializeComponents(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
DebugTools.AssertOwner(uid, metadata);
|
||||
metadata ??= GetComponent<MetaDataComponent>(uid);
|
||||
metadata ??= MetaQuery.GetComponent(uid);
|
||||
DebugTools.Assert(metadata.EntityLifeStage == EntityLifeStage.PreInit);
|
||||
SetLifeStage(metadata, EntityLifeStage.Initializing);
|
||||
|
||||
@@ -158,13 +158,12 @@ namespace Robust.Shared.GameObjects
|
||||
// TODO: please for the love of god remove these initialization order hacks.
|
||||
|
||||
// Init transform first, we always have it.
|
||||
var transform = GetComponent<TransformComponent>(uid);
|
||||
var transform = TransformQuery.GetComponent(uid);
|
||||
if (transform.LifeStage == ComponentLifeStage.Initialized)
|
||||
LifeStartup(transform);
|
||||
|
||||
// Init physics second if it exists.
|
||||
if (TryGetComponent<PhysicsComponent>(uid, out var phys)
|
||||
&& phys.LifeStage == ComponentLifeStage.Initialized)
|
||||
if (_physicsQuery.TryComp(uid, out var phys) && phys.LifeStage == ComponentLifeStage.Initialized)
|
||||
{
|
||||
LifeStartup(phys);
|
||||
}
|
||||
@@ -294,7 +293,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (!uid.IsValid() || !EntityExists(uid))
|
||||
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
|
||||
|
||||
AddComponentInternal(uid, newComponent, false, true);
|
||||
AddComponentInternal(uid, newComponent, false, true, null);
|
||||
|
||||
return new CompInitializeHandle<T>(this, uid, newComponent, reg.Idx);
|
||||
}
|
||||
@@ -302,10 +301,11 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public void AddComponent<T>(EntityUid uid, T component, bool overwrite = false, MetaDataComponent? metadata = null) where T : IComponent
|
||||
{
|
||||
if (!uid.IsValid() || !EntityExists(uid))
|
||||
if (!MetaQuery.Resolve(uid, ref metadata, false))
|
||||
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
|
||||
|
||||
if (component == null) throw new ArgumentNullException(nameof(component));
|
||||
if (component == null)
|
||||
throw new ArgumentNullException(nameof(component));
|
||||
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (component.Owner == default)
|
||||
@@ -321,14 +321,17 @@ namespace Robust.Shared.GameObjects
|
||||
AddComponentInternal(uid, component, overwrite, false, metadata);
|
||||
}
|
||||
|
||||
private void AddComponentInternal<T>(EntityUid uid, T component, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent
|
||||
private void AddComponentInternal<T>(EntityUid uid, T component, bool overwrite, bool skipInit, MetaDataComponent? metadata) where T : IComponent
|
||||
{
|
||||
if (!MetaQuery.ResolveInternal(uid, ref metadata, false))
|
||||
throw new ArgumentException($"Entity {uid} is not valid.", nameof(uid));
|
||||
|
||||
// get interface aliases for mapping
|
||||
var reg = _componentFactory.GetRegistration(component);
|
||||
AddComponentInternal(uid, component, reg, overwrite, skipInit, metadata);
|
||||
}
|
||||
|
||||
private void AddComponentInternal<T>(EntityUid uid, T component, ComponentRegistration reg, bool overwrite, bool skipInit, MetaDataComponent? metadata = null) where T : IComponent
|
||||
private void AddComponentInternal<T>(EntityUid uid, T component, ComponentRegistration reg, bool overwrite, bool skipInit, MetaDataComponent metadata) where T : IComponent
|
||||
{
|
||||
// We can't use typeof(T) here in case T is just Component
|
||||
DebugTools.Assert(component is MetaDataComponent ||
|
||||
@@ -642,13 +645,14 @@ namespace Robust.Shared.GameObjects
|
||||
_runtimeLog.LogException(e, nameof(CullRemovedComponents));
|
||||
}
|
||||
#endif
|
||||
DeleteComponent(uid, component, false);
|
||||
var meta = MetaQuery.GetComponent(uid);
|
||||
DeleteComponent(uid, component, false, meta);
|
||||
}
|
||||
|
||||
_deleteSet.Clear();
|
||||
}
|
||||
|
||||
private void DeleteComponent(EntityUid entityUid, IComponent component, bool terminating, MetaDataComponent? metadata = null)
|
||||
private void DeleteComponent(EntityUid entityUid, IComponent component, bool terminating, MetaDataComponent? metadata)
|
||||
{
|
||||
if (!MetaQuery.ResolveInternal(entityUid, ref metadata))
|
||||
return;
|
||||
@@ -1519,7 +1523,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[Pure]
|
||||
public bool TryComp(EntityUid? uid, [NotNullWhen(true)] out TComp1? component)
|
||||
public bool TryComp([NotNullWhen(true)] EntityUid? uid, [NotNullWhen(true)] out TComp1? component)
|
||||
=> TryGetComponent(uid, out component);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@@ -11,6 +11,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -47,6 +48,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public EntityQuery<MetaDataComponent> MetaQuery;
|
||||
public EntityQuery<TransformComponent> TransformQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<ActorComponent> _actorQuery;
|
||||
|
||||
#endregion Dependencies
|
||||
@@ -210,6 +212,7 @@ namespace Robust.Shared.GameObjects
|
||||
_containers = System<SharedContainerSystem>();
|
||||
MetaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
TransformQuery = GetEntityQuery<TransformComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
_actorQuery = GetEntityQuery<ActorComponent>();
|
||||
}
|
||||
|
||||
@@ -551,17 +554,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
// Detach the base entity to null before iterating over children
|
||||
// This also ensures that the entity-lookup updates don't have to be re-run for every child (which recurses up the transform hierarchy).
|
||||
if (transform.ParentUid != EntityUid.Invalid)
|
||||
{
|
||||
try
|
||||
{
|
||||
_xforms.DetachParentToNull((uid, transform, metadata), parentXform, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Caught exception while trying to detach parent of entity '{ToPrettyString(uid, metadata)}' to null.\n{e}");
|
||||
}
|
||||
}
|
||||
_xforms.DetachEntity(uid, transform, metadata, parentXform, true);
|
||||
|
||||
foreach (var child in transform._children)
|
||||
{
|
||||
|
||||
@@ -19,26 +19,26 @@ namespace Robust.Shared.GameObjects
|
||||
public EntityUid? OldParent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The map Id that the entity was on before its parent changed.
|
||||
/// The map that the entity was on before its parent changed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the old parent was detached to null without manually updating the map ID of its children, then this
|
||||
/// is required as we cannot simply use the old parent's map ID. Also avoids having to fetch the old
|
||||
/// parent's transform component.
|
||||
/// </remarks>
|
||||
public MapId OldMapId { get; }
|
||||
public readonly EntityUid? OldMapId;
|
||||
|
||||
public TransformComponent Transform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="EntParentChangedMessage"/>.
|
||||
/// </summary>
|
||||
public EntParentChangedMessage(EntityUid entity, EntityUid? oldParent, MapId oldMapId, TransformComponent xform)
|
||||
public EntParentChangedMessage(EntityUid entity, EntityUid? oldParent, EntityUid? oldMapId, TransformComponent xform)
|
||||
{
|
||||
Entity = entity;
|
||||
OldParent = oldParent;
|
||||
OldMapId = oldMapId;
|
||||
Transform = xform;
|
||||
OldMapId = oldMapId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<MapChangedEvent>(OnMapChange);
|
||||
|
||||
_transform.OnGlobalMoveEvent += OnMove;
|
||||
_transform.OnBeforeMoveEvent += OnMove;
|
||||
EntityManager.EntityInitialized += OnEntityInit;
|
||||
|
||||
SubscribeLocalEvent<TransformComponent, PhysicsBodyTypeChangedEvent>(OnBodyTypeChange);
|
||||
@@ -142,7 +142,7 @@ public sealed partial class EntityLookupSystem : EntitySystem
|
||||
{
|
||||
base.Shutdown();
|
||||
EntityManager.EntityInitialized -= OnEntityInit;
|
||||
_transform.OnGlobalMoveEvent -= OnMove;
|
||||
_transform.OnBeforeMoveEvent -= OnMove;
|
||||
}
|
||||
|
||||
#region DynamicTree
|
||||
|
||||
@@ -26,7 +26,6 @@ internal sealed class SharedGridTraversalSystem : EntitySystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<TransformStartupEvent>(OnStartup);
|
||||
_transform.OnGlobalMoveEvent += OnMove;
|
||||
}
|
||||
|
||||
private void OnStartup(ref TransformStartupEvent ev)
|
||||
@@ -34,17 +33,6 @@ internal sealed class SharedGridTraversalSystem : EntitySystem
|
||||
CheckTraverse(ev.Entity.Owner, ev.Entity.Comp);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
_transform.OnGlobalMoveEvent -= OnMove;
|
||||
}
|
||||
|
||||
private void OnMove(ref MoveEvent moveEv)
|
||||
{
|
||||
CheckTraverse(moveEv.Sender, moveEv.Component);
|
||||
}
|
||||
|
||||
|
||||
internal void CheckTraverse(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
if (!Enabled || _timing.ApplyingState)
|
||||
|
||||
@@ -145,6 +145,11 @@ namespace Robust.Shared.GameObjects
|
||||
ChunkIndex = chunkIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Was the tile previously empty or is it now empty.
|
||||
/// </summary>
|
||||
public bool EmptyChanged => OldTile.IsEmpty != NewTile.Tile.IsEmpty;
|
||||
|
||||
/// <summary>
|
||||
/// EntityUid of the grid with the tile-change. TileRef stores the GridId.
|
||||
/// </summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Map.Components;
|
||||
@@ -43,21 +44,13 @@ public abstract partial class SharedTransformSystem
|
||||
xform._anchored = true;
|
||||
var oldPos = xform._localPosition;
|
||||
var oldRot = xform._localRotation;
|
||||
var oldMap = xform.MapUid;
|
||||
xform._localPosition = tilePos + newGrid.TileSizeHalfVector;
|
||||
xform._localRotation += rotation;
|
||||
|
||||
SetGridId(uid, xform, newGridUid, XformQuery);
|
||||
var reParent = new EntParentChangedMessage(uid, oldGridUid, xform.MapID, xform);
|
||||
RaiseLocalEvent(uid, ref reParent, true);
|
||||
var meta = MetaData(uid);
|
||||
var movEevee = new MoveEvent((uid, xform, meta),
|
||||
new EntityCoordinates(oldGridUid, oldPos),
|
||||
new EntityCoordinates(newGridUid, xform._localPosition),
|
||||
oldRot,
|
||||
xform.LocalRotation,
|
||||
_gameTiming.ApplyingState);
|
||||
RaiseLocalEvent(uid, ref movEevee);
|
||||
InvokeGlobalMoveEvent(ref movEevee);
|
||||
RaiseMoveEvent((uid, xform, meta), oldGridUid, oldPos, oldRot, oldMap);
|
||||
|
||||
DebugTools.Assert(XformQuery.GetComponent(oldGridUid).MapID == XformQuery.GetComponent(newGridUid).MapID);
|
||||
DebugTools.Assert(xform._anchored);
|
||||
@@ -321,7 +314,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
// I hate this too. Once again, required for shit like containers because they CBF doing their own init logic
|
||||
// and rely on parent changed messages instead. Might also be used by broadphase stuff?
|
||||
var parentEv = new EntParentChangedMessage(uid, null, MapId.Nullspace, xform);
|
||||
var parentEv = new EntParentChangedMessage(uid, null, null, xform);
|
||||
RaiseLocalEvent(uid, ref parentEv, true);
|
||||
|
||||
var ev = new TransformStartupEvent((uid, xform));
|
||||
@@ -449,9 +442,6 @@ public abstract partial class SharedTransformSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var oldPosition = xform._parent.IsValid() ? new EntityCoordinates(xform._parent, xform._localPosition) : default;
|
||||
var oldRotation = xform._localRotation;
|
||||
|
||||
if (xform.Anchored && unanchor)
|
||||
Unanchor(uid, xform);
|
||||
|
||||
@@ -470,6 +460,11 @@ public abstract partial class SharedTransformSystem
|
||||
}
|
||||
}
|
||||
|
||||
var oldParentUid = xform._parent;
|
||||
var oldPosition = xform._localPosition;
|
||||
var oldRotation = xform._localRotation;
|
||||
var oldMap = xform.MapUid;
|
||||
|
||||
// Set new values
|
||||
Dirty(uid, xform, meta);
|
||||
xform.MatricesDirty = true;
|
||||
@@ -485,7 +480,7 @@ public abstract partial class SharedTransformSystem
|
||||
{
|
||||
if (value.EntityId == uid)
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
DetachEntity(uid, xform);
|
||||
if (_netMan.IsServer || IsClientSide(uid))
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to parent an entity to itself: {ToPrettyString(uid)}");
|
||||
@@ -495,7 +490,7 @@ public abstract partial class SharedTransformSystem
|
||||
{
|
||||
if (!XformQuery.Resolve(value.EntityId, ref newParent, false))
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
DetachEntity(uid, xform);
|
||||
if (_netMan.IsServer || IsClientSide(uid))
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to parent entity {ToPrettyString(uid)} to non-existent entity {value.EntityId}");
|
||||
@@ -503,7 +498,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
if (newParent.LifeStage >= ComponentLifeStage.Stopping || LifeStage(value.EntityId) >= EntityLifeStage.Terminating)
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
DetachEntity(uid, xform);
|
||||
if (_netMan.IsServer || IsClientSide(uid))
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(uid)}, new parent: {ToPrettyString(value.EntityId)}");
|
||||
@@ -528,7 +523,7 @@ public abstract partial class SharedTransformSystem
|
||||
// Even though its temporary, this can still cause the client to get stuck in infinite loops while applying the game state.
|
||||
// So we will just break the loop by detaching to null and just trusting that the loop wasn't actually a real feature of the server state.
|
||||
Log.Warning($"Encountered circular transform hierarchy while applying state for entity: {ToPrettyString(uid)}. Detaching child to null: {ToPrettyString(recursiveUid)}");
|
||||
DetachParentToNull(recursiveUid, recursiveXform);
|
||||
DetachEntity(recursiveUid, recursiveXform);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -545,7 +540,6 @@ public abstract partial class SharedTransformSystem
|
||||
newParent?._children.Add(uid);
|
||||
|
||||
xform._parent = value.EntityId;
|
||||
var oldMapId = xform.MapID;
|
||||
|
||||
if (newParent != null)
|
||||
{
|
||||
@@ -576,24 +570,18 @@ public abstract partial class SharedTransformSystem
|
||||
xform._localRotation += GetWorldRotation(oldParent) - GetWorldRotation(newParent);
|
||||
|
||||
DebugTools.Assert(!xform.NoLocalRotation || xform.LocalRotation == 0);
|
||||
|
||||
var entParentChangedMessage = new EntParentChangedMessage(uid, oldParent?.Owner, oldMapId, xform);
|
||||
RaiseLocalEvent(uid, ref entParentChangedMessage, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!xform.Initialized)
|
||||
return;
|
||||
|
||||
var newPosition = xform._parent.IsValid() ? new EntityCoordinates(xform._parent, xform._localPosition) : default;
|
||||
#if DEBUG
|
||||
// If an entity is parented to the map, its grid uid should be null (unless it is itself a grid or we have a map-grid)
|
||||
if (xform.ParentUid == xform.MapUid)
|
||||
DebugTools.Assert(xform.GridUid == null || xform.GridUid == uid || xform.GridUid == xform.MapUid);
|
||||
#endif
|
||||
var moveEvent = new MoveEvent((uid, xform, meta), oldPosition, newPosition, oldRotation, xform._localRotation, _gameTiming.ApplyingState);
|
||||
RaiseLocalEvent(uid, ref moveEvent);
|
||||
InvokeGlobalMoveEvent(ref moveEvent);
|
||||
RaiseMoveEvent(entity, oldParentUid, oldPosition, oldRotation, oldMap);
|
||||
}
|
||||
|
||||
public void SetCoordinates(
|
||||
@@ -668,13 +656,13 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public void SetParent(EntityUid uid, TransformComponent xform, EntityUid parent, EntityQuery<TransformComponent> xformQuery, TransformComponent? parentXform = null)
|
||||
{
|
||||
DebugTools.Assert(uid == xform.Owner);
|
||||
DebugTools.AssertOwner(uid, xform);
|
||||
if (xform.ParentUid == parent)
|
||||
return;
|
||||
|
||||
if (!parent.IsValid())
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
DetachEntity(uid, xform);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -976,7 +964,7 @@ public abstract partial class SharedTransformSystem
|
||||
// Entity was not actually in the transform hierarchy. This is probably a sign that something is wrong, or that the function is being misused.
|
||||
Log.Warning($"Target entity ({ToPrettyString(relative)}) not in transform hierarchy while calling {nameof(GetRelativePositionRotation)}.");
|
||||
var relXform = query.GetComponent(relative);
|
||||
pos = relXform.InvWorldMatrix.Transform(pos);
|
||||
pos = GetInvWorldMatrix(relXform).Transform(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -990,7 +978,6 @@ public abstract partial class SharedTransformSystem
|
||||
SetWorldPosition(xform, worldPos);
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetWorldPosition(TransformComponent component, Vector2 worldPos)
|
||||
{
|
||||
@@ -1048,6 +1035,16 @@ public abstract partial class SharedTransformSystem
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public void SetWorldRotationNoLerp(Entity<TransformComponent?> entity, Angle angle)
|
||||
{
|
||||
if (!XformQuery.Resolve(entity.Owner, ref entity.Comp))
|
||||
return;
|
||||
|
||||
var current = GetWorldRotation(entity.Comp);
|
||||
var diff = angle - current;
|
||||
SetLocalRotationNoLerp(entity, entity.Comp.LocalRotation + diff);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SetWorldRotation(EntityUid uid, Angle angle)
|
||||
{
|
||||
@@ -1143,7 +1140,8 @@ public abstract partial class SharedTransformSystem
|
||||
if (xform._localPosition.EqualsApprox(pos) && xform.LocalRotation.EqualsApprox(rot))
|
||||
return;
|
||||
|
||||
var oldPosition = xform.Coordinates;
|
||||
var oldParent = xform._parent;
|
||||
var oldPosition = xform._localPosition;
|
||||
var oldRotation = xform.LocalRotation;
|
||||
|
||||
if (!xform.Anchored)
|
||||
@@ -1161,9 +1159,7 @@ public abstract partial class SharedTransformSystem
|
||||
if (!xform.Initialized)
|
||||
return;
|
||||
|
||||
var moveEvent = new MoveEvent((uid, xform, meta), oldPosition, xform.Coordinates, oldRotation, rot, _gameTiming.ApplyingState);
|
||||
RaiseLocalEvent(uid, ref moveEvent);
|
||||
InvokeGlobalMoveEvent(ref moveEvent);
|
||||
RaiseMoveEvent((uid, xform, meta), oldParent, oldPosition, oldRotation, xform.MapUid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -1326,7 +1322,7 @@ public abstract partial class SharedTransformSystem
|
||||
if (!_mapManager.IsMap(uid))
|
||||
Log.Warning($"Failed to attach entity to map or grid. Entity: ({ToPrettyString(uid)}). Trace: {Environment.StackTrace}");
|
||||
|
||||
DetachParentToNull(uid, xform);
|
||||
DetachEntity(uid, xform);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1362,21 +1358,50 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
#region State Handling
|
||||
|
||||
[Obsolete("Use DetachEntity")]
|
||||
public void DetachParentToNull(EntityUid uid, TransformComponent xform)
|
||||
=> DetachEntity(uid, xform);
|
||||
|
||||
/// <inheritdoc cref="DetachEntityInternal"/>
|
||||
public void DetachEntity(EntityUid uid, TransformComponent xform)
|
||||
{
|
||||
XformQuery.TryGetComponent(xform.ParentUid, out var oldXform);
|
||||
DetachParentToNull(uid, xform, oldXform);
|
||||
DetachEntity(uid, xform, MetaData(uid), oldXform);
|
||||
}
|
||||
|
||||
public void DetachParentToNull(EntityUid uid, TransformComponent xform, TransformComponent? oldXform)
|
||||
/// <inheritdoc cref="DetachEntityInternal"/>
|
||||
public void DetachEntity(
|
||||
EntityUid uid,
|
||||
TransformComponent xform,
|
||||
MetaDataComponent meta,
|
||||
TransformComponent? oldXform,
|
||||
bool terminating = false)
|
||||
{
|
||||
DetachParentToNull((uid, xform, MetaData(uid)), oldXform);
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
DetachEntityInternal(uid, xform, meta, oldXform, terminating);
|
||||
#else
|
||||
try
|
||||
{
|
||||
DetachEntityInternal(uid, xform, meta, oldXform, terminating);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"Caught exception while attempting to detach an entity to nullspace. Entity: {ToPrettyString(uid, meta)}. Exception: {e}");
|
||||
// TODO detach without content event handling.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public void DetachParentToNull(Entity<TransformComponent,MetaDataComponent> entity, TransformComponent? oldXform, bool terminating = false)
|
||||
/// <summary>
|
||||
/// Remove an entity from the transform hierarchy and send it to null space
|
||||
/// </summary>
|
||||
internal void DetachEntityInternal(
|
||||
EntityUid uid,
|
||||
TransformComponent xform,
|
||||
MetaDataComponent meta,
|
||||
TransformComponent? oldXform,
|
||||
bool terminating = false)
|
||||
{
|
||||
var (uid, xform, meta) = entity;
|
||||
|
||||
if (!terminating && meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
// Something is attempting to remove the entity from this entity's parent while it is in the process of being deleted.
|
||||
@@ -1393,15 +1418,14 @@ public abstract partial class SharedTransformSystem
|
||||
DebugTools.Assert((MetaData(uid).Flags & MetaDataFlags.InContainer) == 0x0,
|
||||
$"Entity is in a container but has no parent? Entity: {ToPrettyString(uid)}");
|
||||
|
||||
if (xform.Broadphase != null)
|
||||
{
|
||||
DebugTools.Assert(
|
||||
xform.Broadphase == BroadphaseData.Invalid
|
||||
|| xform.Broadphase.Value.Uid == uid
|
||||
|| Deleted(xform.Broadphase.Value.Uid)
|
||||
|| Terminating(xform.Broadphase.Value.Uid),
|
||||
$"Entity has no parent but is on some broadphase? Entity: {ToPrettyString(uid)}. Broadphase: {ToPrettyString(xform.Broadphase.Value.Uid)}");
|
||||
}
|
||||
DebugTools.Assert(
|
||||
xform.Broadphase == null
|
||||
|| xform.Broadphase == BroadphaseData.Invalid
|
||||
|| xform.Broadphase.Value.Uid == uid
|
||||
|| Deleted(xform.Broadphase.Value.Uid)
|
||||
|| Terminating(xform.Broadphase.Value.Uid),
|
||||
$"Entity has no parent but is on some broadphase? Entity: {ToPrettyString(uid)}. Broadphase: {ToPrettyString(xform.Broadphase!.Value.Uid)}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1425,7 +1449,7 @@ public abstract partial class SharedTransformSystem
|
||||
RaiseLocalEvent(uid, ref anchorStateChangedEvent, true);
|
||||
}
|
||||
|
||||
SetCoordinates(entity, default, Angle.Zero, oldParent: oldXform);
|
||||
SetCoordinates((uid, xform, meta), default, Angle.Zero, oldParent: oldXform);
|
||||
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0x0,
|
||||
$"Entity is in a container after having been detached to null-space? Entity: {ToPrettyString(uid)}");
|
||||
@@ -1460,7 +1484,7 @@ public abstract partial class SharedTransformSystem
|
||||
var targetXform = target.Comp;
|
||||
if (!XformQuery.Resolve(target, ref targetXform) || !targetXform.ParentUid.IsValid())
|
||||
{
|
||||
DetachParentToNull(entity, xform);
|
||||
DetachEntity(entity, xform);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1498,7 +1522,7 @@ public abstract partial class SharedTransformSystem
|
||||
var targetXform = target.Comp;
|
||||
if (!XformQuery.Resolve(target, ref targetXform) || !targetXform.ParentUid.IsValid())
|
||||
{
|
||||
DetachParentToNull(entity, xform);
|
||||
DetachEntity(entity, xform);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1518,4 +1542,90 @@ public abstract partial class SharedTransformSystem
|
||||
PlaceNextTo((entity, xform), targetXform.ParentUid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swaps the position of two entities, placing them inside of containers when applicable.
|
||||
/// </summary>
|
||||
/// <returns>Returns if the entities can have their positions swapped. Fails if the entities are parented to one another</returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public bool SwapPositions(Entity<TransformComponent?> entity1, Entity<TransformComponent?> entity2)
|
||||
{
|
||||
if (!XformQuery.Resolve(entity1, ref entity1.Comp) || !XformQuery.Resolve(entity2, ref entity2.Comp))
|
||||
return false;
|
||||
|
||||
// save ourselves the hassle and just don't move anything.
|
||||
if (entity1 == entity2)
|
||||
return true;
|
||||
|
||||
// don't parent things to each other by accident
|
||||
if (IsParentOf(entity1.Comp, entity2) || IsParentOf(entity2.Comp, entity1))
|
||||
return false;
|
||||
|
||||
MapCoordinates? pos1 = null;
|
||||
MapCoordinates? pos2 = null;
|
||||
|
||||
if (_container.TryGetContainingContainer(entity1, out var container1))
|
||||
_container.Remove(entity1, container1, force: true);
|
||||
else
|
||||
pos1 = GetMapCoordinates(entity1.Comp);
|
||||
|
||||
if (_container.TryGetContainingContainer(entity2, out var container2))
|
||||
_container.Remove(entity2, container2, force: true);
|
||||
else
|
||||
pos2 = GetMapCoordinates(entity2.Comp);
|
||||
|
||||
// making sure we don't accidentally place something inside of itself
|
||||
if (container1 != null && container1.Owner == entity2.Owner)
|
||||
return false;
|
||||
if (container2 != null && container2.Owner == entity1.Owner)
|
||||
return false;
|
||||
|
||||
if (container2 != null)
|
||||
{
|
||||
_container.Insert(entity1, container2);
|
||||
}
|
||||
else if (pos2 != null)
|
||||
{
|
||||
var mapUid = _mapManager.GetMapEntityId(pos2.Value.MapId);
|
||||
|
||||
if (!_gridQuery.HasComponent(entity1) && _mapManager.TryFindGridAt(mapUid, pos2.Value.Position, out var targetGrid, out _))
|
||||
{
|
||||
var invWorldMatrix = GetInvWorldMatrix(targetGrid);
|
||||
SetCoordinates(entity1, new EntityCoordinates(targetGrid, invWorldMatrix.Transform(pos2.Value.Position)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCoordinates(entity1, new EntityCoordinates(mapUid, pos2.Value.Position));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
if (container1 != null)
|
||||
{
|
||||
_container.Insert(entity2, container1);
|
||||
}
|
||||
else if (pos1 != null)
|
||||
{
|
||||
var mapUid = _mapManager.GetMapEntityId(pos1.Value.MapId);
|
||||
|
||||
if (!_gridQuery.HasComponent(entity1) && _mapManager.TryFindGridAt(mapUid, pos1.Value.Position, out var targetGrid, out _))
|
||||
{
|
||||
var invWorldMatrix = GetInvWorldMatrix(targetGrid);
|
||||
SetCoordinates(entity2, new EntityCoordinates(targetGrid, invWorldMatrix.Transform(pos1.Value.Position)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCoordinates(entity2, new EntityCoordinates(mapUid, pos1.Value.Position));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Robust.Shared.GameObjects
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedGridTraversalSystem _traversal = default!;
|
||||
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
@@ -40,10 +41,12 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public event MoveEventHandler? OnGlobalMoveEvent;
|
||||
|
||||
public void InvokeGlobalMoveEvent(ref MoveEvent ev)
|
||||
{
|
||||
OnGlobalMoveEvent?.Invoke(ref ev);
|
||||
}
|
||||
/// <summary>
|
||||
/// Internal move event handlers. This gets invoked before the global & directed move events. This is mainly
|
||||
/// for exception tolerance, we want to ensure that PVS, physics & entity lookups get updated before some
|
||||
/// content code throws an exception.
|
||||
/// </summary>
|
||||
internal event MoveEventHandler? OnBeforeMoveEvent;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -104,7 +107,7 @@ namespace Robust.Shared.GameObjects
|
||||
// If a tile is being removed due to an explosion or somesuch, some entities are likely being deleted.
|
||||
// Avoid unnecessary entity updates.
|
||||
if (EntityManager.IsQueuedForDeletion(entity))
|
||||
DetachParentToNull(entity, xform, gridXform);
|
||||
DetachEntity(entity, xform, MetaData(entity), gridXform);
|
||||
else
|
||||
SetParent(entity, xform, gridXform.MapUid.Value, mapTransform);
|
||||
}
|
||||
@@ -255,6 +258,44 @@ namespace Robust.Shared.GameObjects
|
||||
indices = _map.CoordinatesToTile(xform.GridUid.Value, grid, xform.Coordinates);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RaiseMoveEvent(
|
||||
Entity<TransformComponent, MetaDataComponent> ent,
|
||||
EntityUid oldParent,
|
||||
Vector2 oldPosition,
|
||||
Angle oldRotation,
|
||||
EntityUid? oldMap)
|
||||
{
|
||||
var pos = ent.Comp1._parent == EntityUid.Invalid
|
||||
? default
|
||||
: new EntityCoordinates(ent.Comp1._parent, ent.Comp1._localPosition);
|
||||
|
||||
var oldPos = oldParent == EntityUid.Invalid
|
||||
? default
|
||||
: new EntityCoordinates(oldParent, oldPosition);
|
||||
|
||||
var ev = new MoveEvent(ent, oldPos, pos, oldRotation, ent.Comp1._localRotation);
|
||||
|
||||
if (oldParent != ent.Comp1._parent)
|
||||
{
|
||||
_physics.OnParentChange(ent, oldParent, oldMap);
|
||||
OnBeforeMoveEvent?.Invoke(ref ev);
|
||||
var entParentChangedMessage = new EntParentChangedMessage(ev.Sender, oldParent, oldMap, ev.Component);
|
||||
RaiseLocalEvent(ev.Sender, ref entParentChangedMessage, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnBeforeMoveEvent?.Invoke(ref ev);
|
||||
}
|
||||
|
||||
RaiseLocalEvent(ev.Sender, ref ev);
|
||||
OnGlobalMoveEvent?.Invoke(ref ev);
|
||||
|
||||
// Finally, handle grid traversal. This is handled separately to avoid out-of-order move events.
|
||||
// I.e., if the traversal raises its own move event, this ensures that all the old move event handlers
|
||||
// have finished running first. Ideally this shouldn't be required, but this is here just in case
|
||||
_traversal.CheckTraverse(ent.Owner, ent.Comp1);
|
||||
}
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Collections;
|
||||
@@ -388,6 +387,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
|
||||
if (!ent.Comp.ClientOpenInterfaces.TryGetValue(key, out var cBui))
|
||||
continue;
|
||||
|
||||
cBui.State = buiState;
|
||||
cBui.UpdateState(buiState);
|
||||
}
|
||||
|
||||
@@ -433,6 +433,7 @@ public abstract class SharedUserInterfaceSystem : EntitySystem
|
||||
|
||||
if (entity.Comp.States.TryGetValue(key, out var buiState))
|
||||
{
|
||||
boundUserInterface.State = buiState;
|
||||
boundUserInterface.UpdateState(buiState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private EntityQuery<JointComponent> _jointsQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
private EntityQuery<JointRelayTargetComponent> _relayQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
@@ -37,6 +38,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
_jointsQuery = GetEntityQuery<JointComponent>();
|
||||
_relayQuery = GetEntityQuery<JointRelayTargetComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
UpdatesBefore.Add(typeof(SharedPhysicsSystem));
|
||||
@@ -136,7 +138,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
var aUid = joint.BodyAUid;
|
||||
var bUid = joint.BodyBUid;
|
||||
|
||||
if (!Resolve(aUid, ref bodyA, false) || !Resolve(bUid, ref bodyB, false))
|
||||
if (!_physicsQuery.Resolve(aUid, ref bodyA, false) || !_physicsQuery.Resolve(bUid, ref bodyB, false))
|
||||
return;
|
||||
|
||||
DebugTools.Assert(Transform(aUid).MapID == Transform(bUid).MapID, "Attempted to initialize cross-map joint");
|
||||
@@ -311,7 +313,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
public WeldJoint GetOrCreateWeldJoint(EntityUid bodyA, EntityUid bodyB, string? id = null)
|
||||
{
|
||||
if (id != null &&
|
||||
EntityManager.TryGetComponent(bodyA, out JointComponent? jointComponent) &&
|
||||
_jointsQuery.TryComp(bodyA, out JointComponent? jointComponent) &&
|
||||
jointComponent.Joints.TryGetValue(id, out var weldJoint))
|
||||
{
|
||||
return (WeldJoint) weldJoint;
|
||||
@@ -404,17 +406,17 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
|
||||
protected void AddJoint(Joint joint, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null)
|
||||
{
|
||||
if (!Resolve(joint.BodyAUid, ref bodyA) || !Resolve(joint.BodyBUid, ref bodyB))
|
||||
if (!_physicsQuery.Resolve(joint.BodyAUid, ref bodyA) || !_physicsQuery.Resolve(joint.BodyBUid, ref bodyB))
|
||||
return;
|
||||
|
||||
if (!joint.CollideConnected)
|
||||
FilterContactsForJoint(joint, bodyA, bodyB);
|
||||
|
||||
// Maybe make this method AddOrUpdate so we can have an Add one that explicitly throws if present?
|
||||
var mapidA = EntityManager.GetComponent<TransformComponent>(joint.BodyAUid).MapID;
|
||||
var mapidA = Transform(joint.BodyAUid).MapID;
|
||||
|
||||
if (mapidA == MapId.Nullspace ||
|
||||
mapidA != EntityManager.GetComponent<TransformComponent>(joint.BodyBUid).MapID)
|
||||
mapidA != Transform(joint.BodyBUid).MapID)
|
||||
{
|
||||
Log.Error($"Tried to add joint to ineligible bodies");
|
||||
return;
|
||||
@@ -447,7 +449,8 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
if (!Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
Resolve(uid, ref component, ref relay, false);
|
||||
_jointsQuery.Resolve(uid, ref component, false);
|
||||
_relayQuery.Resolve(uid, ref relay, false);
|
||||
|
||||
if (relay != null)
|
||||
{
|
||||
@@ -471,7 +474,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
/// </summary>
|
||||
public void ClearJoints(EntityUid uid, JointComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
if (!_jointsQuery.Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
// TODO PERFORMANCE
|
||||
@@ -497,15 +500,9 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use the other ClearJoints overload")]
|
||||
public void ClearJoints(JointComponent joint)
|
||||
{
|
||||
ClearJoints(joint.Owner, joint);
|
||||
}
|
||||
|
||||
public void RemoveJoint(EntityUid uid, string id)
|
||||
{
|
||||
if (!TryComp<JointComponent>(uid, out var jointComp))
|
||||
if (!_jointsQuery.TryComp(uid, out var jointComp))
|
||||
return;
|
||||
|
||||
if (!jointComp.Joints.TryGetValue(id, out var joint))
|
||||
@@ -522,12 +519,12 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
|
||||
// Originally I logged these but because of prediction the client can just nuke them multiple times in a row
|
||||
// because each body has its own JointComponent, bleh.
|
||||
if (!TryComp<JointComponent>(bodyAUid, out var jointComponentA))
|
||||
if (!_jointsQuery.TryComp(bodyAUid, out var jointComponentA))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<JointComponent>(bodyBUid, out var jointComponentB))
|
||||
if (!_jointsQuery.TryComp(bodyBUid, out var jointComponentB))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -543,7 +540,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
}
|
||||
|
||||
// Wake up connected bodies.
|
||||
if (EntityManager.TryGetComponent<PhysicsComponent>(bodyAUid, out var bodyA) &&
|
||||
if (_physicsQuery.TryComp(bodyAUid, out var bodyA) &&
|
||||
MetaData(bodyAUid).EntityLifeStage < EntityLifeStage.Terminating)
|
||||
{
|
||||
var uidA = jointComponentA.Relay ?? bodyAUid;
|
||||
@@ -607,7 +604,7 @@ public abstract partial class SharedJointSystem : EntitySystem
|
||||
|
||||
internal void FilterContactsForJoint(Joint joint, PhysicsComponent? bodyA = null, PhysicsComponent? bodyB = null)
|
||||
{
|
||||
if (!Resolve(joint.BodyBUid, ref bodyB))
|
||||
if (!_physicsQuery.Resolve(joint.BodyBUid, ref bodyB))
|
||||
return;
|
||||
|
||||
var node = bodyB.Contacts.First;
|
||||
|
||||
@@ -115,7 +115,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ApplyAngularImpulse(EntityUid uid, float impulse, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ApplyForce(EntityUid uid, Vector2 force, Vector2 point, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -137,7 +137,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ApplyForce(EntityUid uid, Vector2 force, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ApplyTorque(EntityUid uid, float torque, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -159,7 +159,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -169,7 +169,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ApplyLinearImpulse(EntityUid uid, Vector2 impulse, Vector2 point, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || !IsMoveable(body) || !WakeBody(uid, manager: manager, body: body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -250,7 +250,10 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void ResetMassData(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref manager, ref body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body))
|
||||
return;
|
||||
|
||||
if (!_fixturesQuery.Resolve(uid, ref manager))
|
||||
return;
|
||||
|
||||
body._mass = 0.0f;
|
||||
@@ -315,7 +318,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public bool SetAngularVelocity(EntityUid uid, float value, bool dirty = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body))
|
||||
return false;
|
||||
|
||||
if (body.BodyType == BodyType.Static)
|
||||
@@ -346,7 +349,7 @@ public partial class SharedPhysicsSystem
|
||||
/// </summary>
|
||||
public bool SetLinearVelocity(EntityUid uid, Vector2 velocity, bool dirty = true, bool wakeBody = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body))
|
||||
return false;
|
||||
|
||||
if (body.BodyType == BodyType.Static)
|
||||
@@ -467,7 +470,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void SetBodyType(EntityUid uid, BodyType value, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body))
|
||||
return;
|
||||
|
||||
if (body.BodyType == value)
|
||||
@@ -531,7 +534,7 @@ public partial class SharedPhysicsSystem
|
||||
FixturesComponent? manager = null,
|
||||
PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body))
|
||||
return false;
|
||||
|
||||
if (body.CanCollide == value)
|
||||
@@ -545,7 +548,7 @@ public partial class SharedPhysicsSystem
|
||||
if (_containerSystem.IsEntityOrParentInContainer(uid))
|
||||
return false;
|
||||
|
||||
if (!Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_mapManager.IsGrid(uid))
|
||||
if (!_fixturesQuery.Resolve(uid, ref manager) || manager.FixtureCount == 0 && !_mapManager.IsGrid(uid))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -575,7 +578,7 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public void SetFixedRotation(EntityUid uid, bool value, bool dirty = true, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body) || body.FixedRotation == value)
|
||||
if (!PhysicsQuery.Resolve(uid, ref body) || body.FixedRotation == value)
|
||||
return;
|
||||
|
||||
body.FixedRotation = value;
|
||||
@@ -668,10 +671,13 @@ public partial class SharedPhysicsSystem
|
||||
/// <returns>true if the body is collidable and awake</returns>
|
||||
public bool WakeBody(EntityUid uid, bool force = false, FixturesComponent? manager = null, PhysicsComponent? body = null)
|
||||
{
|
||||
if (!SetCanCollide(uid, true, manager: manager, body: body, force: force) || !Resolve(uid, ref body))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body))
|
||||
return false;
|
||||
|
||||
SetAwake(uid, body, true);
|
||||
if (!SetCanCollide(uid, true, manager: manager, body: body, force: force))
|
||||
return false;
|
||||
|
||||
SetAwake((uid, body), true);
|
||||
return body.Awake;
|
||||
}
|
||||
|
||||
@@ -715,7 +721,9 @@ public partial class SharedPhysicsSystem
|
||||
|
||||
public Box2 GetHardAABB(EntityUid uid, FixturesComponent? manager = null, PhysicsComponent? body = null, TransformComponent? xform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body, ref xform, ref manager))
|
||||
if (!PhysicsQuery.Resolve(uid, ref body)
|
||||
|| !_fixturesQuery.Resolve(uid, ref manager)
|
||||
|| !Resolve(uid, ref xform))
|
||||
{
|
||||
return Box2.Empty;
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
return (linearVelocity + linearVelocityAngularContribution, angularVelocity);
|
||||
}
|
||||
|
||||
private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, ref EntParentChangedMessage args, TransformComponent xform)
|
||||
private void HandleParentChangeVelocity(EntityUid uid, PhysicsComponent physics, EntityUid oldParent, TransformComponent xform)
|
||||
{
|
||||
// If parent changed due to state handling, don't modify velocities. The physics comp state will take care of itself..
|
||||
if (_gameTiming.ApplyingState)
|
||||
@@ -217,15 +217,13 @@ public abstract partial class SharedPhysicsSystem
|
||||
// I guess the question becomes, what do you do with conservation of momentum in that case. I guess its the job
|
||||
// of the teleporter to select a velocity at the after the parent has changed.
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
FixturesComponent? manager = null;
|
||||
|
||||
// for the new velocities (that need to be updated), we can just use the existing function:
|
||||
var (newLinear, newAngular) = GetMapVelocities(uid, physics, xform);
|
||||
|
||||
// for the old velocities, we need to re-implement this function while using the old parent and old local position:
|
||||
if (args.OldParent is not { Valid: true } parent)
|
||||
if (oldParent == EntityUid.Invalid)
|
||||
{
|
||||
// no previous parent --> simple
|
||||
// Old velocity + (old velocity - new velocity)
|
||||
@@ -234,7 +232,8 @@ public abstract partial class SharedPhysicsSystem
|
||||
return;
|
||||
}
|
||||
|
||||
TransformComponent? parentXform = xformQuery.GetComponent(parent);
|
||||
var parent = oldParent;
|
||||
TransformComponent? parentXform = _xformQuery.GetComponent(parent);
|
||||
var localPos = _transform.GetInvWorldMatrix(parentXform).Transform(_transform.GetWorldPosition(xform));
|
||||
|
||||
var oldLinear = physics.LinearVelocity;
|
||||
@@ -243,7 +242,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
|
||||
do
|
||||
{
|
||||
if (physicsQuery.TryGetComponent(parent, out var body))
|
||||
if (PhysicsQuery.TryGetComponent(parent, out var body))
|
||||
{
|
||||
oldAngular += body.AngularVelocity;
|
||||
|
||||
@@ -259,7 +258,7 @@ public abstract partial class SharedPhysicsSystem
|
||||
localPos = parentXform.LocalPosition + parentXform.LocalRotation.RotateVec(localPos);
|
||||
parent = parentXform.ParentUid;
|
||||
|
||||
} while (parent.IsValid() && xformQuery.TryGetComponent(parent, out parentXform));
|
||||
} while (parent.IsValid() && _xformQuery.TryGetComponent(parent, out parentXform));
|
||||
|
||||
oldLinear += linearAngularContribution;
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ namespace Robust.Shared.Physics.Systems
|
||||
SubscribeLocalEvent<GridAddEvent>(OnGridAdd);
|
||||
SubscribeLocalEvent<CollisionChangeEvent>(OnCollisionChange);
|
||||
SubscribeLocalEvent<PhysicsComponent, EntGotRemovedFromContainerMessage>(HandleContainerRemoved);
|
||||
SubscribeLocalEvent<EntParentChangedMessage>(OnParentChange);
|
||||
SubscribeLocalEvent<PhysicsMapComponent, ComponentInit>(HandlePhysicsMapInit);
|
||||
SubscribeLocalEvent<PhysicsComponent, ComponentInit>(OnPhysicsInit);
|
||||
SubscribeLocalEvent<PhysicsComponent, ComponentShutdown>(OnPhysicsShutdown);
|
||||
@@ -150,51 +149,45 @@ namespace Robust.Shared.Physics.Systems
|
||||
_substeps = (int)Math.Ceiling(targetMinTickrate / serverTickrate);
|
||||
}
|
||||
|
||||
private void OnParentChange(ref EntParentChangedMessage args)
|
||||
internal void OnParentChange(Entity<TransformComponent, MetaDataComponent> ent, EntityUid oldParent, EntityUid? oldMap)
|
||||
{
|
||||
// We do not have a directed/body subscription, because the entity changing parents may not have a physics component, but one of its children might.
|
||||
var uid = args.Entity;
|
||||
var xform = args.Transform;
|
||||
var (uid, xform, meta) = ent;
|
||||
|
||||
// If this entity has yet to be initialized, then we can skip this as equivalent code will get run during
|
||||
// init anyways. HOWEVER: it is possible that one of the children of this entity are already post-init, in
|
||||
// which case they still need to handle map changes. This frequently happens when clients receives a server
|
||||
// state where a known/old entity gets attached to a new, previously unknown, entity. The new entity will be
|
||||
// uninitialized but have an initialized child.
|
||||
if (xform.ChildCount == 0 && LifeStage(uid) < EntityLifeStage.Initialized)
|
||||
if (xform.ChildCount == 0 && meta.EntityLifeStage < EntityLifeStage.Initialized)
|
||||
return;
|
||||
|
||||
// Is this entity getting recursively detached after it's parent was already detached to null?
|
||||
if (args.OldMapId == MapId.Nullspace && xform.MapID == MapId.Nullspace)
|
||||
if (oldMap == null && xform.MapUid == null)
|
||||
return;
|
||||
|
||||
var body = CompOrNull<PhysicsComponent>(uid);
|
||||
var body = PhysicsQuery.CompOrNull(uid);
|
||||
|
||||
// Handle map changes
|
||||
if (args.OldMapId != xform.MapID)
|
||||
if (oldMap != xform.MapUid)
|
||||
{
|
||||
// This will also handle broadphase updating & joint clearing.
|
||||
HandleMapChange(uid, xform, body, args.OldMapId, xform.MapID);
|
||||
HandleMapChange(uid, xform, body, oldMap, xform.MapUid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.OldMapId != xform.MapID)
|
||||
return;
|
||||
|
||||
if (body != null)
|
||||
HandleParentChangeVelocity(uid, body, ref args, xform);
|
||||
HandleParentChangeVelocity(uid, body, oldParent, xform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively add/remove from awake bodies, clear joints, remove from move buffer, and update broadphase.
|
||||
/// </summary>
|
||||
private void HandleMapChange(EntityUid uid, TransformComponent xform, PhysicsComponent? body, MapId oldMapId, MapId newMapId)
|
||||
private void HandleMapChange(EntityUid uid, TransformComponent xform, PhysicsComponent? body, EntityUid? oldMapId, EntityUid? newMapId)
|
||||
{
|
||||
var jointQuery = GetEntityQuery<JointComponent>();
|
||||
|
||||
PhysMapQuery.TryGetComponent(_mapManager.GetMapEntityId(oldMapId), out var oldMap);
|
||||
PhysMapQuery.TryGetComponent(_mapManager.GetMapEntityId(newMapId), out var newMap);
|
||||
|
||||
RecursiveMapUpdate(uid, xform, body, newMap, oldMap, jointQuery);
|
||||
PhysMapQuery.TryGetComponent(oldMapId, out var oldMap);
|
||||
PhysMapQuery.TryGetComponent(newMapId, out var newMap);
|
||||
RecursiveMapUpdate(uid, xform, body, newMap, oldMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -205,8 +198,7 @@ namespace Robust.Shared.Physics.Systems
|
||||
TransformComponent xform,
|
||||
PhysicsComponent? body,
|
||||
PhysicsMapComponent? newMap,
|
||||
PhysicsMapComponent? oldMap,
|
||||
EntityQuery<JointComponent> jointQuery)
|
||||
PhysicsMapComponent? oldMap)
|
||||
{
|
||||
DebugTools.Assert(!Deleted(uid));
|
||||
|
||||
@@ -223,16 +215,14 @@ namespace Robust.Shared.Physics.Systems
|
||||
DebugTools.Assert(oldMap?.AwakeBodies.Contains(body) != true);
|
||||
}
|
||||
|
||||
if (jointQuery.TryGetComponent(uid, out var joint))
|
||||
_joints.ClearJoints(uid, joint);
|
||||
|
||||
_joints.ClearJoints(uid);
|
||||
|
||||
foreach (var child in xform._children)
|
||||
{
|
||||
if (_xformQuery.TryGetComponent(child, out var childXform))
|
||||
{
|
||||
PhysicsQuery.TryGetComponent(child, out var childBody);
|
||||
RecursiveMapUpdate(child, childXform, childBody, newMap, oldMap, jointQuery);
|
||||
RecursiveMapUpdate(child, childXform, childBody, newMap, oldMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace Robust.UnitTesting.Client.UserInterface
|
||||
control4.OnKeyBindDown += _ => Assert.Fail("Control 4 should not get a mouse event.");
|
||||
|
||||
_userInterfaceManager.KeyBindDown(mouseEvent);
|
||||
_userInterfaceManager.KeyBindUp(mouseEvent);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -124,6 +125,7 @@ namespace Robust.UnitTesting.Client.UserInterface
|
||||
control2.MouseFilter = Control.MouseFilterMode.Pass;
|
||||
|
||||
_userInterfaceManager.KeyBindDown(mouseEvent);
|
||||
_userInterfaceManager.KeyBindUp(mouseEvent);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -247,6 +249,7 @@ namespace Robust.UnitTesting.Client.UserInterface
|
||||
pos, true, pos.Position / 1 - control.GlobalPosition, pos.Position - control.GlobalPixelPosition);
|
||||
|
||||
_userInterfaceManager.KeyBindDown(mouseEvent);
|
||||
_userInterfaceManager.KeyBindUp(mouseEvent);
|
||||
|
||||
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
|
||||
Assert.That(parentXform.MapID, Is.EqualTo(mapId));
|
||||
Assert.That(childXform.MapID, Is.EqualTo(mapId));
|
||||
|
||||
xformSystem.DetachParentToNull(parent, parentXform);
|
||||
xformSystem.DetachEntity(parent, parentXform);
|
||||
Assert.That(parentXform.MapID, Is.EqualTo(MapId.Nullspace));
|
||||
Assert.That(childXform.MapID, Is.EqualTo(MapId.Nullspace));
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ public sealed class Broadphase_Test
|
||||
Assert.That(lookup.FindBroadphase(child2), Is.EqualTo(mapBroadphase));
|
||||
|
||||
// They should get deparented to the map and updated to the map's broadphase instead.
|
||||
xformSystem.DetachParentToNull(parent, parentXform);
|
||||
xformSystem.DetachEntity(parent, parentXform);
|
||||
Assert.That(lookup.FindBroadphase(parent), Is.EqualTo(null));
|
||||
Assert.That(lookup.FindBroadphase(child1), Is.EqualTo(null));
|
||||
Assert.That(lookup.FindBroadphase(child2), Is.EqualTo(null));
|
||||
|
||||
Reference in New Issue
Block a user