using System.Collections.Generic;
using Robust.Shared.Containers;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
///
/// Handles moving entities between grids as they move around.
///
internal sealed class SharedGridTraversalSystem : EntitySystem
{
[Dependency] private readonly IMapManagerInternal _mapManager = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
private Stack _queuedEvents = new();
private HashSet _handledThisTick = new();
private List _gridBuffer = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent((ref MoveEvent ev) => _queuedEvents.Push(ev));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdatesOutsidePrediction = true;
// Need to queue because otherwise calling HandleMove during FrameUpdate will lead to prediction issues.
// TODO: Need to check if that's even still relevant since transform lerping fix?
ProcessChanges();
}
private void ProcessChanges()
{
var maps = EntityManager.GetEntityQuery();
var grids = EntityManager.GetEntityQuery();
var bodies = GetEntityQuery();
var xforms = EntityManager.GetEntityQuery();
var metas = EntityManager.GetEntityQuery();
while (_queuedEvents.TryPop(out var moveEvent))
{
if (!_handledThisTick.Add(moveEvent.Sender)) continue;
HandleMove(ref moveEvent, xforms, bodies, maps, grids, metas);
}
_handledThisTick.Clear();
}
private void HandleMove(
ref MoveEvent moveEvent,
EntityQuery xforms,
EntityQuery bodies,
EntityQuery maps,
EntityQuery grids,
EntityQuery metas)
{
var entity = moveEvent.Sender;
if (!metas.TryGetComponent(entity, out MetaDataComponent? meta) ||
meta.EntityDeleted ||
(meta.Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer ||
maps.HasComponent(entity) ||
grids.HasComponent(entity))
{
return;
}
var xform = xforms.GetComponent(entity);
DebugTools.Assert(!float.IsNaN(moveEvent.NewPosition.X) && !float.IsNaN(moveEvent.NewPosition.Y));
// We only do grid-traversal parent changes if the entity is currently parented to a map or a grid.
var parentIsMap = xform.GridID == GridId.Invalid && maps.HasComponent(xform.ParentUid);
if (!parentIsMap && !grids.HasComponent(xform.ParentUid))
return;
var mapPos = moveEvent.NewPosition.ToMapPos(EntityManager);
_gridBuffer.Clear();
// Change parent if necessary
if (_mapManager.TryFindGridAt(xform.MapID, mapPos, _gridBuffer, xforms, bodies, out var grid))
{
// Some minor duplication here with AttachParent but only happens when going on/off grid so not a big deal ATM.
if (grid.Index != xform.GridID)
{
xform.AttachParent(grid.GridEntityId);
RaiseLocalEvent(entity, new ChangedGridEvent(entity, xform.GridID, grid.Index));
}
}
else
{
var oldGridId = xform.GridID;
// Attach them to map / they are on an invalid grid
if (oldGridId != GridId.Invalid)
{
xform.AttachParent(_mapManager.GetMapEntityIdOrThrow(xform.MapID));
RaiseLocalEvent(entity, new ChangedGridEvent(entity, oldGridId, GridId.Invalid));
}
}
}
}
public sealed class ChangedGridEvent : EntityEventArgs
{
public EntityUid Entity;
public GridId OldGrid;
public GridId NewGrid;
public ChangedGridEvent(EntityUid entity, GridId oldGrid, GridId newGrid)
{
Entity = entity;
OldGrid = oldGrid;
NewGrid = newGrid;
}
}
}