mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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,35 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -36,7 +36,7 @@ public abstract partial class SharedAudioSystem : EntitySystem
|
||||
/// <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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -43,21 +43,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 +313,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 +441,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 +459,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 +479,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 +489,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 +497,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 +522,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 +539,6 @@ public abstract partial class SharedTransformSystem
|
||||
newParent?._children.Add(uid);
|
||||
|
||||
xform._parent = value.EntityId;
|
||||
var oldMapId = xform.MapID;
|
||||
|
||||
if (newParent != null)
|
||||
{
|
||||
@@ -576,24 +569,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 +655,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;
|
||||
}
|
||||
|
||||
@@ -1143,7 +1130,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 +1149,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 +1312,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 +1348,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 +1408,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 +1439,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 +1474,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 +1512,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 +1532,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;
|
||||
|
||||
@@ -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