Network entity ids (#4252)

This commit is contained in:
metalgearsloth
2023-09-11 09:42:55 +10:00
committed by GitHub
parent cb1d4ae843
commit 3fd731d917
131 changed files with 3266 additions and 1480 deletions

View File

@@ -22,7 +22,8 @@ namespace Robust.Client.Console.Commands
return;
}
var entity = EntityUid.Parse(args[0]);
var netEntity = NetEntity.Parse(args[0]);
var entity = _entityManager.GetEntity(netEntity);
var componentName = args[1];
var component = (Component) _componentFactory.GetComponent(componentName);
@@ -49,7 +50,8 @@ namespace Robust.Client.Console.Commands
return;
}
var entityUid = EntityUid.Parse(args[0]);
var netEntity = NetEntity.Parse(args[0]);
var entityUid = _entityManager.GetEntity(netEntity);
var componentName = args[1];
var registration = _componentFactory.GetRegistration(componentName);

View File

@@ -296,6 +296,7 @@ namespace Robust.Client.Console.Commands
internal sealed class SnapGridGetCell : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _map = default!;
public override string Command => "sggcell";
@@ -310,7 +311,7 @@ namespace Robust.Client.Console.Commands
string indices = args[1];
if (!EntityUid.TryParse(args[0], out var gridUid))
if (!NetEntity.TryParse(args[0], out var gridNet))
{
shell.WriteError($"{args[0]} is not a valid entity UID.");
return;
@@ -322,7 +323,7 @@ namespace Robust.Client.Console.Commands
return;
}
if (_map.TryGetGrid(gridUid, out var grid))
if (_map.TryGetGrid(_entManager.GetEntity(gridNet), out var grid))
{
foreach (var entity in grid.GetAnchoredEntities(new Vector2i(
int.Parse(indices.Split(',')[0], CultureInfo.InvariantCulture),
@@ -430,6 +431,7 @@ namespace Robust.Client.Console.Commands
internal sealed class GridTileCount : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _map = default!;
public override string Command => "gridtc";
@@ -442,7 +444,8 @@ namespace Robust.Client.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var gridUid))
if (!NetEntity.TryParse(args[0], out var gridUidNet) ||
!_entManager.TryGetEntity(gridUidNet, out var gridUid))
{
shell.WriteLine($"{args[0]} is not a valid entity UID.");
return;

View File

@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Robust.Client.GameObjects;
public sealed partial class ClientEntityManager
{
protected override NetEntity GenerateNetEntity() => new(NextNetworkId++ | NetEntity.ClientEntity);
/// <summary>
/// If the client fails to resolve a NetEntity then during component state handling or the likes we
/// flag that comp state as requiring re-running if that NetEntity comes in.
/// </summary>
/// <returns></returns>
internal readonly Dictionary<NetEntity, List<(Type type, EntityUid Owner)>> PendingNetEntityStates = new();
public override bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null)
{
// Can't log false because some content code relies on invalid UIDs.
if (!MetaQuery.Resolve(uid, ref metadata, false))
return false;
return metadata.NetEntity.IsClientSide();
}
public override EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity)
{
if (!nEntity.Valid)
{
return EntityUid.Invalid;
}
if (NetEntityLookup.TryGetValue(nEntity, out var entity))
{
return entity;
}
// Flag the callerEntity to have their state potentially re-run later.
var pending = PendingNetEntityStates.GetOrNew(nEntity);
pending.Add((typeof(T), callerEntity));
return entity;
}
public override EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
{
var entity = EnsureEntity<T>(netCoordinates.NetEntity, callerEntity);
return new EntityCoordinates(entity, netCoordinates.Position);
}
}

View File

@@ -16,7 +16,7 @@ namespace Robust.Client.GameObjects
/// <summary>
/// Manager for entities -- controls things like template loading and instantiation
/// </summary>
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
public sealed partial class ClientEntityManager : EntityManager, IClientEntityManagerInternal
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IClientNetManager _networkManager = default!;
@@ -25,8 +25,6 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
protected override int NextEntityUid { get; set; } = EntityUid.ClientUid + 1;
public override void Initialize()
{
SetupNetworking();
@@ -37,13 +35,16 @@ namespace Robust.Client.GameObjects
public override void FlushEntities()
{
// Server doesn't network deletions on client shutdown so we need to
// manually clear these out or risk stale data getting used.
PendingNetEntityStates.Clear();
using var _ = _gameTiming.StartStateApplicationArea();
base.FlushEntities();
}
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid uid)
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName)
{
return base.CreateEntity(prototypeName, uid);
return base.CreateEntity(prototypeName);
}
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
@@ -66,7 +67,7 @@ namespace Robust.Client.GameObjects
public override void QueueDeleteEntity(EntityUid uid)
{
if (uid.IsClientSide())
if (IsClientSide(uid))
{
base.QueueDeleteEntity(uid);
return;

View File

@@ -128,7 +128,7 @@ namespace Robust.Client.GameObjects
return;
}
if (component.Owner.IsClientSide() || !animatedComp.NetSyncEnabled)
if (IsClientSide(component.Owner) || !animatedComp.NetSyncEnabled)
continue;
var reg = _compFact.GetRegistration(animatedComp);

View File

@@ -81,9 +81,13 @@ public sealed class AudioSystem : SharedAudioSystem
#region Event Handlers
private void PlayAudioEntityHandler(PlayAudioEntityMessage ev)
{
var stream = EntityManager.EntityExists(ev.EntityUid)
? (PlayingStream?) Play(ev.FileName, ev.EntityUid, ev.FallbackCoordinates, ev.AudioParams, false)
: (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams, false);
var uid = GetEntity(ev.NetEntity);
var coords = GetCoordinates(ev.Coordinates);
var fallback = GetCoordinates(ev.FallbackCoordinates);
var stream = EntityManager.EntityExists(uid)
? (PlayingStream?) Play(ev.FileName, uid, fallback, ev.AudioParams, false)
: (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false);
if (stream != null)
stream.NetIdentifier = ev.Identifier;
@@ -98,7 +102,10 @@ public sealed class AudioSystem : SharedAudioSystem
private void PlayAudioPositionalHandler(PlayAudioPositionalMessage ev)
{
var stream = (PlayingStream?) Play(ev.FileName, ev.Coordinates, ev.FallbackCoordinates, ev.AudioParams, false);
var coords = GetCoordinates(ev.Coordinates);
var fallback = GetCoordinates(ev.FallbackCoordinates);
var stream = (PlayingStream?) Play(ev.FileName, coords, fallback, ev.AudioParams, false);
if (stream != null)
stream.NetIdentifier = ev.Identifier;
}
@@ -383,8 +390,8 @@ public sealed class AudioSystem : SharedAudioSystem
_replayRecording.RecordReplayMessage(new PlayAudioEntityMessage
{
FileName = filename,
EntityUid = entity,
FallbackCoordinates = fallbackCoordinates ?? default,
NetEntity = GetNetEntity(entity),
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates) ?? default,
AudioParams = audioParams ?? AudioParams.Default
});
}
@@ -437,8 +444,8 @@ public sealed class AudioSystem : SharedAudioSystem
_replayRecording.RecordReplayMessage(new PlayAudioPositionalMessage
{
FileName = filename,
Coordinates = coordinates,
FallbackCoordinates = fallbackCoordinates,
Coordinates = GetNetCoordinates(coordinates),
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
AudioParams = audioParams ?? AudioParams.Default
});
}

View File

@@ -1,3 +1,4 @@
using System;
using Robust.Shared.Collections;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
@@ -5,13 +6,11 @@ using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.Physics.Components;
using Robust.Shared.Serialization;
using static Robust.Shared.Containers.ContainerManagerComponent;
namespace Robust.Client.GameObjects
@@ -23,14 +22,20 @@ namespace Robust.Client.GameObjects
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
[Dependency] private readonly PointLightSystem _lightSys = default!;
private EntityQuery<PointLightComponent> _pointLightQuery;
private EntityQuery<SpriteComponent> _spriteQuery;
private readonly HashSet<EntityUid> _updateQueue = new();
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
public readonly Dictionary<NetEntity, BaseContainer> ExpectedEntities = new();
public override void Initialize()
{
base.Initialize();
_pointLightQuery = GetEntityQuery<PointLightComponent>();
_spriteQuery = GetEntityQuery<SpriteComponent>();
EntityManager.EntityInitialized += HandleEntityInitialized;
SubscribeLocalEvent<ContainerManagerComponent, ComponentHandleState>(HandleComponentState);
@@ -43,20 +48,18 @@ namespace Robust.Client.GameObjects
base.Shutdown();
}
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
{
DebugTools.Assert(ExpectedEntities.TryGetValue(missing, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(missing));
var netEntity = GetNetEntity(missing);
DebugTools.Assert(ExpectedEntities.TryGetValue(netEntity, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(netEntity));
}
private void HandleEntityInitialized(EntityUid uid)
{
if (!RemoveExpectedEntity(uid, out var container))
if (!RemoveExpectedEntity(GetNetEntity(uid), out var container))
return;
if (container.Deleted)
return;
container.Insert(uid);
container.Insert(uid, EntityManager, transform: TransformQuery.GetComponent(uid), meta: MetaQuery.GetComponent(uid));
}
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
@@ -64,23 +67,24 @@ namespace Robust.Client.GameObjects
if (args.Current is not ContainerManagerComponentState cast)
return;
var metaQuery = GetEntityQuery<MetaDataComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
var xform = xformQuery.GetComponent(uid);
var xform = TransformQuery.GetComponent(uid);
// Delete now-gone containers.
var toDelete = new ValueList<string>();
foreach (var (id, container) in component.Containers)
{
if (cast.Containers.ContainsKey(id))
{
DebugTools.Assert(cast.Containers[id].ContainerType == container.GetType().Name);
continue;
}
foreach (var entity in container.ContainedEntities.ToArray())
{
container.Remove(entity,
EntityManager,
xformQuery.GetComponent(entity),
metaQuery.GetComponent(entity),
TransformQuery.GetComponent(entity),
MetaQuery.GetComponent(entity),
force: true,
reparent: false);
@@ -98,26 +102,32 @@ namespace Robust.Client.GameObjects
// Add new containers and update existing contents.
foreach (var (containerType, id, showEnts, occludesLight, entityUids) in cast.Containers.Values)
foreach (var (id, data) in cast.Containers)
{
if (!component.Containers.TryGetValue(id, out var container))
{
container = ContainerFactory(component, containerType, id);
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
container.Init(id, uid, component);
component.Containers.Add(id, container);
}
// sync show flag
container.ShowContents = showEnts;
container.OccludesLight = occludesLight;
DebugTools.Assert(container.ID == id);
container.ShowContents = data.ShowContents;
container.OccludesLight = data.OccludesLight;
// Remove gone entities.
var toRemove = new ValueList<EntityUid>();
DebugTools.Assert(!container.Contains(EntityUid.Invalid));
var stateNetEnts = data.ContainedEntities;
var stateEnts = GetEntityArray(stateNetEnts); // No need to ensure entities.
foreach (var entity in container.ContainedEntities)
{
if (!entityUids.Contains(entity))
{
if (!stateEnts.Contains(entity))
toRemove.Add(entity);
}
}
foreach (var entity in toRemove)
@@ -125,8 +135,8 @@ namespace Robust.Client.GameObjects
container.Remove(
entity,
EntityManager,
xformQuery.GetComponent(entity),
metaQuery.GetComponent(entity),
TransformQuery.GetComponent(entity),
MetaQuery.GetComponent(entity),
force: true,
reparent: false);
@@ -134,13 +144,11 @@ namespace Robust.Client.GameObjects
}
// Remove entities that were expected, but have been removed from the container.
var removedExpected = new ValueList<EntityUid>();
foreach (var entityUid in container.ExpectedEntities)
var removedExpected = new ValueList<NetEntity>();
foreach (var netEntity in container.ExpectedEntities)
{
if (!entityUids.Contains(entityUid))
{
removedExpected.Add(entityUid);
}
if (!stateNetEnts.Contains(netEntity))
removedExpected.Add(netEntity);
}
foreach (var entityUid in removedExpected)
@@ -149,14 +157,20 @@ namespace Robust.Client.GameObjects
}
// Add new entities.
foreach (var entity in entityUids)
for (var i = 0; i < stateNetEnts.Length; i++)
{
if (!EntityManager.TryGetComponent(entity, out MetaDataComponent? meta))
var entity = stateEnts[i];
var netEnt = stateNetEnts[i];
if (!entity.IsValid())
{
AddExpectedEntity(entity, container);
DebugTools.Assert(netEnt.IsValid());
AddExpectedEntity(netEnt, container);
continue;
}
var meta = MetaData(entity);
DebugTools.Assert(meta.NetEntity == netEnt);
// If an entity is currently in the shadow realm, it means we probably left PVS and are now getting
// back into range. We do not want to directly insert this entity, as IF the container and entity
// transform states did not get sent simultaneously, the entity's transform will be modified by the
@@ -166,18 +180,18 @@ namespace Robust.Client.GameObjects
// containers/players.
if ((meta.Flags & MetaDataFlags.Detached) != 0)
{
AddExpectedEntity(entity, container);
AddExpectedEntity(netEnt, container);
continue;
}
if (container.Contains(entity))
continue;
RemoveExpectedEntity(entity, out _);
RemoveExpectedEntity(netEnt, out _);
container.Insert(entity, EntityManager,
xformQuery.GetComponent(entity),
TransformQuery.GetComponent(entity),
xform,
metaQuery.GetComponent(entity),
MetaQuery.GetComponent(entity),
force: true);
DebugTools.Assert(container.Contains(entity));
@@ -198,7 +212,7 @@ namespace Robust.Client.GameObjects
if (message.OldParent != null && message.OldParent.Value.IsValid())
return;
if (!RemoveExpectedEntity(message.Entity, out var container))
if (!RemoveExpectedEntity(GetNetEntity(message.Entity), out var container))
return;
if (xform.ParentUid != container.Owner)
@@ -208,84 +222,69 @@ namespace Robust.Client.GameObjects
return;
}
if (container.Deleted)
return;
container.Insert(message.Entity, EntityManager);
}
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
public void AddExpectedEntity(NetEntity netEntity, BaseContainer container)
{
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);
if (type is null) throw new ArgumentException($"Container of type {containerType} for id {id} cannot be found.");
#if DEBUG
var uid = GetEntity(netEntity);
var newContainer = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
newContainer.ID = id;
newContainer.Manager = component;
return newContainer;
}
if (TryComp<MetaDataComponent>(uid, out var meta))
{
DebugTools.Assert((meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
}
#endif
public void AddExpectedEntity(EntityUid uid, IContainer container)
{
DebugTools.Assert(!TryComp(uid, out MetaDataComponent? meta) ||
(meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
if (!ExpectedEntities.TryAdd(uid, container))
if (!ExpectedEntities.TryAdd(netEntity, container))
{
// It is possible that we were expecting this entity in one container, but it has now moved to another
// container, and this entity's state is just being applied before the old container is getting updated.
var oldContainer = ExpectedEntities[uid];
ExpectedEntities[uid] = container;
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(uid),
$"Entity {ToPrettyString(uid)} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
oldContainer.ExpectedEntities.Remove(uid);
var oldContainer = ExpectedEntities[netEntity];
ExpectedEntities[netEntity] = container;
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(netEntity),
$"Entity {netEntity} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
oldContainer.ExpectedEntities.Remove(netEntity);
}
DebugTools.Assert(!container.ExpectedEntities.Contains(uid),
$"Contained entity {ToPrettyString(uid)} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
container.ExpectedEntities.Add(uid);
DebugTools.Assert(!container.ExpectedEntities.Contains(netEntity),
$"Contained entity {netEntity} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
container.ExpectedEntities.Add(netEntity);
}
public bool RemoveExpectedEntity(EntityUid uid, [NotNullWhen(true)] out IContainer? container)
public bool RemoveExpectedEntity(NetEntity netEntity, [NotNullWhen(true)] out BaseContainer? container)
{
if (!ExpectedEntities.Remove(uid, out container))
if (!ExpectedEntities.Remove(netEntity, out container))
return false;
DebugTools.Assert(container.ExpectedEntities.Contains(uid),
$"While removing expected contained entity {ToPrettyString(uid)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
container.ExpectedEntities.Remove(uid);
DebugTools.Assert(container.ExpectedEntities.Contains(netEntity),
$"While removing expected contained entity {ToPrettyString(netEntity)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
container.ExpectedEntities.Remove(netEntity);
return true;
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
foreach (var toUpdate in _updateQueue)
{
if (Deleted(toUpdate))
continue;
UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
UpdateEntityRecursively(toUpdate);
}
_updateQueue.Clear();
}
private void UpdateEntityRecursively(
EntityUid entity,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<PointLightComponent> pointQuery,
EntityQuery<SpriteComponent> spriteQuery)
private void UpdateEntityRecursively(EntityUid entity)
{
// Recursively go up parents and containers to see whether both sprites and lights need to be occluded
// Could maybe optimise this more by checking nearest parent that has sprite / light and whether it's container
// occluded but this probably isn't a big perf issue.
var xform = xformQuery.GetComponent(entity);
var xform = TransformQuery.GetComponent(entity);
var parent = xform.ParentUid;
var child = entity;
var spriteOccluded = false;
@@ -293,7 +292,7 @@ namespace Robust.Client.GameObjects
while (parent.IsValid() && (!spriteOccluded || !lightOccluded))
{
var parentXform = xformQuery.GetComponent(parent);
var parentXform = TransformQuery.GetComponent(parent);
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
{
spriteOccluded = spriteOccluded || !container.ShowContents;
@@ -308,24 +307,21 @@ namespace Robust.Client.GameObjects
// This is the CBT bit.
// The issue is we need to go through the children and re-check whether they are or are not contained.
// if they are contained then the occlusion values may need updating for all those children
UpdateEntity(entity, xform, xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
UpdateEntity(entity, xform, spriteOccluded, lightOccluded);
}
private void UpdateEntity(
EntityUid entity,
TransformComponent xform,
EntityQuery<TransformComponent> xformQuery,
EntityQuery<PointLightComponent> pointQuery,
EntityQuery<SpriteComponent> spriteQuery,
bool spriteOccluded,
bool lightOccluded)
{
if (spriteQuery.TryGetComponent(entity, out var sprite))
if (_spriteQuery.TryGetComponent(entity, out var sprite))
{
sprite.ContainerOccluded = spriteOccluded;
}
if (pointQuery.TryGetComponent(entity, out var light))
if (_pointLightQuery.TryGetComponent(entity, out var light))
_lightSys.SetContainerOccluded(entity, lightOccluded, light);
var childEnumerator = xform.ChildEnumerator;
@@ -346,14 +342,14 @@ namespace Robust.Client.GameObjects
childLightOccluded = childLightOccluded || container.OccludesLight;
}
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, childSpriteOccluded, childLightOccluded);
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), childSpriteOccluded, childLightOccluded);
}
}
else
{
while (childEnumerator.MoveNext(out var child))
{
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), spriteOccluded, lightOccluded);
}
}
}

View File

@@ -52,7 +52,7 @@ namespace Robust.Client.GameObjects
/// <param name="message">Arguments for this event.</param>
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
/// old input that was saved or buffered until further processing could be done</param>
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, IFullInputCmdMessage message, bool replay = false)
{
#if DEBUG
@@ -78,14 +78,27 @@ namespace Robust.Client.GameObjects
continue;
// local handlers can block sending over the network.
if (handler.HandleCmdMessage(session, message))
if (handler.HandleCmdMessage(EntityManager, session, message))
{
return true;
}
}
// send it off to the server
DispatchInputCommand(message);
var clientMsg = (ClientFullInputCmdMessage)message;
var fullMsg = new FullInputCmdMessage(
clientMsg.Tick,
clientMsg.SubTick,
(int)clientMsg.InputSequence,
clientMsg.InputFunctionId,
clientMsg.State,
GetNetCoordinates(clientMsg.Coordinates),
clientMsg.ScreenCoordinates)
{
Uid = GetNetEntity(clientMsg.Uid)
};
DispatchInputCommand(clientMsg, fullMsg);
return false;
}
@@ -93,7 +106,7 @@ namespace Robust.Client.GameObjects
/// Handle a predicted input command.
/// </summary>
/// <param name="inputCmd">Input command to handle as predicted.</param>
public void PredictInputCommand(FullInputCmdMessage inputCmd)
public void PredictInputCommand(IFullInputCmdMessage inputCmd)
{
DebugTools.AssertNotNull(_playerManager.LocalPlayer);
@@ -103,15 +116,16 @@ namespace Robust.Client.GameObjects
var session = _playerManager.LocalPlayer!.Session;
foreach (var handler in BindRegistry.GetHandlers(keyFunc))
{
if (handler.HandleCmdMessage(session, inputCmd)) break;
if (handler.HandleCmdMessage(EntityManager, session, inputCmd))
break;
}
Predicted = false;
}
private void DispatchInputCommand(FullInputCmdMessage message)
private void DispatchInputCommand(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message)
{
_stateManager.InputCommandDispatched(message);
_stateManager.InputCommandDispatched(clientMsg, message);
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, message.InputSequence);
}
@@ -152,7 +166,7 @@ namespace Robust.Client.GameObjects
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
coords, new ScreenCoordinates(0, 0, default), EntityUid.Invalid);
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
HandleInputCommand(localPlayer.Session, keyFunction, message);
}

View File

@@ -43,7 +43,8 @@ namespace Robust.Client.GameObjects
private void MessageReceived(BoundUIWrapMessage ev)
{
var uid = ev.Entity;
var uid = GetEntity(ev.Entity);
if (!TryComp<ClientUserInterfaceComponent>(uid, out var cmp))
return;
@@ -53,7 +54,7 @@ namespace Robust.Client.GameObjects
if(_playerManager.LocalPlayer != null)
message.Session = _playerManager.LocalPlayer.Session;
message.Entity = uid;
message.Entity = GetNetEntity(uid);
message.UiKey = uiKey;
// Raise as object so the correct type is used.
@@ -125,7 +126,7 @@ namespace Robust.Client.GameObjects
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
{
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, msg, bui.UiKey));
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), msg, bui.UiKey));
}
}
}

View File

@@ -6,7 +6,7 @@ namespace Robust.Client.GameObjects
{
// These methods are used by the Game State Manager.
EntityUid CreateEntity(string? prototypeName, EntityUid uid = default);
EntityUid CreateEntity(string? prototypeName);
void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null);

View File

@@ -15,7 +15,7 @@ public sealed class ClientDirtySystem : EntitySystem
{
[Dependency] private readonly IClientGameTiming _timing = default!;
[Dependency] private readonly IComponentFactory _compFact = default!;
// Entities that have removed networked components
// could pool the ushort sets, but predicted component changes are rare... soo...
internal readonly Dictionary<EntityUid, HashSet<ushort>> RemovedComponents = new();
@@ -40,11 +40,11 @@ public sealed class ClientDirtySystem : EntitySystem
private void OnTerminate(ref EntityTerminatingEvent ev)
{
if (!_timing.InPrediction || ev.Entity.IsClientSide())
if (!_timing.InPrediction || IsClientSide(ev.Entity))
return;
// Client-side entity deletion is not supported and will cause errors.
Logger.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
}
private void OnCompRemoved(RemovedComponentEventArgs args)
@@ -52,8 +52,9 @@ public sealed class ClientDirtySystem : EntitySystem
if (args.Terminating)
return;
var uid = args.BaseArgs.Owner;
var comp = args.BaseArgs.Component;
if (!_timing.InPrediction || comp.Owner.IsClientSide() || !comp.NetSyncEnabled)
if (!_timing.InPrediction || !comp.NetSyncEnabled || IsClientSide(uid))
return;
// Was this component added during prediction? If yes, then there is no need to re-add it when resetting.
@@ -62,7 +63,7 @@ public sealed class ClientDirtySystem : EntitySystem
var netId = _compFact.GetRegistration(comp).NetID;
if (netId != null)
RemovedComponents.GetOrNew(comp.Owner).Add(netId.Value);
RemovedComponents.GetOrNew(uid).Add(netId.Value);
}
public void Reset()
@@ -73,7 +74,7 @@ public sealed class ClientDirtySystem : EntitySystem
private void OnEntityDirty(EntityUid e)
{
if (_timing.InPrediction && !e.IsClientSide())
if (_timing.InPrediction && !IsClientSide(e))
DirtyEntities.Add(e);
}
}

View File

@@ -47,8 +47,10 @@ namespace Robust.Client.GameStates
= new();
// Game state dictionaries that get used every tick.
private readonly Dictionary<EntityUid, (bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState?nextState)> _toApply = new();
private readonly Dictionary<EntityUid, EntityState> _toCreate = new();
private readonly Dictionary<EntityUid, (bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
private uint _metaCompNetId;
@@ -157,7 +159,7 @@ namespace Robust.Client.GameStates
}
}
public void InputCommandDispatched(FullInputCmdMessage message)
public void InputCommandDispatched(ClientFullInputCmdMessage clientMessage, FullInputCmdMessage message)
{
if (!IsPredictionEnabled)
{
@@ -201,7 +203,7 @@ namespace Robust.Client.GameStates
public void UpdateFullRep(GameState state, bool cloneDelta = false)
=> _processor.UpdateFullRep(state, cloneDelta);
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
=> _processor.GetFullRep();
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
@@ -210,7 +212,7 @@ namespace Robust.Client.GameStates
PvsLeave?.Invoke(message);
}
public void QueuePvsDetach(List<EntityUid> entities, GameTick tick)
public void QueuePvsDetach(List<NetEntity> entities, GameTick tick)
{
_processor.AddLeavePvsMessage(entities, tick);
if (_replayRecording.IsRecording)
@@ -298,7 +300,7 @@ namespace Robust.Client.GameStates
_processor.UpdateFullRep(curState);
}
IEnumerable<EntityUid> createdEntities;
IEnumerable<NetEntity> createdEntities;
using (_prof.Group("ApplyGameState"))
{
if (_timing.LastProcessedTick < targetProcessedTick && nextState != null)
@@ -323,7 +325,7 @@ namespace Robust.Client.GameStates
catch (MissingMetadataException e)
{
// Something has gone wrong. Probably a missing meta-data component. Perhaps a full server state will fix it.
RequestFullState(e.Uid);
RequestFullState(e.NetEntity);
throw;
}
#endif
@@ -392,10 +394,10 @@ namespace Robust.Client.GameStates
}
}
public void RequestFullState(EntityUid? missingEntity = null)
public void RequestFullState(NetEntity? missingEntity = null)
{
_sawmill.Info("Requesting full server state");
_network.ClientSendMessage(new MsgStateRequestFull { Tick = _timing.LastRealTick , MissingEntity = missingEntity ?? EntityUid.Invalid });
_network.ClientSendMessage(new MsgStateRequestFull { Tick = _timing.LastRealTick , MissingEntity = missingEntity ?? NetEntity.Invalid });
_processor.RequestFullState();
}
@@ -472,7 +474,7 @@ namespace Robust.Client.GameStates
var countReset = 0;
var system = _entitySystemManager.GetEntitySystem<ClientDirtySystem>();
var query = _entityManager.GetEntityQuery<MetaDataComponent>();
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
RemQueue<Component> toRemove = new();
// This is terrible, and I hate it.
@@ -486,7 +488,8 @@ namespace Robust.Client.GameStates
if (_sawmill.Level <= LogLevel.Debug)
_sawmill.Debug($"Entity {entity} was made dirty.");
if (!_processor.TryGetLastServerStates(entity, out var last))
if (!metaQuery.TryGetComponent(entity, out var meta) ||
!_processor.TryGetLastServerStates(meta.NetEntity, out var last))
{
// Entity was probably deleted on the server so do nothing.
continue;
@@ -564,7 +567,6 @@ namespace Robust.Client.GameStates
}
}
var meta = query.GetComponent(entity);
DebugTools.Assert(meta.EntityLastModifiedTick > _timing.LastRealTick);
meta.EntityLastModifiedTick = _timing.LastRealTick;
}
@@ -588,15 +590,16 @@ namespace Robust.Client.GameStates
/// initial server state for any newly created entity. It does this by simply using the standard <see
/// cref="IEntityManager.GetComponentState(IEventBus, IComponent)"/>.
/// </remarks>
private void MergeImplicitData(IEnumerable<EntityUid> createdEntities)
private void MergeImplicitData(IEnumerable<NetEntity> createdEntities)
{
var outputData = new Dictionary<EntityUid, Dictionary<ushort, ComponentState>>();
var outputData = new Dictionary<NetEntity, Dictionary<ushort, ComponentState>>();
var bus = _entityManager.EventBus;
foreach (var createdEntity in createdEntities)
foreach (var netEntity in createdEntities)
{
var createdEntity = _entityManager.GetEntity(netEntity);
var compData = new Dictionary<ushort, ComponentState>();
outputData.Add(createdEntity, compData);
outputData.Add(netEntity, compData);
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
{
@@ -617,7 +620,7 @@ namespace Robust.Client.GameStates
_network.ClientSendMessage(new MsgStateAck() { Sequence = sequence });
}
public IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState)
public IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState)
{
using var _ = _timing.StartStateApplicationArea();
@@ -637,7 +640,7 @@ namespace Robust.Client.GameStates
_config.TickProcessMessages();
}
(IEnumerable<EntityUid> Created, List<EntityUid> Detached) output;
(IEnumerable<NetEntity> Created, List<NetEntity> Detached) output;
using (_prof.Group("Entity"))
{
output = ApplyEntityStates(curState, nextState);
@@ -656,7 +659,7 @@ namespace Robust.Client.GameStates
return output.Created;
}
private (IEnumerable<EntityUid> Created, List<EntityUid> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
private (IEnumerable<NetEntity> Created, List<NetEntity> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
{
var metas = _entities.GetEntityQuery<MetaDataComponent>();
var xforms = _entities.GetEntityQuery<TransformComponent>();
@@ -665,6 +668,7 @@ namespace Robust.Client.GameStates
var enteringPvs = 0;
_toApply.Clear();
_toCreate.Clear();
_pendingReapplyNetStates.Clear();
var curSpan = curState.EntityStates.Span;
// Create new entities
@@ -675,21 +679,40 @@ namespace Robust.Client.GameStates
foreach (var es in curSpan)
{
if (metas.HasComponent(es.Uid))
if (_entityManager.TryGetEntity(es.NetEntity, out var nUid))
{
DebugTools.Assert(_entityManager.EntityExists(nUid));
continue;
}
count++;
var uid = es.Uid;
var metaState = (MetaDataComponentState?)es.ComponentChanges.Value?.FirstOrDefault(c => c.NetID == _metaCompNetId).State;
if (metaState == null)
throw new MissingMetadataException(uid);
throw new MissingMetadataException(es.NetEntity);
_entities.CreateEntity(metaState.PrototypeId, uid);
_toCreate.Add(uid, es);
var uid = _entities.CreateEntity(metaState.PrototypeId);
_toCreate.Add(es.NetEntity, es);
_toApply.Add(uid, (false, GameTick.Zero, es, null));
var newMeta = metas.GetComponent(uid);
// Client creates a client-side net entity for the newly created entity.
// We need to clear this mapping before assigning the real net id.
// TODO NetEntity Jank: prevent the client from creating this in the first place.
_entityManager.ClearNetEntity(newMeta.NetEntity);
_entityManager.SetNetEntity(uid, es.NetEntity, newMeta);
newMeta.LastStateApplied = curState.ToSequence;
// Check if there's any component states awaiting this entity.
if (_entityManager.PendingNetEntityStates.TryGetValue(es.NetEntity, out var value))
{
foreach (var (type, owner) in value)
{
var pending = _pendingReapplyNetStates.GetOrNew(owner);
pending.Add(type);
}
}
}
_prof.WriteValue("Count", ProfData.Int32(count));
@@ -697,7 +720,9 @@ namespace Robust.Client.GameStates
foreach (var es in curSpan)
{
if (!metas.TryGetComponent(es.Uid, out var meta) || _toCreate.ContainsKey(es.Uid))
var uid = _entityManager.GetEntity(es.NetEntity);
if (!metas.TryGetComponent(uid, out var meta) || _toCreate.ContainsKey(es.NetEntity))
{
continue;
}
@@ -714,7 +739,7 @@ namespace Robust.Client.GameStates
continue;
}
_toApply.Add(es.Uid, (isEnteringPvs, meta.LastStateApplied, es, null));
_toApply.Add(uid, (isEnteringPvs, meta.LastStateApplied, es, null));
meta.LastStateApplied = curState.ToSequence;
}
@@ -728,22 +753,31 @@ namespace Robust.Client.GameStates
{
foreach (var es in nextState.EntityStates.Span)
{
var uid = es.Uid;
if (!metas.TryGetComponent(uid, out var meta))
if (!_entityManager.TryGetEntity(es.NetEntity, out var uid))
continue;
DebugTools.Assert(metas.HasComponent(uid));
// Does the next state actually have any future information about this entity that could be used for interpolation?
if (es.EntityLastModified != nextState.ToSequence)
continue;
if (_toApply.TryGetValue(uid, out var state))
_toApply[uid] = (state.EnteringPvs, state.LastApplied, state.curState, es);
if (_toApply.TryGetValue(uid.Value, out var state))
_toApply[uid.Value] = (state.EnteringPvs, state.LastApplied, state.curState, es);
else
_toApply[uid] = (false, GameTick.Zero, null, es);
_toApply[uid.Value] = (false, GameTick.Zero, null, es);
}
}
// Check pending states and see if we need to force any entities to re-run component states.
foreach (var uid in _pendingReapplyNetStates.Keys)
{
if (_toApply.ContainsKey(uid))
continue;
_toApply[uid] = (false, GameTick.Zero, null, null);
}
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
// Apply entity states.
@@ -766,6 +800,7 @@ namespace Robust.Client.GameStates
if (!_toApply.TryGetValue(xform.ParentUid, out var parent) || !parent.EnteringPvs)
queuedBroadphaseUpdates.Add((entity, xform));
}
_prof.WriteValue("Count", ProfData.Int32(_toApply.Count));
}
@@ -829,37 +864,37 @@ namespace Robust.Client.GameStates
// Construct hashset for set.Contains() checks.
var entityStates = state.EntityStates.Span;
var stateEnts = new HashSet<EntityUid>(entityStates.Length);
var stateEnts = new HashSet<NetEntity>();
foreach (var entState in entityStates)
{
stateEnts.Add(entState.Uid);
stateEnts.Add(entState.NetEntity);
}
var metas = _entities.GetEntityQuery<MetaDataComponent>();
var xforms = _entities.GetEntityQuery<TransformComponent>();
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
var currentEnts = _entities.GetEntities();
var toDelete = new List<EntityUid>(Math.Max(64, _entities.EntityCount - stateEnts.Count));
foreach (var ent in currentEnts)
// Client side entities won't need the transform, but that should always be a tiny minority of entities
var metaQuery = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent>();
while (metaQuery.MoveNext(out var ent, out var metadata, out var xform))
{
if (ent.IsClientSide())
var netEnt = metadata.NetEntity;
if (metadata.NetEntity.IsClientSide())
{
if (deleteClientEntities)
toDelete.Add(ent);
continue;
}
if (stateEnts.Contains(ent) && metas.TryGetComponent(ent, out var meta))
if (stateEnts.Contains(netEnt))
{
if (resetAllEntities || meta.LastStateApplied > state.ToSequence)
meta.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
if (resetAllEntities || metadata.LastStateApplied > state.ToSequence)
metadata.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
continue;
}
if (!xforms.TryGetComponent(ent, out var xform))
continue;
// 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);
@@ -872,7 +907,7 @@ namespace Robust.Client.GameStates
if (deleteClientChildren
&& !deleteClientEntities // don't add duplicates
&& child.Value.IsClientSide())
&& _entities.IsClientSide(child.Value))
{
toDelete.Add(child.Value);
}
@@ -887,7 +922,7 @@ namespace Robust.Client.GameStates
}
private void ProcessDeletions(
ReadOnlySpan<EntityUid> delSpan,
ReadOnlySpan<NetEntity> delSpan,
EntityQuery<TransformComponent> xforms,
EntityQuery<MetaDataComponent> metas,
SharedTransformSystem xformSys)
@@ -904,13 +939,19 @@ namespace Robust.Client.GameStates
using var _ = _prof.Group("Deletion");
foreach (var id in delSpan)
foreach (var netEntity in delSpan)
{
// Don't worry about this for later.
_entityManager.PendingNetEntityStates.Remove(netEntity);
if (!_entityManager.TryGetEntity(netEntity, out var id))
continue;
if (!xforms.TryGetComponent(id, out var xform))
continue; // Already deleted? or never sent to us?
// First, a single recursive map change
xformSys.DetachParentToNull(id, xform);
xformSys.DetachParentToNull(id.Value, xform);
// Then detach all children.
var childEnumerator = xform.ChildEnumerator;
@@ -920,12 +961,12 @@ namespace Robust.Client.GameStates
}
// Finally, delete the entity.
_entities.DeleteEntity(id);
_entities.DeleteEntity(id.Value);
}
_prof.WriteValue("Count", ProfData.Int32(delSpan.Length));
}
public void DetachImmediate(List<EntityUid> entities)
public void DetachImmediate(List<NetEntity> entities)
{
var metas = _entities.GetEntityQuery<MetaDataComponent>();
var xforms = _entities.GetEntityQuery<TransformComponent>();
@@ -935,7 +976,7 @@ namespace Robust.Client.GameStates
Detach(GameTick.MaxValue, null, entities, metas, xforms, xformSys, containerSys, lookupSys);
}
private List<EntityUid> ProcessPvsDeparture(
private List<NetEntity> ProcessPvsDeparture(
GameTick toTick,
EntityQuery<MetaDataComponent> metas,
EntityQuery<TransformComponent> xforms,
@@ -944,7 +985,7 @@ namespace Robust.Client.GameStates
EntityLookupSystem lookupSys)
{
var toDetach = _processor.GetEntitiesToDetach(toTick, _pvsDetachBudget);
var detached = new List<EntityUid>();
var detached = new List<NetEntity>();
if (toDetach.Count == 0)
return detached;
@@ -966,16 +1007,18 @@ namespace Robust.Client.GameStates
private void Detach(GameTick maxTick,
GameTick? lastStateApplied,
List<EntityUid> entities,
List<NetEntity> entities,
EntityQuery<MetaDataComponent> metas,
EntityQuery<TransformComponent> xforms,
SharedTransformSystem xformSys,
ContainerSystem containerSys,
EntityLookupSystem lookupSys,
List<EntityUid>? detached = null)
List<NetEntity>? detached = null)
{
foreach (var ent in entities)
foreach (var netEntity in entities)
{
var ent = _entityManager.GetEntity(netEntity);
if (!metas.TryGetComponent(ent, out var meta))
continue;
@@ -1000,7 +1043,7 @@ namespace Robust.Client.GameStates
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
// In those situations, we need to add the entity back to the list of expected entities after detaching.
IContainer? container = null;
BaseContainer? container = null;
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
@@ -1014,35 +1057,38 @@ namespace Robust.Client.GameStates
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
if (container != null)
containerSys.AddExpectedEntity(ent, container);
containerSys.AddExpectedEntity(netEntity, container);
}
detached?.Add(ent);
detached?.Add(netEntity);
}
}
private void InitializeAndStart(Dictionary<EntityUid, EntityState> toCreate)
private void InitializeAndStart(Dictionary<NetEntity, EntityState> toCreate)
{
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
#if EXCEPTION_TOLERANCE
HashSet<EntityUid> brokenEnts = new HashSet<EntityUid>();
var brokenEnts = new List<EntityUid>();
#endif
using (_prof.Group("Initialize Entity"))
{
foreach (var entity in toCreate.Keys)
foreach (var netEntity in toCreate.Keys)
{
var entity = _entityManager.GetEntity(netEntity);
#if EXCEPTION_TOLERANCE
try
{
#endif
_entities.InitializeEntity(entity);
_entities.InitializeEntity(entity, metaQuery.GetComponent(entity));
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
{
_sawmill.Error($"Server entity threw in Init: ent={_entityManager.ToPrettyString(entity)}");
_sawmill.Error($"Server entity threw in Init: ent={_entities.ToPrettyString(entity)}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
brokenEnts.Add(entity);
toCreate.Remove(entity);
toCreate.Remove(netEntity);
}
#endif
}
@@ -1050,8 +1096,9 @@ namespace Robust.Client.GameStates
using (_prof.Group("Start Entity"))
{
foreach (var entity in toCreate.Keys)
foreach (var netEntity in toCreate.Keys)
{
var entity = _entityManager.GetEntity(netEntity);
#if EXCEPTION_TOLERANCE
try
{
@@ -1064,7 +1111,7 @@ namespace Robust.Client.GameStates
_sawmill.Error($"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}");
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
brokenEnts.Add(entity);
toCreate.Remove(entity);
toCreate.Remove(netEntity);
}
#endif
}
@@ -1081,8 +1128,9 @@ namespace Robust.Client.GameStates
private void HandleEntityState(EntityUid uid, IEventBus bus, EntityState? curState,
EntityState? nextState, GameTick lastApplied, GameTick toTick, bool enteringPvs)
{
var size = (curState?.ComponentChanges.Span.Length ?? 0) + (nextState?.ComponentChanges.Span.Length ?? 0);
var compStateWork = new Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)>(size);
_compStateWork.Clear();
var meta = _entityManager.GetComponent<MetaDataComponent>(uid);
var netEntity = meta.NetEntity;
// First remove any deleted components
if (curState?.NetComponents != null)
@@ -1107,7 +1155,7 @@ namespace Robust.Client.GameStates
//
// as to why we need to reset: because in the process of detaching to null-space, we will have dirtied
// the entity. most notably, all entities will have been ejected from their containers.
foreach (var (id, state) in _processor.GetLastServerStates(uid))
foreach (var (id, state) in _processor.GetLastServerStates(netEntity))
{
if (!_entityManager.TryGetComponent(uid, id, out var comp))
{
@@ -1117,7 +1165,7 @@ namespace Robust.Client.GameStates
_entityManager.AddComponent(uid, newComp, true);
}
compStateWork[id] = (comp, state, null);
_compStateWork[id] = (comp, state, null);
}
}
else if (curState != null)
@@ -1134,7 +1182,7 @@ namespace Robust.Client.GameStates
else if (compChange.LastModifiedTick <= lastApplied && lastApplied != GameTick.Zero)
continue;
compStateWork[compChange.NetID] = (comp, compChange.State, null);
_compStateWork[compChange.NetID] = (comp, compChange.State, null);
}
}
@@ -1152,14 +1200,38 @@ namespace Robust.Client.GameStates
continue;
}
if (compStateWork.TryGetValue(compState.NetID, out var state))
compStateWork[compState.NetID] = (comp, state.curState, compState.State);
if (_compStateWork.TryGetValue(compState.NetID, out var state))
_compStateWork[compState.NetID] = (comp, state.curState, compState.State);
else
compStateWork[compState.NetID] = (comp, null, compState.State);
_compStateWork[compState.NetID] = (comp, null, compState.State);
}
}
foreach (var (comp, cur, next) in compStateWork.Values)
// If we have a NetEntity we reference come in then apply their state.
if (_pendingReapplyNetStates.TryGetValue(uid, out var reapplyTypes))
{
var lastState = _processor.GetLastServerStates(netEntity);
foreach (var type in reapplyTypes)
{
var compRef = _compFactory.GetRegistration(type);
var netId = compRef.NetID;
if (netId == null)
continue;
if (_compStateWork.ContainsKey(netId.Value) ||
!_entityManager.TryGetComponent(uid, type, out var comp) ||
!lastState.TryGetValue(netId.Value, out var lastCompState))
{
continue;
}
_compStateWork[netId.Value] = (comp, lastCompState, null);
}
}
foreach (var (comp, cur, next) in _compStateWork.Values)
{
try
{
@@ -1180,6 +1252,7 @@ namespace Robust.Client.GameStates
}
#region Debug Commands
private bool TryParseUid(IConsoleShell shell, string[] args, out EntityUid uid, [NotNullWhen(true)] out MetaDataComponent? meta)
{
if (args.Length != 1)
@@ -1238,7 +1311,7 @@ namespace Robust.Client.GameStates
var xform = _entities.GetComponent<TransformComponent>(uid);
if (xform.ParentUid.IsValid())
{
IContainer? container = null;
BaseContainer? container = null;
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
_entities.TryGetComponent(xform.ParentUid, out MetaDataComponent? containerMeta) &&
(containerMeta.Flags & MetaDataFlags.Detached) == 0)
@@ -1249,7 +1322,7 @@ namespace Robust.Client.GameStates
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
if (container != null)
containerSys.AddExpectedEntity(uid, container);
containerSys.AddExpectedEntity(_entities.GetNetEntity(uid), container);
}
}
@@ -1266,18 +1339,21 @@ namespace Robust.Client.GameStates
// If this is not a client-side entity, it also needs to be removed from the full-server state dictionary to
// avoid errors. This has to be done recursively for all children.
void _recursiveRemoveState(TransformComponent xform, EntityQuery<TransformComponent> query)
void _recursiveRemoveState(NetEntity netEntity, TransformComponent xform, EntityQuery<MetaDataComponent> metaQuery, EntityQuery<TransformComponent> xformQuery)
{
_processor._lastStateFullRep.Remove(xform.Owner);
_processor._lastStateFullRep.Remove(netEntity);
foreach (var child in xform.ChildEntities)
{
if (query.TryGetComponent(child, out var childXform))
_recursiveRemoveState(childXform, query);
if (xformQuery.TryGetComponent(child, out var childXform) &&
metaQuery.TryGetComponent(child, out var childMeta))
{
_recursiveRemoveState(childMeta.NetEntity, childXform, metaQuery, xformQuery);
}
}
}
if (!uid.IsClientSide() && _entities.TryGetComponent(uid, out TransformComponent? xform))
_recursiveRemoveState(xform, _entities.GetEntityQuery<TransformComponent>());
if (!_entities.IsClientSide(uid) && _entities.TryGetComponent(uid, out TransformComponent? xform))
_recursiveRemoveState(meta.NetEntity, xform, _entities.GetEntityQuery<MetaDataComponent>(), _entities.GetEntityQuery<TransformComponent>());
// Set ApplyingState to true to avoid logging errors about predicting the deletion of networked entities.
using (_timing.StartStateApplicationArea())
@@ -1311,7 +1387,7 @@ namespace Robust.Client.GameStates
meta.Flags &= ~MetaDataFlags.Detached;
if (!_processor.TryGetLastServerStates(uid, out var lastState))
if (!_processor.TryGetLastServerStates(meta.NetEntity, out var lastState))
return;
foreach (var (id, state) in lastState)
@@ -1352,9 +1428,9 @@ namespace Robust.Client.GameStates
public sealed class GameStateAppliedArgs : EventArgs
{
public GameState AppliedState { get; }
public readonly List<EntityUid> Detached;
public readonly List<NetEntity> Detached;
public GameStateAppliedArgs(GameState appliedState, List<EntityUid> detached)
public GameStateAppliedArgs(GameState appliedState, List<NetEntity> detached)
{
AppliedState = appliedState;
Detached = detached;
@@ -1363,12 +1439,12 @@ namespace Robust.Client.GameStates
public sealed class MissingMetadataException : Exception
{
public readonly EntityUid Uid;
public readonly NetEntity NetEntity;
public MissingMetadataException(EntityUid uid)
: base($"Server state is missing the metadata component for a new entity: {uid}.")
public MissingMetadataException(NetEntity netEntity)
: base($"Server state is missing the metadata component for a new entity: {netEntity}.")
{
Uid = uid;
NetEntity = netEntity;
}
}
}

View File

@@ -20,7 +20,7 @@ namespace Robust.Client.GameStates
private readonly List<GameState> _stateBuffer = new();
private readonly Dictionary<GameTick, List<EntityUid>> _pvsDetachMessages = new();
private readonly Dictionary<GameTick, List<NetEntity>> _pvsDetachMessages = new();
private ISawmill _logger = default!;
private ISawmill _stateLogger = default!;
@@ -44,7 +44,7 @@ namespace Robust.Client.GameStates
/// <summary>
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
/// </summary>
internal readonly Dictionary<EntityUid, Dictionary<ushort, ComponentState>> _lastStateFullRep
internal readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _lastStateFullRep
= new();
/// <inheritdoc />
@@ -178,10 +178,10 @@ namespace Robust.Client.GameStates
foreach (var entityState in state.EntityStates.Span)
{
if (!_lastStateFullRep.TryGetValue(entityState.Uid, out var compData))
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
{
compData = new Dictionary<ushort, ComponentState>();
_lastStateFullRep.Add(entityState.Uid, compData);
_lastStateFullRep.Add(entityState.NetEntity, compData);
}
foreach (var change in entityState.ComponentChanges.Span)
@@ -263,7 +263,7 @@ namespace Robust.Client.GameStates
return false;
}
internal void AddLeavePvsMessage(List<EntityUid> entities, GameTick tick)
internal void AddLeavePvsMessage(List<NetEntity> entities, GameTick tick)
{
// Late message may still need to be processed,
DebugTools.Assert(entities.Count > 0);
@@ -272,9 +272,9 @@ namespace Robust.Client.GameStates
public void ClearDetachQueue() => _pvsDetachMessages.Clear();
public List<(GameTick Tick, List<EntityUid> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
public List<(GameTick Tick, List<NetEntity> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
{
var result = new List<(GameTick Tick, List<EntityUid> Entities)>();
var result = new List<(GameTick Tick, List<NetEntity> Entities)>();
foreach (var (tick, entities) in _pvsDetachMessages)
{
if (tick > toTick)
@@ -353,11 +353,11 @@ namespace Robust.Client.GameStates
LastFullStateRequested = _timing.LastRealTick;
}
public void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> implicitData)
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
{
foreach (var (uid, implicitEntState) in implicitData)
foreach (var (netEntity, implicitEntState) in implicitData)
{
var fullRep = _lastStateFullRep[uid];
var fullRep = _lastStateFullRep[netEntity];
foreach (var (netId, implicitCompState) in implicitEntState)
{
@@ -374,7 +374,7 @@ namespace Robust.Client.GameStates
// state from the entity prototype.
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
{
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {uid}");
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
continue;
}
@@ -385,17 +385,17 @@ namespace Robust.Client.GameStates
}
}
public Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity)
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
{
return _lastStateFullRep[entity];
return _lastStateFullRep[netEntity];
}
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
{
return _lastStateFullRep;
}
public bool TryGetLastServerStates(EntityUid entity,
public bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary)
{
return _lastStateFullRep.TryGetValue(entity, out dictionary);

View File

@@ -75,7 +75,7 @@ namespace Robust.Client.GameStates
/// <summary>
/// Applies a given set of game states.
/// </summary>
IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState);
IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState);
/// <summary>
/// Resets any entities that have changed while predicting future ticks.
@@ -86,12 +86,12 @@ namespace Robust.Client.GameStates
/// An input command has been dispatched.
/// </summary>
/// <param name="message">Message being dispatched.</param>
void InputCommandDispatched(FullInputCmdMessage message);
void InputCommandDispatched(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message);
/// <summary>
/// Requests a full state from the server. This should override even implicit entity data.
/// </summary>
void RequestFullState(EntityUid? missingEntity = null);
void RequestFullState(NetEntity? missingEntity = null);
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
@@ -105,7 +105,7 @@ namespace Robust.Client.GameStates
/// <summary>
/// Returns the full collection of cached game states that are used to reset predicted entities.
/// </summary>
Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep();
Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep();
/// <summary>
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state
@@ -144,12 +144,12 @@ namespace Robust.Client.GameStates
/// Queue a collection of entities that are to be detached to null-space & marked as PVS-detached.
/// This store and modify the list given to it.
/// </summary>
void QueuePvsDetach(List<EntityUid> entities, GameTick tick);
void QueuePvsDetach(List<NetEntity> entities, GameTick tick);
/// <summary>
/// Immediately detach several entities.
/// </summary>
void DetachImmediate(List<EntityUid> entities);
void DetachImmediate(List<NetEntity> entities);
/// <summary>
/// Clears the PVS detach queue.

View File

@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
/// The data to merge.
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
/// </param>
void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> data);
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> data);
/// <summary>
/// Get the last state data from the server for an entity.
/// </summary>
/// <returns>Dictionary (net ID -> ComponentState)</returns>
Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity);
Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity entity);
/// <summary>
/// Calculate the number of applicable states in the game state buffer from a given tick.
@@ -98,7 +98,7 @@ namespace Robust.Client.GameStates
/// <param name="fromTick">The tick to calculate from.</param>
int CalculateBufferSize(GameTick fromTick);
bool TryGetLastServerStates(EntityUid entity,
bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary);
}
}

View File

@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
private readonly Font _font;
private readonly int _lineHeight;
private readonly Dictionary<EntityUid, NetEntData> _netEnts = new();
private readonly Dictionary<NetEntity, NetEntData> _netEnts = new();
public NetEntityOverlay()
{
@@ -77,12 +77,12 @@ namespace Robust.Client.GameStates
foreach (var entityState in gameState.EntityStates.Span)
{
if (!_netEnts.TryGetValue(entityState.Uid, out var netEnt))
if (!_netEnts.TryGetValue(entityState.NetEntity, out var netEnt))
{
if (_netEnts.Count >= _maxEnts)
continue;
_netEnts[entityState.Uid] = netEnt = new();
_netEnts[entityState.NetEntity] = netEnt = new();
}
if (!netEnt.InPVS && netEnt.LastUpdate < gameState.ToSequence)
@@ -119,11 +119,13 @@ namespace Robust.Client.GameStates
var screenHandle = args.ScreenHandle;
int i = 0;
foreach (var (uid, netEnt) in _netEnts)
foreach (var (nent, netEnt) in _netEnts)
{
var uid = _entityManager.GetEntity(nent);
if (!_entityManager.EntityExists(uid))
{
_netEnts.Remove(uid);
_netEnts.Remove(nent);
continue;
}

View File

@@ -26,6 +26,7 @@ namespace Robust.Client.GameStates
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
private const int HistorySize = 60 * 5; // number of ticks to keep in history.
private const int TargetPayloadBps = 56000 / 8; // Target Payload size in Bytes per second. A mind-numbing fifty-six thousand bits per second, who would ever need more?
@@ -73,7 +74,7 @@ namespace Robust.Client.GameStates
_history.Add((toSeq, sz, lag, buffer));
// not watching an ent
if(!WatchEntId.IsValid() || WatchEntId.IsClientSide())
if(!WatchEntId.IsValid() || _entManager.IsClientSide(WatchEntId))
return;
string? entStateString = null;
@@ -86,7 +87,9 @@ namespace Robust.Client.GameStates
var sb = new StringBuilder();
foreach (var entState in entStates.Span)
{
if (entState.Uid != WatchEntId)
var uid = _entManager.GetEntity(entState.NetEntity);
if (uid != WatchEntId)
continue;
if (!entState.ComponentChanges.HasContents)
@@ -115,7 +118,9 @@ namespace Robust.Client.GameStates
foreach (var ent in args.Detached)
{
if (ent != WatchEntId)
var uid = _entManager.GetEntity(ent);
if (uid != WatchEntId)
continue;
conShell.WriteLine($"watchEnt: Left PVS at tick {args.AppliedState.ToSequence}, eid={WatchEntId}" + "\n");
@@ -126,7 +131,9 @@ namespace Robust.Client.GameStates
{
foreach (var entDelete in entDeletes.Span)
{
if (entDelete == WatchEntId)
var uid = _entManager.GetEntity(entDelete);
if (uid == WatchEntId)
entDelString = "\n Deleted";
}
}
@@ -294,30 +301,33 @@ namespace Robust.Client.GameStates
private sealed class NetWatchEntCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override string Command => "net_watchent";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
EntityUid eValue;
EntityUid? entity;
if (args.Length == 0)
{
eValue = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
entity = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
}
else if (!EntityUid.TryParse(args[0], out eValue))
else if (!NetEntity.TryParse(args[0], out var netEntity) || !_entManager.TryGetEntity(netEntity, out entity))
{
shell.WriteError("Invalid argument: Needs to be 0 or an entityId.");
return;
}
var overlayMan = IoCManager.Resolve<IOverlayManager>();
if (!overlayMan.TryGetOverlay(out NetGraphOverlay? overlay))
if (!_overlayManager.TryGetOverlay(out NetGraphOverlay? overlay))
{
overlay = new();
overlayMan.AddOverlay(overlay);
overlay = new NetGraphOverlay();
_overlayManager.AddOverlay(overlay);
}
overlay.WatchEntId = eValue;
overlay.WatchEntId = entity.Value;
}
}
}

View File

@@ -62,8 +62,9 @@ namespace Robust.Client.Physics
Log.Info($"Received grid fixture debug data");
if (!_enableDebug) return;
_nodes[ev.Grid] = ev.Nodes;
_connections[ev.Grid] = ev.Connections;
var grid = GetEntity(ev.Grid);
_nodes[grid] = ev.Nodes;
_connections[grid] = ev.Connections;
}
private sealed class GridSplitNodeOverlay : Overlay

View File

@@ -20,7 +20,7 @@ namespace Robust.Client.Physics
{
if (args.Current is not JointComponentState jointState) return;
component.Relay = jointState.Relay;
component.Relay = EnsureEntity<JointComponent>(jointState.Relay, uid);
// Initial state gets applied before the entity (& entity's transform) have been initialized.
// So just let joint init code handle that.
@@ -29,7 +29,7 @@ namespace Robust.Client.Physics
component.Joints.Clear();
foreach (var (id, state) in jointState.Joints)
{
component.Joints[id] = state.GetJoint();
component.Joints[id] = state.GetJoint(EntityManager, uid);
}
return;
}
@@ -62,8 +62,8 @@ namespace Robust.Client.Physics
continue;
}
var other = state.UidA == uid ? state.UidB : state.UidA;
var uidA = GetEntity(state.UidA);
var other = uidA == uid ? GetEntity(state.UidB) : uidA;
// Add new joint (if possible).
// Need to wait for BOTH joint components to come in first before we can add it. Yay dependencies!
@@ -82,11 +82,11 @@ namespace Robust.Client.Physics
// TODO: component state handling ordering.
if (Transform(uid).MapID == MapId.Nullspace)
{
AddedJoints.Add(state.GetJoint());
AddedJoints.Add(state.GetJoint(EntityManager, uid));
continue;
}
AddJoint(state.GetJoint());
AddJoint(state.GetJoint(EntityManager, uid));
}
}
}

View File

@@ -225,7 +225,7 @@ namespace Robust.Client.Placement
}
}))
.Bind(EngineKeyFunctions.EditorPlaceObject, new PointerStateInputCmdHandler(
(session, coords, uid) =>
(session, netCoords, nent) =>
{
if (!IsActive)
return false;
@@ -239,15 +239,15 @@ namespace Robust.Client.Placement
if (Eraser)
{
if (HandleDeletion(coords))
if (HandleDeletion(netCoords))
return true;
if (uid == EntityUid.Invalid)
if (nent == EntityUid.Invalid)
{
return false;
}
HandleDeletion(uid);
HandleDeletion(nent);
}
else
{
@@ -428,7 +428,7 @@ namespace Robust.Client.Placement
var msg = new MsgPlacement();
msg.PlaceType = PlacementManagerMessage.RequestEntRemove;
msg.EntityUid = entity;
msg.EntityUid = EntityManager.GetNetEntity(entity);
_networkManager.ClientSendMessage(msg);
}
@@ -436,7 +436,7 @@ namespace Robust.Client.Placement
{
var msg = new MsgPlacement();
msg.PlaceType = PlacementManagerMessage.RequestRectRemove;
msg.EntityCoordinates = new EntityCoordinates(StartPoint.EntityId, rect.BottomLeft);
msg.NetCoordinates = new NetCoordinates(EntityManager.GetNetEntity(StartPoint.EntityId), rect.BottomLeft);
msg.RectSize = rect.Size;
_networkManager.ClientSendMessage(msg);
}
@@ -790,7 +790,7 @@ namespace Robust.Client.Placement
message.EntityTemplateName = CurrentPermission.EntityType;
// world x and y
message.EntityCoordinates = coordinates;
message.NetCoordinates = EntityManager.GetNetCoordinates(coordinates);
message.DirRcv = Direction;

View File

@@ -6,6 +6,7 @@ using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Players;
@@ -24,6 +25,7 @@ namespace Robust.Client.Player
[Dependency] private readonly IClientNetManager _network = default!;
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly ILogManager _logMan = default!;
/// <summary>
/// Active sessions of connected clients to the server.
@@ -65,6 +67,8 @@ namespace Robust.Client.Player
}
}
private LocalPlayer? _localPlayer;
private ISawmill _sawmill = default!;
public event Action<LocalPlayerChangedEventArgs>? LocalPlayerChanged;
/// <inheritdoc />
@@ -82,6 +86,7 @@ namespace Robust.Client.Player
{
_client.RunLevelChanged += OnRunLevelChanged;
_sawmill = _logMan.GetSawmill("player");
_network.RegisterNetMessage<MsgPlayerListReq>();
_network.RegisterNetMessage<MsgPlayerList>(HandlePlayerList);
}
@@ -122,7 +127,13 @@ namespace Robust.Client.Player
if (myState != null)
{
UpdateAttachedEntity(myState.ControlledEntity);
var uid = _entManager.GetEntity(myState.ControlledEntity);
if (myState.ControlledEntity is {Valid: true} && !_entManager.EntityExists(uid))
{
_sawmill.Error($"Received player state for local player with an unknown net entity!");
}
UpdateAttachedEntity(uid);
UpdateSessionStatus(myState.Status);
}
@@ -181,11 +192,13 @@ namespace Robust.Client.Player
if (_sessions.TryGetValue(state.UserId, out var session))
{
var local = (PlayerSession) session;
var controlled = _entManager.GetEntity(state.ControlledEntity);
// Exists, update data.
if (local.Name == state.Name
&& local.Status == state.Status
&& local.Ping == state.Ping
&& local.AttachedEntity == state.ControlledEntity)
&& local.AttachedEntity == controlled)
{
continue;
}
@@ -194,7 +207,7 @@ namespace Robust.Client.Player
local.Name = state.Name;
local.Status = state.Status;
local.Ping = state.Ping;
local.AttachedEntity = state.ControlledEntity;
local.AttachedEntity = controlled;
}
else
{
@@ -206,7 +219,7 @@ namespace Robust.Client.Player
Name = state.Name,
Status = state.Status,
Ping = state.Ping,
AttachedEntity = state.ControlledEntity,
AttachedEntity = _entManager.GetEntity(state.ControlledEntity),
};
_sessions.Add(state.UserId, newSession);
if (state.UserId == LocalPlayer!.UserId)

View File

@@ -83,8 +83,8 @@ public sealed partial class ReplayLoadManager
}
HashSet<ResPath> uploadedFiles = new();
var detached = new HashSet<EntityUid>();
var detachQueue = new Dictionary<GameTick, List<EntityUid>>();
var detached = new HashSet<NetEntity>();
var detachQueue = new Dictionary<GameTick, List<NetEntity>>();
if (initMessages != null)
UpdateMessages(initMessages, uploadedFiles, prototypes, cvars, detachQueue, ref timeBase, true);
@@ -92,11 +92,11 @@ public sealed partial class ReplayLoadManager
ProcessQueue(GameTick.MaxValue, detachQueue, detached);
var entSpan = state0.EntityStates.Value;
Dictionary<EntityUid, EntityState> entStates = new(entSpan.Count);
Dictionary<NetEntity, EntityState> entStates = new(entSpan.Count);
foreach (var entState in entSpan)
{
var modifiedState = AddImplicitData(entState);
entStates.Add(entState.Uid, modifiedState);
entStates.Add(entState.NetEntity, modifiedState);
}
await callback(0, states.Count, LoadingState.ProcessingFiles, true);
@@ -112,11 +112,11 @@ public sealed partial class ReplayLoadManager
default,
entStates.Values.ToArray(),
playerStates.Values.ToArray(),
Array.Empty<EntityUid>());
Array.Empty<NetEntity>());
checkPoints.Add(new CheckpointState(state0, timeBase, cvars, 0, detached));
DebugTools.Assert(state0.EntityDeletions.Value.Count == 0);
var empty = Array.Empty<EntityUid>();
var empty = Array.Empty<NetEntity>();
TimeSpan GetTime(GameTick tick)
{
@@ -176,8 +176,8 @@ public sealed partial class ReplayLoadManager
private void ProcessQueue(
GameTick curTick,
Dictionary<GameTick, List<EntityUid>> detachQueue,
HashSet<EntityUid> detached)
Dictionary<GameTick, List<NetEntity>> detachQueue,
HashSet<NetEntity> detached)
{
foreach (var (tick, ents) in detachQueue)
{
@@ -192,7 +192,7 @@ public sealed partial class ReplayLoadManager
HashSet<ResPath> uploadedFiles,
Dictionary<Type, HashSet<string>> prototypes,
Dictionary<string, object> cvars,
Dictionary<GameTick, List<EntityUid>> detachQueue,
Dictionary<GameTick, List<NetEntity>> detachQueue,
ref (TimeSpan, GameTick) timeBase,
bool ignoreDuplicates = false)
{
@@ -301,8 +301,8 @@ public sealed partial class ReplayLoadManager
_locMan.ReloadLocalizations();
}
private void UpdateDeletions(NetListAsArray<EntityUid> entityDeletions,
Dictionary<EntityUid, EntityState> entStates, HashSet<EntityUid> detached)
private void UpdateDeletions(NetListAsArray<NetEntity> entityDeletions,
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached)
{
foreach (var ent in entityDeletions.Span)
{
@@ -311,16 +311,16 @@ public sealed partial class ReplayLoadManager
}
}
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<EntityUid, EntityState> entStates,
ref int spawnedTracker, ref int stateTracker, HashSet<EntityUid> detached)
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<NetEntity, EntityState> entStates,
ref int spawnedTracker, ref int stateTracker, HashSet<NetEntity> detached)
{
foreach (var entState in span)
{
detached.Remove(entState.Uid);
if (!entStates.TryGetValue(entState.Uid, out var oldEntState))
detached.Remove(entState.NetEntity);
if (!entStates.TryGetValue(entState.NetEntity, out var oldEntState))
{
var modifiedState = AddImplicitData(entState);
entStates[entState.Uid] = modifiedState;
entStates[entState.NetEntity] = modifiedState;
spawnedTracker++;
#if DEBUG
@@ -333,11 +333,11 @@ public sealed partial class ReplayLoadManager
}
stateTracker++;
DebugTools.Assert(oldEntState.Uid == entState.Uid);
entStates[entState.Uid] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
DebugTools.Assert(oldEntState.NetEntity == entState.NetEntity);
entStates[entState.NetEntity] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
#if DEBUG
foreach (var state in entStates[entState.Uid].ComponentChanges.Span)
foreach (var state in entStates[entState.NetEntity].ComponentChanges.Span)
{
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
}
@@ -388,7 +388,7 @@ public sealed partial class ReplayLoadManager
}
DebugTools.Assert(newState.NetComponents == null || newState.NetComponents.Count == combined.Count);
return new EntityState(newState.Uid, combined, newState.EntityLastModified, newState.NetComponents ?? oldNetComps);
return new EntityState(newState.NetEntity, combined, newState.EntityLastModified, newState.NetComponents ?? oldNetComps);
}
private void UpdatePlayerStates(ReadOnlySpan<PlayerState> span, Dictionary<NetUserId, PlayerState> playerStates)

View File

@@ -70,14 +70,14 @@ public sealed partial class ReplayLoadManager
{
// This shouldn't be possible, yet it has happened?
// TODO this should probably also throw an exception.
_sawmill.Error($"Encountered blank entity state? Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}. Attempting to continue.");
_sawmill.Error($"Encountered blank entity state? Entity: {entState.NetEntity}. Last modified: {entState.EntityLastModified}. Attempting to continue.");
return null;
}
if (!_confMan.GetCVar(CVars.ReplayIgnoreErrors))
throw new MissingMetadataException(entState.Uid);
throw new MissingMetadataException(entState.NetEntity);
_sawmill.Error($"Missing metadata component. Entity: {entState.Uid}. Last modified: {entState.EntityLastModified}.");
_sawmill.Error($"Missing metadata component. Entity: {entState.NetEntity}. Last modified: {entState.EntityLastModified}.");
return null;
}
}

View File

@@ -68,10 +68,18 @@ public sealed partial class ReplayLoadManager
var metaState = (MetaDataComponentState?)ent.ComponentChanges.Value?
.FirstOrDefault(c => c.NetID == _metaId).State;
if (metaState == null)
throw new MissingMetadataException(ent.Uid);
throw new MissingMetadataException(ent.NetEntity);
_entMan.CreateEntityUninitialized(metaState.PrototypeId, ent.Uid);
entities.Add(ent.Uid);
var uid = _entMan.CreateEntityUninitialized(metaState.PrototypeId);
entities.Add(uid);
var metaComp = _entMan.GetComponent<MetaDataComponent>(uid);
// Client creates a client-side net entity for the newly created entity.
// We need to clear this mapping before assigning the real net id.
// TODO NetEntity Jank: prevent the client from creating this in the first place.
_entMan.ClearNetEntity(metaComp.NetEntity);
_entMan.SetNetEntity(uid, ent.NetEntity, metaComp);
if (i++ % 50 == 0)
{

View File

@@ -19,7 +19,7 @@ public sealed partial class ReplayLoadManager : IReplayLoadManager
{
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly EntityManager _entMan = default!;
[Dependency] private readonly IClientGameTiming _timing = default!;
[Dependency] private readonly IClientNetManager _netMan = default!;
[Dependency] private readonly IComponentFactory _factory = default!;

View File

@@ -82,19 +82,28 @@ internal sealed partial class ReplayPlaybackManager
var metas = _entMan.GetEntityQuery<MetaDataComponent>();
foreach (var es in checkpoint.DetachedStates)
{
if (metas.TryGetComponent(es.Uid, out var meta) && !meta.EntityDeleted)
var uid = _entMan.GetEntity(es.NetEntity);
if (metas.TryGetComponent(uid, out var meta) && !meta.EntityDeleted)
continue;
;
var metaState = (MetaDataComponentState?)es.ComponentChanges.Value?
.FirstOrDefault(c => c.NetID == _metaId).State;
if (metaState == null)
throw new MissingMetadataException(es.Uid);
throw new MissingMetadataException(es.NetEntity);
_entMan.CreateEntityUninitialized(metaState.PrototypeId, es.Uid);
meta = metas.GetComponent(es.Uid);
_entMan.InitializeEntity(es.Uid, meta);
_entMan.StartEntity(es.Uid);
_entMan.CreateEntityUninitialized(metaState.PrototypeId, uid);
meta = metas.GetComponent(uid);
// Client creates a client-side net entity for the newly created entity.
// We need to clear this mapping before assigning the real net id.
// TODO NetEntity Jank: prevent the client from creating this in the first place.
_entMan.ClearNetEntity(meta.NetEntity);
_entMan.SetNetEntity(uid, es.NetEntity, meta);
_entMan.InitializeEntity(uid, meta);
_entMan.StartEntity(uid);
meta.LastStateApplied = checkpoint.Tick;
}
}

View File

@@ -50,7 +50,7 @@ internal sealed partial class ReplayPlaybackManager
}
_timing.CurTick += 1;
_entMan.TickUpdate(args.DeltaSeconds, noPredictions: true);
_cEntManager.TickUpdate(args.DeltaSeconds, noPredictions: true);
if (!Playing || AutoPauseCountdown == null)
return;
@@ -82,7 +82,7 @@ internal sealed partial class ReplayPlaybackManager
// Maybe track our own detach queue and use _gameState.DetachImmediate()?
// That way we don't have to clone this. Downside would be that all entities will be immediately
// detached. I.e., the detach budget cvar will simply be ignored.
var clone = new List<EntityUid>(leavePvs.Entities);
var clone = new List<NetEntity>(leavePvs.Entities);
_gameState.QueuePvsDetach(clone, leavePvs.Tick);
continue;

View File

@@ -33,7 +33,8 @@ internal sealed partial class ReplayPlaybackManager : IReplayPlaybackManager
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IGameController _controller = default!;
[Dependency] private readonly IClientEntityManager _entMan = default!;
[Dependency] private readonly IClientEntityManager _cEntManager = default!;
[Dependency] private readonly ClientEntityManager _entMan = default!;
[Dependency] private readonly IConfigurationManager _confMan = default!;
[Dependency] private readonly NetworkResourceManager _netResMan = default!;
[Dependency] private readonly IClientGameStateManager _gameState = default!;

View File

@@ -95,12 +95,12 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
{
var tick = _timing.LastRealTick;
var players = _player.Sessions.Select(GetPlayerState).ToArray();
var deletions = Array.Empty<EntityUid>();
var deletions = Array.Empty<NetEntity>();
var fullRep = _state.GetFullRep();
var entStates = new EntityState[fullRep.Count];
var i = 0;
foreach (var (uid, dict) in fullRep)
foreach (var (netEntity, dict) in fullRep)
{
var compData = new ComponentChange[dict.Count];
var netComps = new HashSet<ushort>(dict.Keys);
@@ -110,7 +110,7 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
compData[j++] = new ComponentChange(id, compState, tick);
}
entStates[i++] = new EntityState(uid, compData, tick, netComps);
entStates[i++] = new EntityState(netEntity, compData, tick, netComps);
}
var state = new GameState(
@@ -121,16 +121,17 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
players,
deletions);
var detached = new List<EntityUid>();
var detached = new List<NetEntity>();
var query = _entMan.AllEntityQueryEnumerator<MetaDataComponent>();
while (query.MoveNext(out var uid, out var comp))
{
if (uid.IsClientSide())
if (_entMan.IsClientSide(uid))
continue;
DebugTools.Assert(fullRep.ContainsKey(uid));
var nent = comp.NetEntity;
DebugTools.Assert(fullRep.ContainsKey(nent));
if ((comp.Flags & MetaDataFlags.Detached) != 0)
detached.Add(uid);
detached.Add(nent);
}
var detachMsg = detached.Count > 0 ? new ReplayMessage.LeavePvs(detached, tick) : null;
@@ -144,7 +145,7 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
UserId = session.UserId,
Status = session.Status,
Name = session.Name,
ControlledEntity = session.AttachedEntity,
ControlledEntity = _entMan.GetNetEntity(session.AttachedEntity),
};
}
}

View File

@@ -218,7 +218,7 @@ namespace Robust.Client.ViewVariables
{
// TODO: more flexibility in allowing custom instances here.
ViewVariablesInstance instance;
if (obj is EntityUid entity && _entityManager.EntityExists(entity))
if (obj is NetEntity netEntity && _entityManager.GetEntity(netEntity).IsValid())
{
instance = new ViewVariablesInstanceEntity(this, _entityManager, _robustSerializer, Sawmill);
}
@@ -269,7 +269,7 @@ namespace Robust.Client.ViewVariables
var type = Type.GetType(blob.ObjectType);
// TODO: more flexibility in allowing custom instances here.
ViewVariablesInstance instance;
if (type != null && typeof(EntityUid).IsAssignableFrom(type))
if (type != null && typeof(NetEntity).IsAssignableFrom(type))
{
instance = new ViewVariablesInstanceEntity(this, _entityManager, _robustSerializer, Sawmill);
}

View File

@@ -41,7 +41,8 @@ namespace Robust.Client.ViewVariables.Instances
private const int TabServerComponents = 3;
private TabContainer _tabs = default!;
private EntityUid _entity = default!;
private EntityUid _entity;
private NetEntity _netEntity = default!;
private ViewVariablesAddWindow? _addComponentWindow;
private bool _addComponentServer;
@@ -71,7 +72,8 @@ namespace Robust.Client.ViewVariables.Instances
public override void Initialize(DefaultWindow window, object obj)
{
_entity = (EntityUid) obj;
_netEntity = (NetEntity) obj;
_entity = _entityManager.GetEntity(_netEntity);
var scrollContainer = new ScrollContainer();
//scrollContainer.SetAnchorPreset(Control.LayoutPreset.Wide, true);
@@ -163,7 +165,7 @@ namespace Robust.Client.ViewVariables.Instances
PopulateClientComponents();
if (!_entity.IsClientSide())
if (!_entityManager.IsClientSide(_entity))
{
_serverVariables = new BoxContainer
{
@@ -268,12 +270,12 @@ namespace Robust.Client.ViewVariables.Instances
button.OnPressed += _ =>
{
ViewVariablesManager.OpenVV(
new ViewVariablesComponentSelector(_entity, componentType.FullName));
new ViewVariablesComponentSelector(_entityManager.GetNetEntity(_entity), componentType.FullName));
};
removeButton.OnPressed += _ =>
{
// We send a command to remove the component.
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_entity} {componentType.ComponentName}");
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_netEntity} {componentType.ComponentName}");
PopulateServerComponents();
};
button.AddChild(removeButton);
@@ -417,7 +419,7 @@ namespace Robust.Client.ViewVariables.Instances
if (_addComponentServer)
{
// Attempted to add a component to the server entity... We send a command.
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_entity} {eventArgs.Entry}");
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_netEntity} {eventArgs.Entry}");
PopulateServerComponents();
_addComponentWindow?.Populate(await GetValidServerComponentsForAdding());
return;
@@ -504,7 +506,7 @@ namespace Robust.Client.ViewVariables.Instances
try
{
_entitySession =
await ViewVariablesManager.RequestSession(new ViewVariablesEntitySelector(_entity));
await ViewVariablesManager.RequestSession(new ViewVariablesEntitySelector(_netEntity));
}
catch (SessionDenyException e)
{

View File

@@ -63,20 +63,22 @@ namespace Robust.Client.ViewVariables
}
// Entity.
if (!EntityUid.TryParse(args[0], out var entity))
if (!NetEntity.TryParse(args[0], out var netEntity))
{
shell.WriteLine("Invalid specifier format.");
return;
}
var entity = _entities.GetEntity(netEntity);
if (!_entities.EntityExists(entity))
{
shell.WriteLine("That entity does not exist locally. Attempting to open remote view...");
_cvvm.OpenVV(new ViewVariablesEntitySelector(entity));
_cvvm.OpenVV(new ViewVariablesEntitySelector(netEntity));
return;
}
_cvvm.OpenVV(entity);
_cvvm.OpenVV(netEntity);
}
}
}

View File

@@ -22,13 +22,13 @@ namespace Robust.Server.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var uid))
if (!NetEntity.TryParse(args[0], out var uidNet))
{
shell.WriteLine($"{uid} is not a valid entity uid.");
shell.WriteLine($"{args[0]} is not a valid entity.");
return;
}
if (!_entityManager.EntityExists(uid))
if (!_entityManager.TryGetEntity(uidNet, out var uid) || !_entityManager.EntityExists(uid))
{
shell.WriteLine($"No entity found with id {uid}.");
return;
@@ -44,17 +44,17 @@ namespace Robust.Server.Console.Commands
if (_entityManager.HasComponent(uid, registration.Type))
{
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid).EntityName} already has a {componentName} component.");
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName} already has a {componentName} component.");
}
var component = (Component) _componentFactory.GetComponent(registration.Type);
#pragma warning disable CS0618
component.Owner = uid;
component.Owner = uid.Value;
#pragma warning restore CS0618
_entityManager.AddComponent(uid, component);
_entityManager.AddComponent(uid.Value, component);
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid).EntityName}.");
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName}.");
}
}
}

View File

@@ -19,19 +19,19 @@ namespace Robust.Server.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var entity))
if (!NetEntity.TryParse(args[0], out var entityNet))
{
shell.WriteLine("Invalid entity UID.");
return;
}
if (!_entityManager.EntityExists(entity))
if (!_entityManager.TryGetEntity(entityNet, out var entity) || !_entityManager.EntityExists(entity))
{
shell.WriteLine("That entity does not exist.");
return;
}
_entityManager.DeleteEntity(entity);
_entityManager.DeleteEntity(entity.Value);
}
}
}

View File

@@ -27,12 +27,14 @@ namespace Robust.Server.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var uid))
if (!NetEntity.TryParse(args[0], out var uidNet))
{
shell.WriteError("Not a valid entity ID.");
return;
}
var uid = _ent.GetEntity(uidNet);
// no saving default grid
if (!_ent.EntityExists(uid))
{

View File

@@ -22,12 +22,14 @@ namespace Robust.Server.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var uid))
if (!NetEntity.TryParse(args[0], out var netEntity))
{
shell.WriteLine($"{uid} is not a valid entity uid.");
shell.WriteLine($"{netEntity} is not a valid entity uid.");
return;
}
var uid = _entityManager.GetEntity(netEntity);
if (!_entityManager.EntityExists(uid))
{
shell.WriteLine($"No entity found with id {uid}.");

View File

@@ -24,7 +24,7 @@ public sealed class ScaleCommand : LocalizedCommands
return;
}
if (!EntityUid.TryParse(args[0], out var uid))
if (!NetEntity.TryParse(args[0], out var netEntity))
{
shell.WriteError($"Unable to find entity {args[0]}");
return;
@@ -47,6 +47,7 @@ public sealed class ScaleCommand : LocalizedCommands
var physics = _entityManager.System<SharedPhysicsSystem>();
var appearance = _entityManager.System<AppearanceSystem>();
var uid = _entityManager.GetEntity(netEntity);
_entityManager.EnsureComponent<ScaleVisualsComponent>(uid);
var @event = new ScaleEntityEvent();
_entityManager.EventBus.RaiseLocalEvent(uid, ref @event);

View File

@@ -29,10 +29,12 @@ public sealed class SpinCommand : LocalizedCommands
}
// get the target
EntityUid target;
EntityUid? target;
if (args.Length == 3)
{
if (!EntityUid.TryParse(args[2], out target))
if (!NetEntity.TryParse(args[2], out var targetNet) ||
!_entities.TryGetEntity(targetNet, out target))
{
shell.WriteError($"Unable to find entity {args[1]}");
return;
@@ -65,6 +67,6 @@ public sealed class SpinCommand : LocalizedCommands
var physicsSystem = _entities.System<SharedPhysicsSystem>();
physicsSystem.SetAngularDamping(physics, drag);
physicsSystem.SetAngularVelocity(target, speed, body: physics);
physicsSystem.SetAngularVelocity(target.Value, speed, body: physics);
}
}

View File

@@ -29,19 +29,19 @@ namespace Robust.Server.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var uid))
if (!NetEntity.TryParse(args[0], out var uidNet))
{
shell.WriteError($"Unable to parse {args[0]} as a {nameof(EntityUid)}");
return;
}
if (!_entities.EntityExists(uid))
if (!_entities.TryGetEntity(uidNet, out var uid) || !_entities.EntityExists(uid))
{
shell.WriteError($"Unable to find entity {uid}");
return;
}
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().AddViewSubscriber(uid, playerSession);
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().AddViewSubscriber(uid.Value, playerSession);
}
public sealed class RemoveViewSubscriberCommand : LocalizedCommands
@@ -66,19 +66,19 @@ namespace Robust.Server.Console.Commands
return;
}
if (!EntityUid.TryParse(args[0], out var uid))
if (!NetEntity.TryParse(args[0], out var uidNet))
{
shell.WriteError($"Unable to parse {args[0]} as a {nameof(EntityUid)}");
shell.WriteError($"Unable to parse {args[0]} as a {nameof(NetEntity)}");
return;
}
if (!_entities.EntityExists(uid))
if (!_entities.TryGetEntity(uidNet, out var uid) || !_entities.EntityExists(uid))
{
shell.WriteError($"Unable to find entity {uid}");
return;
}
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().RemoveViewSubscriber(uid, playerSession);
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().RemoveViewSubscriber(uid.Value, playerSession);
}
}
}

View File

@@ -6,7 +6,7 @@ namespace Robust.Server.Containers
{
public sealed class ContainerSystem : SharedContainerSystem
{
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
{
Log.Error($"Missing entity for container {ToPrettyString(uid)}. Missing uid: {missing}");
//cont.InternalRemove(ent);

View File

@@ -86,9 +86,9 @@ public sealed class AudioSystem : SharedAudioSystem
var msg = new PlayAudioEntityMessage
{
FileName = filename,
Coordinates = transform.Coordinates,
FallbackCoordinates = fallbackCoordinates,
EntityUid = uid,
Coordinates = GetNetCoordinates(transform.Coordinates),
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
NetEntity = GetNetEntity(uid),
AudioParams = audioParams ?? AudioParams.Default,
Identifier = id,
};
@@ -108,8 +108,8 @@ public sealed class AudioSystem : SharedAudioSystem
var msg = new PlayAudioPositionalMessage
{
FileName = filename,
Coordinates = coordinates,
FallbackCoordinates = fallbackCoordinates,
Coordinates = GetNetCoordinates(coordinates),
FallbackCoordinates = GetNetCoordinates(fallbackCoordinates),
AudioParams = audioParams ?? AudioParams.Default,
Identifier = id
};

View File

@@ -61,7 +61,7 @@ namespace Robust.Server.GameObjects
//Client Sanitization: unbound command, just ignore
foreach (var handler in BindRegistry.GetHandlers(function))
{
if (handler.HandleCmdMessage(session, msg)) return;
if (handler.HandleCmdMessage(EntityManager, session, msg)) return;
}
}

View File

@@ -83,7 +83,7 @@ namespace Robust.Server.GameObjects
/// </summary>
private void OnMessageReceived(BoundUIWrapMessage msg, EntitySessionEventArgs args)
{
var uid = msg.Entity;
var uid = GetEntity(msg.Entity);
if (!TryComp(uid, out ServerUserInterfaceComponent? uiComp) || args.SenderSession is not IPlayerSession session)
return;
@@ -118,7 +118,7 @@ namespace Robust.Server.GameObjects
// get the wrapped message and populate it with the sender & UI key information.
var message = msg.Message;
message.Session = args.SenderSession;
message.Entity = uid;
message.Entity = GetNetEntity(uid);
message.UiKey = msg.UiKey;
// Raise as object so the correct type is used.
@@ -321,9 +321,9 @@ namespace Robust.Server.GameObjects
/// The player session to send this new state to.
/// Set to null for sending it to every subscribed player session.
/// </param>
public static void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
public void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
{
var msg = new BoundUIWrapMessage(bui.Owner, new UpdateBoundStateMessage(state), bui.UiKey);
var msg = new BoundUIWrapMessage(GetNetEntity(bui.Owner), new UpdateBoundStateMessage(state), bui.UiKey);
if (session == null)
{
bui.LastStateMsg = msg;
@@ -385,7 +385,7 @@ namespace Robust.Server.GameObjects
_openInterfaces.GetOrNew(session).Add(bui);
RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session));
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
// Fun fact, clients needs to have BUIs open before they can receive the state.....
if (bui.LastStateMsg != null)
@@ -414,7 +414,7 @@ namespace Robust.Server.GameObjects
if (!bui._subscribedSessions.Remove(session))
return false;
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
CloseShared(bui, session, activeUis);
return true;
}
@@ -492,7 +492,7 @@ namespace Robust.Server.GameObjects
/// </summary>
public void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage message)
{
var msg = new BoundUIWrapMessage(bui.Owner, message, bui.UiKey);
var msg = new BoundUIWrapMessage(GetNetEntity(bui.Owner), message, bui.UiKey);
foreach (var session in bui.SubscribedSessions)
{
RaiseNetworkEvent(msg, session.ConnectedClient);
@@ -518,7 +518,7 @@ namespace Robust.Server.GameObjects
if (!bui.SubscribedSessions.Contains(session))
return false;
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, message, bui.UiKey), session.ConnectedClient);
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), message, bui.UiKey), session.ConnectedClient);
return true;
}

View File

@@ -8,7 +8,7 @@ namespace Robust.Server.GameObjects
// These methods are used by the map loader to do multi-stage entity construction during map load.
// I would recommend you refer to the MapLoader for usage.
EntityUid AllocEntity(EntityPrototype? prototype, EntityUid uid = default);
EntityUid AllocEntity(EntityPrototype? prototype);
void FinishEntityLoad(EntityUid entity, IEntityLoadContext? context = null);

View File

@@ -42,8 +42,6 @@ namespace Robust.Server.GameObjects
private ISawmill _netEntSawmill = default!;
protected override int NextEntityUid { get; set; } = (int) EntityUid.FirstUid;
public override void Initialize()
{
_netEntSawmill = LogManager.GetSawmill("net.ent");
@@ -54,9 +52,9 @@ namespace Robust.Server.GameObjects
base.Initialize();
}
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype, EntityUid uid)
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
{
return AllocEntity(prototype, out _, uid);
return AllocEntity(prototype, out _);
}
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)
@@ -79,15 +77,15 @@ namespace Robust.Server.GameObjects
StartEntity(entity);
}
private protected override EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
private protected override EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
{
if (prototypeName == null)
return base.CreateEntity(prototypeName, uid, context);
return base.CreateEntity(prototypeName, context);
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
var entity = base.CreateEntity(prototype, uid, context);
var entity = base.CreateEntity(prototype, context);
// At this point in time, all data configure on the entity *should* be purely from the prototype.
// As such, we can reset the modified ticks to Zero,

View File

@@ -24,6 +24,6 @@ namespace Robust.Server.GameStates
Action<ICommonSession, GameTick>? ClientAck { get; set; }
Action<ICommonSession, GameTick, EntityUid?>? ClientRequestFull { get; set; }
Action<ICommonSession, GameTick, NetEntity?>? ClientRequestFull { get; set; }
}
}

View File

@@ -18,7 +18,7 @@ public sealed class PvsOverrideSystem : EntitySystem
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false)
{
_pvs.EntityPVSCollection.AddGlobalOverride(uid, removeExistingOverride, recursive);
_pvs.EntityPVSCollection.AddGlobalOverride(GetNetEntity(uid), removeExistingOverride, recursive);
}
/// <summary>
@@ -28,7 +28,7 @@ public sealed class PvsOverrideSystem : EntitySystem
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
{
_pvs.EntityPVSCollection.UpdateIndex(uid, session, removeExistingOverride);
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), session, removeExistingOverride);
}
/// <summary>
@@ -39,6 +39,6 @@ public sealed class PvsOverrideSystem : EntitySystem
if (!Resolve(uid, ref xform))
return;
_pvs.EntityPVSCollection.UpdateIndex(uid, xform.Coordinates, true);
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), xform.Coordinates, true);
}
}

View File

@@ -45,7 +45,7 @@ internal sealed partial class PvsSystem
return;
var ackedTick = sessionData.LastReceivedAck;
Dictionary<EntityUid, PvsEntityVisibility>? ackedData;
Dictionary<NetEntity, PvsEntityVisibility>? ackedData;
if (sessionData.Overflow != null && sessionData.Overflow.Value.Tick <= ackedTick)
{

View File

@@ -24,14 +24,13 @@ namespace Robust.Server.GameStates;
internal sealed partial class PvsSystem : EntitySystem
{
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
[Shared.IoC.Dependency] private readonly IMapManagerInternal _mapManager = default!;
[Shared.IoC.Dependency] private readonly IPlayerManager _playerManager = default!;
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
[Shared.IoC.Dependency] private readonly SharedTransformSystem _transform = default!;
[Shared.IoC.Dependency] private readonly IServerNetConfigurationManager _netConfigManager = default!;
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
[Shared.IoC.Dependency] private readonly IParallelManager _parallelManager = default!;
[Shared.IoC.Dependency] private readonly IComponentFactory _factory = default!;
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
[Shared.IoC.Dependency] private readonly IServerNetConfigurationManager _netConfigManager = default!;
[Shared.IoC.Dependency] private readonly SharedTransformSystem _transform = default!;
public const float ChunkSize = 8;
@@ -63,31 +62,28 @@ internal sealed partial class PvsSystem : EntitySystem
internal readonly Dictionary<ICommonSession, SessionPVSData> PlayerData = new();
private PVSCollection<EntityUid> _entityPvsCollection = default!;
public PVSCollection<EntityUid> EntityPVSCollection => _entityPvsCollection;
private PVSCollection<NetEntity> _entityPvsCollection = default!;
public PVSCollection<NetEntity> EntityPVSCollection => _entityPvsCollection;
private readonly List<IPVSCollection> _pvsCollections = new();
private readonly ObjectPool<Dictionary<EntityUid, PvsEntityVisibility>> _visSetPool
= new DefaultObjectPool<Dictionary<EntityUid, PvsEntityVisibility>>(
new DictPolicy<EntityUid, PvsEntityVisibility>(), MaxVisPoolSize);
private readonly ObjectPool<Dictionary<NetEntity, PvsEntityVisibility>> _visSetPool
= new DefaultObjectPool<Dictionary<NetEntity, PvsEntityVisibility>>(
new DictPolicy<NetEntity, PvsEntityVisibility>(), MaxVisPoolSize);
private readonly ObjectPool<Stack<EntityUid>> _stackPool
= new DefaultObjectPool<Stack<EntityUid>>(
new StackPolicy<EntityUid>(), MaxVisPoolSize);
private readonly ObjectPool<Stack<NetEntity>> _stackPool
= new DefaultObjectPool<Stack<NetEntity>>(
new StackPolicy<NetEntity>(), MaxVisPoolSize);
private readonly ObjectPool<HashSet<EntityUid>> _uidSetPool
= new DefaultObjectPool<HashSet<EntityUid>>(new SetPolicy<EntityUid>(), MaxVisPoolSize);
private readonly ObjectPool<Dictionary<EntityUid, MetaDataComponent>> _chunkCachePool =
new DefaultObjectPool<Dictionary<EntityUid, MetaDataComponent>>(
new DictPolicy<EntityUid, MetaDataComponent>(), MaxVisPoolSize);
private readonly ObjectPool<Dictionary<NetEntity, MetaDataComponent>> _chunkCachePool =
new DefaultObjectPool<Dictionary<NetEntity, MetaDataComponent>>(
new DictPolicy<NetEntity, MetaDataComponent>(), MaxVisPoolSize);
private readonly ObjectPool<HashSet<int>> _playerChunkPool =
new DefaultObjectPool<HashSet<int>>(new SetPolicy<int>(), MaxVisPoolSize);
private readonly ObjectPool<RobustTree<EntityUid>> _treePool =
new DefaultObjectPool<RobustTree<EntityUid>>(new TreePolicy<EntityUid>(), MaxVisPoolSize);
private readonly ObjectPool<RobustTree<NetEntity>> _treePool =
new DefaultObjectPool<RobustTree<NetEntity>>(new TreePolicy<NetEntity>(), MaxVisPoolSize);
private readonly ObjectPool<Dictionary<MapChunkLocation, int>> _mapChunkPool =
new DefaultObjectPool<Dictionary<MapChunkLocation, int>>(
@@ -102,19 +98,25 @@ internal sealed partial class PvsSystem : EntitySystem
private readonly List<(uint, IChunkIndexLocation)> _chunkList = new(64);
internal readonly HashSet<ICommonSession> PendingAcks = new();
private readonly Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary<NetEntity, MetaDataComponent> metadata,
RobustTree<NetEntity> tree)?> _previousTrees = new();
private readonly HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
private EntityQuery<EyeComponent> _eyeQuery;
private EntityQuery<TransformComponent> _xformQuery;
private EntityQuery<MetaDataComponent> _metaQuery;
private EntityQuery<TransformComponent> _xformQuery;
public override void Initialize()
{
base.Initialize();
_eyeQuery = GetEntityQuery<EyeComponent>();
_metaQuery = GetEntityQuery<MetaDataComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
_metaQuery = GetEntityQuery<MetaDataComponent>();
_entityPvsCollection = RegisterPVSCollection<EntityUid>();
_entityPvsCollection = RegisterPVSCollection<NetEntity>();
SubscribeLocalEvent<MapChangedEvent>(ev =>
{
@@ -159,7 +161,7 @@ internal sealed partial class PvsSystem : EntitySystem
}
// TODO rate limit this?
private void OnClientRequestFull(ICommonSession session, GameTick tick, EntityUid? missingEntity)
private void OnClientRequestFull(ICommonSession session, GameTick tick, NetEntity? missingEntity)
{
if (!PlayerData.TryGetValue(session, out var sessionData))
return;
@@ -173,7 +175,8 @@ internal sealed partial class PvsSystem : EntitySystem
if (missingEntity != null)
{
sb.Append($" Apparently they received an entity without metadata: {ToPrettyString(missingEntity.Value)}.");
var entity = GetEntity(missingEntity)!;
sb.Append($" Apparently they received an entity without metadata: {ToPrettyString(entity.Value)}.");
if (sessionData.LastSeenAt.TryGetValue(missingEntity.Value, out var lastSeenTick))
sb.Append($" Entity last sent: {lastSeenTick.Value}");
@@ -240,17 +243,17 @@ internal sealed partial class PvsSystem : EntitySystem
#region PVSCollection Event Updates
private void OnEntityDeleted(EntityUid e)
private void OnEntityDeleted(EntityUid e, MetaDataComponent metadata)
{
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, e);
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, metadata.NetEntity);
var previousTick = _gameTiming.CurTick - 1;
foreach (var sessionData in PlayerData.Values)
{
sessionData.LastSeenAt.Remove(e);
sessionData.LastSeenAt.Remove(metadata.NetEntity);
if (sessionData.SentEntities.TryGetValue(previousTick, out var ents))
ents.Remove(e);
ents.Remove(metadata.NetEntity);
}
}
@@ -278,7 +281,7 @@ internal sealed partial class PvsSystem : EntitySystem
DebugTools.Assert(!_mapManager.IsMap(ev.Sender));
var coordinates = _transform.GetMoverCoordinates(ev.Sender, ev.Component);
UpdateEntityRecursive(ev.Sender, ev.Component, coordinates, false, ev.ParentChanged);
UpdateEntityRecursive(ev.Sender, _metaQuery.GetComponent(ev.Sender), ev.Component, coordinates, false, ev.ParentChanged);
}
private void OnTransformStartup(EntityUid uid, TransformComponent component, ref TransformStartupEvent args)
@@ -295,10 +298,10 @@ internal sealed partial class PvsSystem : EntitySystem
DebugTools.Assert(!_mapManager.IsMap(uid));
var coordinates = _transform.GetMoverCoordinates(uid, component);
UpdateEntityRecursive(uid, component, coordinates, false, false);
UpdateEntityRecursive(uid, _metaQuery.GetComponent(uid), component, coordinates, false, false);
}
private void UpdateEntityRecursive(EntityUid uid, TransformComponent xform, EntityCoordinates coordinates, bool mover, bool forceDirty)
private void UpdateEntityRecursive(EntityUid uid, MetaDataComponent metadata, TransformComponent xform, EntityCoordinates coordinates, bool mover, bool forceDirty)
{
if (mover && !xform.LocalPosition.Equals(Vector2.Zero))
{
@@ -310,9 +313,9 @@ internal sealed partial class PvsSystem : EntitySystem
var indices = PVSCollection<EntityUid>.GetChunkIndices(coordinates.Position);
if (xform.GridUid != null)
_entityPvsCollection.UpdateIndex(uid, xform.GridUid.Value, indices, forceDirty: forceDirty);
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.GridUid.Value, indices, forceDirty: forceDirty);
else
_entityPvsCollection.UpdateIndex(uid, xform.MapID, indices, forceDirty: forceDirty);
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.MapID, indices, forceDirty: forceDirty);
var children = xform.ChildEnumerator;
@@ -322,7 +325,7 @@ internal sealed partial class PvsSystem : EntitySystem
// directly.
while (children.MoveNext(out var child))
{
UpdateEntityRecursive(child.Value, _xformQuery.GetComponent(child.Value), coordinates, true, forceDirty);
UpdateEntityRecursive(child.Value, _metaQuery.GetComponent(child.Value), _xformQuery.GetComponent(child.Value), coordinates, true, forceDirty);
}
}
@@ -386,7 +389,7 @@ internal sealed partial class PvsSystem : EntitySystem
pvsCollection.AddGrid(gridId);
}
_entityPvsCollection.AddGlobalOverride(gridId, true, false);
_entityPvsCollection.AddGlobalOverride(_metaQuery.GetComponent(gridId).NetEntity, true, false);
}
private void OnMapDestroyed(MapChangedEvent e)
@@ -406,7 +409,7 @@ internal sealed partial class PvsSystem : EntitySystem
if(e.Map == MapId.Nullspace) return;
var uid = _mapManager.GetMapEntityId(e.Map);
_entityPvsCollection.AddGlobalOverride(uid, true, false);
_entityPvsCollection.AddGlobalOverride(_metaQuery.GetComponent(uid).NetEntity, true, false);
}
#endregion
@@ -533,14 +536,9 @@ internal sealed partial class PvsSystem : EntitySystem
return (_chunkList, playerChunks, viewerEntities);
}
private Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary<EntityUid, MetaDataComponent> metadata,
RobustTree<EntityUid> tree)?> _previousTrees = new();
private HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
public void RegisterNewPreviousChunkTrees(
List<(uint, IChunkIndexLocation)> chunks,
(Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] trees,
(Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] trees,
bool[] reuse)
{
// For any chunks able to re-used we'll chuck them in a dictionary for faster lookup.
@@ -584,9 +582,7 @@ internal sealed partial class PvsSystem : EntitySystem
public bool TryCalculateChunk(
IChunkIndexLocation chunkLocation,
uint visMask,
EntityQuery<TransformComponent> transform,
EntityQuery<MetaDataComponent> metadata,
out (Dictionary<EntityUid, MetaDataComponent> mData, RobustTree<EntityUid> tree)? result)
out (Dictionary<NetEntity, MetaDataComponent> mData, RobustTree<NetEntity> tree)? result)
{
if (!_entityPvsCollection.IsDirty(chunkLocation) && _previousTrees.TryGetValue((visMask, chunkLocation), out var previousTree))
{
@@ -613,11 +609,12 @@ internal sealed partial class PvsSystem : EntitySystem
}
var chunkSet = _chunkCachePool.Get();
var tree = _treePool.Get();
foreach (var uid in chunk)
foreach (var netEntity in chunk)
{
AddToChunkSetRecursively(in uid, visMask, tree, chunkSet, transform, metadata);
var uid = GetEntity(netEntity);
AddToChunkSetRecursively(in uid, in netEntity, visMask, tree, chunkSet);
#if DEBUG
var xform = transform.GetComponent(uid);
var xform = _xformQuery.GetComponent(uid);
if (chunkLocation is MapChunkLocation)
DebugTools.Assert(xform.GridUid == null || xform.GridUid == uid);
else if (chunkLocation is GridChunkLocation)
@@ -649,51 +646,55 @@ internal sealed partial class PvsSystem : EntitySystem
}
}
private bool AddToChunkSetRecursively(in EntityUid uid, uint visMask, RobustTree<EntityUid> tree, Dictionary<EntityUid, MetaDataComponent> set, EntityQuery<TransformComponent> transform,
EntityQuery<MetaDataComponent> metadata)
private bool AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, uint visMask, RobustTree<NetEntity> tree, Dictionary<NetEntity, MetaDataComponent> set)
{
if (set.ContainsKey(uid))
if (set.ContainsKey(netEntity))
return true;
var mComp = metadata.GetComponent(uid);
var mComp = _metaQuery.GetComponent(uid);
// TODO: Don't need to know about parents so no longer need to use bool for this method.
// If the eye is missing ANY layer this entity or any of its parents belongs to, it is considered invisible.
if ((visMask & mComp.VisibilityMask) != mComp.VisibilityMask)
return false;
var xform = transform.GetComponent(uid);
var xform = _xformQuery.GetComponent(uid);
// is this a map or grid?
var isRoot = !xform.ParentUid.IsValid() || uid == xform.GridUid;
if (isRoot)
{
DebugTools.Assert(_mapManager.IsGrid(uid) || _mapManager.IsMap(uid));
tree.Set(uid);
set.Add(uid, mComp);
tree.Set(netEntity);
set.Add(netEntity, mComp);
return true;
}
DebugTools.Assert(!_mapManager.IsGrid(uid) && !_mapManager.IsMap(uid));
var parent = xform.ParentUid;
if (!set.ContainsKey(parent) && //was the parent not yet added to toSend?
!AddToChunkSetRecursively(in parent, visMask, tree, set, transform, metadata)) //did we just fail to add the parent?
var parentNetEntity = _metaQuery.GetComponent(parent).NetEntity;
// TODO performance
// AddToChunkSetRecursively will result in a redundant set.ContainsKey() check.
// This can probably be avoided somehow
if (!set.ContainsKey(parentNetEntity) && //was the parent not yet added to toSend?
!AddToChunkSetRecursively(in parent, in parentNetEntity, visMask, tree, set)) //did we just fail to add the parent?
{
return false; //we failed? suppose we dont get added either
}
//i want it to crash here if it gets added double bc that shouldnt happen and will add alot of unneeded cycles
tree.Set(uid, parent);
set.Add(uid, mComp);
tree.Set(netEntity, parentNetEntity);
set.Add(netEntity, mComp);
return true;
}
internal (List<EntityState>? updates, List<EntityUid>? deletions, List<EntityUid>? leftPvs, GameTick fromTick)
internal (List<EntityState>? updates, List<NetEntity>? deletions, List<NetEntity>? leftPvs, GameTick fromTick)
CalculateEntityStates(IPlayerSession session,
GameTick fromTick,
GameTick toTick,
EntityQuery<MetaDataComponent> mQuery,
EntityQuery<TransformComponent> tQuery,
(Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] chunks,
(Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] chunks,
HashSet<int> visibleChunks,
EntityUid[] viewers)
{
@@ -712,7 +713,6 @@ internal sealed partial class PvsSystem : EntitySystem
throw new Exception("Encountered non-empty object inside of _visSetPool. Was the same object returned to the pool more than once?");
var deletions = _entityPvsCollection.GetDeletedIndices(fromTick);
var entStateCount = 0;
var stack = _stackPool.Get();
@@ -726,14 +726,15 @@ internal sealed partial class PvsSystem : EntitySystem
// Each root nodes should simply be a map or a grid entity.
DebugTools.Assert(cache.Value.tree.RootNodes.Count == 1,
$"Root node count is {cache.Value.tree.RootNodes.Count} instead of 1. Session: {session}");
var ent = cache.Value.tree.RootNodes.FirstOrDefault();
var nent = cache.Value.tree.RootNodes.FirstOrDefault();
var ent = GetEntity(nent);
DebugTools.Assert(Exists(ent), $"Root node does not exist. Node {ent}. Session: {session}");
DebugTools.Assert(HasComp<MapComponent>(ent) || HasComp<MapGridComponent>(ent));
#endif
foreach (var rootNode in cache.Value.tree.RootNodes)
{
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, cache.Value.metadata, stack, in fromTick,
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, stack, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
}
}
@@ -742,8 +743,9 @@ internal sealed partial class PvsSystem : EntitySystem
var globalEnumerator = _entityPvsCollection.GlobalOverridesEnumerator;
while (globalEnumerator.MoveNext())
{
var uid = globalEnumerator.Current;
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
var netEntity = globalEnumerator.Current;
var uid = GetEntity(netEntity);
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
}
globalEnumerator.Dispose();
@@ -752,8 +754,9 @@ internal sealed partial class PvsSystem : EntitySystem
var globalRecursiveEnumerator = _entityPvsCollection.GlobalRecursiveOverridesEnumerator;
while (globalRecursiveEnumerator.MoveNext())
{
var uid = globalRecursiveEnumerator.Current;
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
var netEntity = globalRecursiveEnumerator.Current;
var uid = GetEntity(netEntity);
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
}
globalRecursiveEnumerator.Dispose();
@@ -761,15 +764,16 @@ internal sealed partial class PvsSystem : EntitySystem
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
while (localEnumerator.MoveNext())
{
var uid = localEnumerator.Current;
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
var netEntity = localEnumerator.Current;
var uid = GetEntity(netEntity);
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
}
localEnumerator.Dispose();
foreach (var viewerEntity in viewers)
{
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
}
@@ -777,27 +781,32 @@ internal sealed partial class PvsSystem : EntitySystem
RaiseLocalEvent(ref expandEvent);
foreach (var entityUid in expandEvent.Entities)
{
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
}
foreach (var entityUid in expandEvent.RecursiveEntities)
{
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
}
var entityStates = new List<EntityState>(entStateCount);
foreach (var (uid, visiblity) in visibleEnts)
foreach (var (netEntity, visiblity) in visibleEnts)
{
var uid = GetEntity(netEntity);
#if DEBUG
// if an entity is visible, its parents should always be visible.
DebugTools.Assert((tQuery.GetComponent(uid).ParentUid is not { Valid: true } parent) || visibleEnts.ContainsKey(parent),
DebugTools.Assert((_xformQuery.GetComponent(uid).ParentUid is not { Valid: true } parent) ||
visibleEnts.ContainsKey(_metaQuery.GetComponent(parent).NetEntity),
$"Attempted to send an entity without sending it's parents. Entity: {ToPrettyString(uid)}.");
#endif
if (sessionData.RequestedFull)
{
entityStates.Add(GetFullEntityState(session, uid, mQuery.GetComponent(uid)));
entityStates.Add(GetFullEntityState(session, uid, _metaQuery.GetComponent(uid)));
continue;
}
@@ -805,8 +814,8 @@ internal sealed partial class PvsSystem : EntitySystem
continue;
var entered = visiblity == PvsEntityVisibility.Entered;
var entFromTick = entered ? lastSeen.GetValueOrDefault(uid) : fromTick;
var state = GetEntityState(session, uid, entFromTick, mQuery.GetComponent(uid));
var entFromTick = entered ? lastSeen.GetValueOrDefault(netEntity) : fromTick;
var state = GetEntityState(session, uid, entFromTick, _metaQuery.GetComponent(uid));
if (entered || !state.Empty)
entityStates.Add(state);
@@ -850,31 +859,30 @@ internal sealed partial class PvsSystem : EntitySystem
/// Figure out what entities are no longer visible to the client. These entities are sent reliably to the client
/// in a separate net message.
/// </summary>
private List<EntityUid>? ProcessLeavePVS(
Dictionary<EntityUid, PvsEntityVisibility> visibleEnts,
Dictionary<EntityUid, PvsEntityVisibility>? lastSent)
private List<NetEntity>? ProcessLeavePVS(
Dictionary<NetEntity, PvsEntityVisibility> visibleEnts,
Dictionary<NetEntity, PvsEntityVisibility>? lastSent)
{
if (lastSent == null)
return null;
var leftView = new List<EntityUid>();
foreach (var uid in lastSent.Keys)
var leftView = new List<NetEntity>();
foreach (var netEntity in lastSent.Keys)
{
if (!visibleEnts.ContainsKey(uid))
leftView.Add(uid);
if (!visibleEnts.ContainsKey(netEntity))
leftView.Add(netEntity);
}
return leftView.Count > 0 ? leftView : null;
}
private void RecursivelyAddTreeNode(in EntityUid nodeIndex,
RobustTree<EntityUid> tree,
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
Dictionary<EntityUid, PvsEntityVisibility> toSend,
Dictionary<EntityUid, GameTick> lastSeen,
Dictionary<EntityUid, MetaDataComponent> metaDataCache,
Stack<EntityUid> stack,
private void RecursivelyAddTreeNode(in NetEntity nodeIndex,
RobustTree<NetEntity> tree,
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
Dictionary<NetEntity, PvsEntityVisibility> toSend,
Dictionary<NetEntity, GameTick> lastSeen,
Stack<NetEntity> stack,
in GameTick fromTick,
ref int newEntityCount,
ref int enteredEntityCount,
@@ -899,7 +907,8 @@ internal sealed partial class PvsSystem : EntitySystem
if (!shouldAdd)
continue;
AddToSendSet(in currentNodeIndex, metaDataCache[currentNodeIndex], toSend, fromTick, in entered, ref entStateCount);
var entity = GetEntity(currentNodeIndex);
AddToSendSet(in currentNodeIndex, _metaQuery.GetComponent(entity), toSend, fromTick, in entered, ref entStateCount);
}
var node = tree[currentNodeIndex];
@@ -914,12 +923,10 @@ internal sealed partial class PvsSystem : EntitySystem
}
public bool RecursivelyAddOverride(in EntityUid uid,
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
Dictionary<EntityUid, PvsEntityVisibility> toSend,
Dictionary<EntityUid, GameTick> lastSeen,
in EntityQuery<MetaDataComponent> metaQuery,
in EntityQuery<TransformComponent> transQuery,
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
Dictionary<NetEntity, PvsEntityVisibility> toSend,
Dictionary<NetEntity, GameTick> lastSeen,
in GameTick fromTick,
ref int newEntityCount,
ref int enteredEntityCount,
@@ -933,25 +940,31 @@ internal sealed partial class PvsSystem : EntitySystem
if (!uid.IsValid())
return false;
var xform = transQuery.GetComponent(uid);
var xform = _xformQuery.GetComponent(uid);
var parent = xform.ParentUid;
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in metaQuery, in transQuery, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget))
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in fromTick,
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget,
in enteredEntityBudget))
{
return false;
}
var metadata = _metaQuery.GetComponent(uid);
var netEntity = GetNetEntity(uid, metadata);
//did we already get added?
// Note that we check this AFTER adding parents. This is because while this entity may already have been added
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
// send the new parents, which may otherwise be delayed because of the PVS budget..
if (!toSend.ContainsKey(uid))
if (!toSend.ContainsKey(netEntity))
{
// TODO PERFORMANCE.
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
var (entered, _) = ProcessEntry(in netEntity, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
AddToSendSet(in netEntity, _metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
}
if (addChildren)
@@ -964,10 +977,10 @@ internal sealed partial class PvsSystem : EntitySystem
}
private void RecursivelyAddChildren(TransformComponent xform,
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
Dictionary<EntityUid, PvsEntityVisibility> toSend,
Dictionary<EntityUid, GameTick> lastSeen,
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
Dictionary<NetEntity, PvsEntityVisibility> toSend,
Dictionary<NetEntity, GameTick> lastSeen,
in GameTick fromTick,
ref int newEntityCount,
ref int enteredEntityCount,
@@ -980,12 +993,15 @@ internal sealed partial class PvsSystem : EntitySystem
if (!_xformQuery.TryGetComponent(child, out var childXform))
continue;
if (!toSend.ContainsKey(child))
var metadata = _metaQuery.GetComponent(child);
var childNetEntity = GetNetEntity(child, metadata);
if (!toSend.ContainsKey(childNetEntity))
{
var (entered, _) = ProcessEntry(in child, lastAcked, lastSent, lastSeen, ref newEntityCount,
var (entered, _) = ProcessEntry(in childNetEntity, lastAcked, lastSent, lastSeen, ref newEntityCount,
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
AddToSendSet(in child, _metaQuery.GetComponent(child), toSend, fromTick, in entered, ref entStateCount);
AddToSendSet(in childNetEntity, metadata, toSend, fromTick, in entered, ref entStateCount);
}
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
@@ -993,19 +1009,20 @@ internal sealed partial class PvsSystem : EntitySystem
}
}
private (bool Entered, bool ShouldAdd) ProcessEntry(in EntityUid uid,
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
Dictionary<EntityUid, GameTick> lastSeen,
private (bool Entered, bool ShouldAdd) ProcessEntry(
in NetEntity netEntity,
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
Dictionary<NetEntity, GameTick> lastSeen,
ref int newEntityCount,
ref int enteredEntityCount,
in int newEntityBudget,
in int enteredEntityBudget)
{
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(uid);
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(netEntity);
var entered = enteredSinceLastSent || // OR, entered since last ack:
lastAcked == null || !lastAcked.ContainsKey(uid);
lastAcked == null || !lastAcked.ContainsKey(netEntity);
// If the entity is entering, but we already sent this entering entity in the last message, we won't add it to
// the budget. Chances are the packet will arrive in a nice and orderly fashion, and the client will stick to
@@ -1020,25 +1037,25 @@ internal sealed partial class PvsSystem : EntitySystem
return (entered, false);
enteredEntityCount++;
if (!lastSeen.ContainsKey(uid))
if (!lastSeen.ContainsKey(netEntity))
newEntityCount++;
}
return (entered, true);
}
private void AddToSendSet(in EntityUid uid, MetaDataComponent metaDataComponent, Dictionary<EntityUid, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
private void AddToSendSet(in NetEntity netEntity, MetaDataComponent metaDataComponent, Dictionary<NetEntity, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
{
if (metaDataComponent.EntityLifeStage >= EntityLifeStage.Terminating)
{
var rep = new EntityStringRepresentation(uid, metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
var rep = new EntityStringRepresentation(GetEntity(netEntity), metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
Log.Error($"Attempted to add a deleted entity to PVS send set: '{rep}'. Trace:\n{Environment.StackTrace}");
return;
}
if (entered)
{
toSend.Add(uid, PvsEntityVisibility.Entered);
toSend.Add(netEntity, PvsEntityVisibility.Entered);
entStateCount++;
return;
}
@@ -1046,22 +1063,22 @@ internal sealed partial class PvsSystem : EntitySystem
if (metaDataComponent.EntityLastModifiedTick <= fromTick)
{
//entity has been sent before and hasnt been updated since
toSend.Add(uid, PvsEntityVisibility.StayedUnchanged);
toSend.Add(netEntity, PvsEntityVisibility.StayedUnchanged);
return;
}
//add us
toSend.Add(uid, PvsEntityVisibility.StayedChanged);
toSend.Add(netEntity, PvsEntityVisibility.StayedChanged);
entStateCount++;
}
/// <summary>
/// Gets all entity states that have been modified after and including the provided tick.
/// </summary>
public (List<EntityState>?, List<EntityUid>?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
public (List<EntityState>?, List<NetEntity>?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
{
List<EntityState>? stateEntities;
var toSend = _uidSetPool.Get();
var toSend = new HashSet<EntityUid>();
DebugTools.Assert(toSend.Count == 0);
bool enumerateAll = false;
@@ -1096,7 +1113,7 @@ internal sealed partial class PvsSystem : EntitySystem
if (state.Empty)
{
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
Tick: {fromTick}--{_gameTiming.CurTick}
Entity: {ToPrettyString(uid)}
Last modified: {md.EntityLastModifiedTick}
@@ -1163,7 +1180,6 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
}
}
_uidSetPool.Return(toSend);
var deletions = _entityPvsCollection.GetDeletedIndices(fromTick);
if (stateEntities.Count == 0)
@@ -1221,7 +1237,8 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
}
DebugTools.Assert(meta.EntityLastModifiedTick >= meta.LastComponentRemoved);
var entState = new EntityState(entityUid, changed, meta.EntityLastModifiedTick, netComps);
DebugTools.Assert(GetEntity(meta.NetEntity) == entityUid);
var entState = new EntityState(meta.NetEntity, changed, meta.EntityLastModifiedTick, netComps);
return entState;
}
@@ -1253,7 +1270,7 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
netComps.Add(netId);
}
var entState = new EntityState(entityUid, changed, meta.EntityLastModifiedTick, netComps);
var entState = new EntityState(meta.NetEntity, changed, meta.EntityLastModifiedTick, netComps);
return entState;
}
@@ -1263,14 +1280,13 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
if (session.Status != SessionStatus.InGame)
return Array.Empty<EntityUid>();
var viewers = _uidSetPool.Get();
var viewers = new HashSet<EntityUid>();
if (session.AttachedEntity != null)
{
// Fast path
if (session is IPlayerSession { ViewSubscriptionCount: 0 })
{
_uidSetPool.Return(viewers);
return new[] { session.AttachedEntity.Value };
}
@@ -1288,7 +1304,6 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
var viewerArray = viewers.ToArray();
_uidSetPool.Return(viewers);
return viewerArray;
}
@@ -1336,23 +1351,23 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
/// <summary>
/// All <see cref="EntityUid"/>s that this session saw during the last <see cref="DirtyBufferSize"/> ticks.
/// </summary>
public readonly OverflowDictionary<GameTick, Dictionary<EntityUid, PvsEntityVisibility>> SentEntities = new(DirtyBufferSize);
public readonly OverflowDictionary<GameTick, Dictionary<NetEntity, PvsEntityVisibility>> SentEntities = new(DirtyBufferSize);
/// <summary>
/// The most recently acked entities
/// </summary>
public (GameTick Tick, Dictionary<EntityUid, PvsEntityVisibility> Data)? LastAcked;
public (GameTick Tick, Dictionary<NetEntity, PvsEntityVisibility> Data)? LastAcked;
/// <summary>
/// Stores the last tick at which a given entity was acked by a player. Used to avoid re-sending the whole entity
/// state when an item re-enters PVS.
/// </summary>
public readonly Dictionary<EntityUid, GameTick> LastSeenAt = new();
public readonly Dictionary<NetEntity, GameTick> LastSeenAt = new();
/// <summary>
/// <see cref="SentEntities"/> overflow in case a player's last ack is more than <see cref="DirtyBufferSize"/> ticks behind the current tick.
/// </summary>
public (GameTick Tick, Dictionary<EntityUid, PvsEntityVisibility> SentEnts)? Overflow;
public (GameTick Tick, Dictionary<NetEntity, PvsEntityVisibility> SentEnts)? Overflow;
/// <summary>
/// If true, the client has explicitly requested a full state. Unlike the first state, we will send them

View File

@@ -63,7 +63,7 @@ namespace Robust.Server.GameStates
public ushort TransformNetId { get; set; }
public Action<ICommonSession, GameTick>? ClientAck { get; set; }
public Action<ICommonSession, GameTick, EntityUid?>? ClientRequestFull { get; set; }
public Action<ICommonSession, GameTick, NetEntity?>? ClientRequestFull { get; set; }
public void PostInject()
{
@@ -134,7 +134,7 @@ namespace Robust.Server.GameStates
if (!_playerManager.TryGetSessionById(msg.MsgChannel.UserId, out var session))
return;
EntityUid? ent = msg.MissingEntity.IsValid() ? msg.MissingEntity : null;
NetEntity? ent = msg.MissingEntity.IsValid() ? msg.MissingEntity : null;
ClientRequestFull?.Invoke(session, msg.Tick, ent);
}
@@ -245,7 +245,7 @@ namespace Robust.Server.GameStates
{
public HashSet<int>[] PlayerChunks;
public EntityUid[][] ViewerEntities;
public (Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] ChunkCache;
public (Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] ChunkCache;
}
private PvsData? GetPVSData(IPlayerSession[] players)
@@ -255,13 +255,11 @@ namespace Robust.Server.GameStates
var chunksCount = chunks.Count;
var chunkBatches = (int)MathF.Ceiling((float)chunksCount / ChunkBatchSize);
var chunkCache =
new (Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[chunksCount];
new (Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[chunksCount];
// Update the reused trees sequentially to avoid having to lock the dictionary per chunk.
var reuse = ArrayPool<bool>.Shared.Rent(chunksCount);
var transformQuery = _entityManager.GetEntityQuery<TransformComponent>();
var metadataQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
Parallel.For(0, chunkBatches,
new ParallelOptions { MaxDegreeOfParallelism = _parallelMgr.ParallelProcessCount },
i =>
@@ -272,8 +270,7 @@ namespace Robust.Server.GameStates
for (var j = start; j < end; ++j)
{
var (visMask, chunkIndexLocation) = chunks[j];
reuse[j] = _pvs.TryCalculateChunk(chunkIndexLocation, visMask, transformQuery, metadataQuery,
out var chunk);
reuse[j] = _pvs.TryCalculateChunk(chunkIndexLocation, visMask, out var chunk);
chunkCache[j] = chunk;
#if DEBUG
@@ -283,7 +280,8 @@ namespace Robust.Server.GameStates
// Each root nodes should simply be a map or a grid entity.
DebugTools.Assert(chunk.Value.tree.RootNodes.Count == 1,
$"Root node count is {chunk.Value.tree.RootNodes.Count} instead of 1.");
var ent = chunk.Value.tree.RootNodes.FirstOrDefault();
var nent = chunk.Value.tree.RootNodes.FirstOrDefault();
var ent = _entityManager.GetEntity(nent);
DebugTools.Assert(_entityManager.EntityExists(ent), $"Root node does not exist. Node {ent}.");
DebugTools.Assert(_entityManager.HasComponent<MapComponent>(ent)
|| _entityManager.HasComponent<MapGridComponent>(ent));
@@ -313,9 +311,9 @@ namespace Robust.Server.GameStates
var channel = session.ConnectedClient;
var sessionData = _pvs.PlayerData[session];
var lastAck = sessionData.LastReceivedAck;
List<EntityUid>? leftPvs = null;
List<NetEntity>? leftPvs = null;
List<EntityState>? entStates;
List<EntityUid>? deletions;
List<NetEntity>? deletions;
GameTick fromTick;
DebugTools.Assert(_pvs.CullingEnabled == (pvsData != null));
@@ -325,8 +323,6 @@ namespace Robust.Server.GameStates
session,
lastAck,
_gameTiming.CurTick,
mQuery,
tQuery,
pvsData.Value.ChunkCache,
pvsData.Value.PlayerChunks[i],
pvsData.Value.ViewerEntities[i]);

View File

@@ -130,7 +130,7 @@ namespace Robust.Server.Physics
var msg = new ChunkSplitDebugMessage
{
Grid = uid,
Grid = GetNetEntity(uid),
};
foreach (var (index, group) in _nodes[uid])

View File

@@ -21,9 +21,9 @@ public sealed class JointSystem : SharedJointSystem
foreach (var (id, joint) in component.Joints)
{
states.Add(id, joint.GetState());
states.Add(id, joint.GetState(EntityManager));
}
args.State = new JointComponentState(component.Relay, states);
args.State = new JointComponentState(GetNetEntity(component.Relay), states);
}
}

View File

@@ -98,8 +98,10 @@ namespace Robust.Server.Placement
return;
//TODO: Distance check, so you can't place things off of screen.
// I don't think that's this manager's biggest problem
var coordinates = msg.EntityCoordinates;
var netCoordinates = msg.NetCoordinates;
var coordinates = _entityManager.GetCoordinates(netCoordinates);
if (!coordinates.IsValid(_entityManager))
{
@@ -211,17 +213,19 @@ namespace Robust.Server.Placement
private void HandleEntRemoveReq(MsgPlacement msg)
{
//TODO: Some form of admin check
if (!_entityManager.EntityExists(msg.EntityUid))
var entity = _entityManager.GetEntity(msg.EntityUid);
if (!_entityManager.EntityExists(entity))
return;
var placementEraseEvent = new PlacementEntityEvent(msg.EntityUid, _entityManager.GetComponent<TransformComponent>(msg.EntityUid).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
var placementEraseEvent = new PlacementEntityEvent(entity, _entityManager.GetComponent<TransformComponent>(entity).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
_entityManager.DeleteEntity(msg.EntityUid);
_entityManager.DeleteEntity(entity);
}
private void HandleRectRemoveReq(MsgPlacement msg)
{
EntityCoordinates start = msg.EntityCoordinates;
EntityCoordinates start = _entityManager.GetCoordinates(msg.NetCoordinates);
Vector2 rectSize = msg.RectSize;
foreach (EntityUid entity in EntitySystem.Get<EntityLookupSystem>().GetEntitiesIntersecting(start.GetMapId(_entityManager),
new Box2(start.Position, start.Position + rectSize)))

View File

@@ -210,7 +210,7 @@ namespace Robust.Server.Player
{
PlayerState.Status = Status;
PlayerState.Name = Name;
PlayerState.ControlledEntity = AttachedEntity;
PlayerState.ControlledEntity = IoCManager.Resolve<IEntityManager>().GetNetEntity(AttachedEntity);
_playerManager.Dirty();
}

View File

@@ -125,8 +125,10 @@ namespace Robust.Server.ViewVariables
case ViewVariablesComponentSelector componentSelector:
{
var compType = _reflectionManager.GetType(componentSelector.ComponentType);
var entity = _entityManager.GetEntity(componentSelector.Entity);
if (compType == null ||
!_entityManager.TryGetComponent(componentSelector.Entity, compType, out var component))
!_entityManager.TryGetComponent(entity, compType, out var component))
{
Deny(ViewVariablesResponseCode.NoObject);
return;
@@ -137,7 +139,9 @@ namespace Robust.Server.ViewVariables
}
case ViewVariablesEntitySelector entitySelector:
{
if (!_entityManager.EntityExists(entitySelector.Entity))
var entity = _entityManager.GetEntity(entitySelector.Entity);
if (!_entityManager.EntityExists(entity))
{
Deny(ViewVariablesResponseCode.NoObject);
return;

View File

@@ -14,7 +14,8 @@ namespace Robust.Server.ViewVariables.Traits
public ViewVariablesTraitEntity(IViewVariablesSession session) : base(session)
{
_entity = (EntityUid) Session.Object;
var netEntity = (NetEntity) Session.Object;
_entity = IoCManager.Resolve<IEntityManager>().GetEntity(netEntity);
}
public override ViewVariablesBlob? DataRequest(ViewVariablesRequest viewVariablesRequest)

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
@@ -27,10 +28,11 @@ namespace Robust.Server.ViewVariables.Traits
if (messageRequestMeta is ViewVariablesRequestMembers)
{
var members = new List<(MemberData mData, MemberInfo mInfo)>();
var obj = Session.Object;
var objType = Session.ObjectType;
foreach (var property in Session.ObjectType.GetAllProperties())
foreach (var property in objType.GetAllProperties())
{
if (!ViewVariablesUtility.TryGetViewVariablesAccess(property, out var access))
{
continue;
@@ -53,7 +55,7 @@ namespace Robust.Server.ViewVariables.Traits
_members.Add(property);
}
foreach (var field in Session.ObjectType.GetAllFields())
foreach (var field in objType.GetAllFields())
{
if (!ViewVariablesUtility.TryGetViewVariablesAccess(field, out var access))
{
@@ -66,7 +68,7 @@ namespace Robust.Server.ViewVariables.Traits
Name = field.Name,
Type = field.FieldType.AssemblyQualifiedName,
TypePretty = PrettyPrint.PrintUserFacingTypeShort(field.FieldType, 2),
Value = field.GetValue(Session.Object),
Value = field.GetValue(obj),
PropertyIndex = _members.Count
}, field));

View File

@@ -14,9 +14,14 @@ namespace Robust.Shared.CompNetworkGenerator
{
private const string ClassAttributeName = "Robust.Shared.Analyzers.AutoGenerateComponentStateAttribute";
private const string MemberAttributeName = "Robust.Shared.Analyzers.AutoNetworkedFieldAttribute";
private const string GlobalEntityUidName = "global::Robust.Shared.GameObjects.EntityUid";
private const string GlobalNullableEntityUidName = "global::Robust.Shared.GameObjects.EntityUid?";
private const string GlobalEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates?";
private const string GlobalNullableEntityCoordinatesName = "global::Robust.Shared.Map.EntityCoordinates";
private static string GenerateSource(in GeneratorExecutionContext context, INamedTypeSymbol classSymbol, CSharpCompilation comp, bool raiseAfterAutoHandle)
{
// Debugger.Launch();
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
var componentName = classSymbol.Name;
var stateName = $"{componentName}_AutoState";
@@ -115,27 +120,59 @@ namespace Robust.Shared.CompNetworkGenerator
foreach (var (type, name, attribute) in fields)
{
stateFields.Append($@"
public {type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)} {name} = default!;");
var typeDisplayStr = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var nullable = type.NullableAnnotation == NullableAnnotation.Annotated;
var nullableAnnotation = nullable ? "?" : string.Empty;
// get first ctor arg of the field attribute, which determines whether the field should be cloned
// (like if its a dict or list)
if (attribute.ConstructorArguments[0].Value is bool val && val)
switch (typeDisplayStr)
{
getStateInit.Append($@"
case GlobalEntityUidName:
case GlobalNullableEntityUidName:
stateFields.Append($@"
public NetEntity{nullableAnnotation} {name} = default!;");
getStateInit.Append($@"
{name} = GetNetEntity(component.{name}),");
handleStateSetters.Append($@"
component.{name} = EnsureEntity<{componentName}>(state.{name}, uid);");
break;
case GlobalEntityCoordinatesName:
case GlobalNullableEntityCoordinatesName:
stateFields.Append($@"
public NetCoordinates{nullableAnnotation} {name} = default!;");
getStateInit.Append($@"
{name} = GetNetCoordinates(component.{name}),");
handleStateSetters.Append($@"
component.{name} = EnsureCoordinates<{componentName}>(state.{name}, uid);");
break;
default:
stateFields.Append($@"
public {typeDisplayStr} {name} = default!;");
if (attribute.ConstructorArguments[0].Value is bool val && val)
{
// get first ctor arg of the field attribute, which determines whether the field should be cloned
// (like if its a dict or list)
getStateInit.Append($@"
{name} = component.{name},");
handleStateSetters.Append($@"
handleStateSetters.Append($@"
if (state.{name} != null)
component.{name} = new(state.{name});");
}
else
{
getStateInit.Append($@"
}
else
{
getStateInit.Append($@"
{name} = component.{name},");
handleStateSetters.Append($@"
handleStateSetters.Append($@"
component.{name} = state.{name};");
}
break;
}
}
@@ -153,6 +190,7 @@ using Robust.Shared.GameStates;
using Robust.Shared.GameObjects;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization;
using Robust.Shared.Map;
namespace {nameSpace};

View File

@@ -19,7 +19,9 @@ internal sealed class DumpEventTablesCommand : LocalizedCommands
return;
}
if (!EntityUid.TryParse(args[0], out var entity) || !_entities.EntityExists(entity))
if (!NetEntity.TryParse(args[0], out var entityNet) ||
!_entities.TryGetEntity(entityNet, out var entity) ||
!_entities.EntityExists(entity))
{
shell.WriteError(Loc.GetString("cmd-dump_event_tables-error-entity"));
return;
@@ -27,7 +29,7 @@ internal sealed class DumpEventTablesCommand : LocalizedCommands
var eventBus = (EntityEventBus)_entities.EventBus;
var table = eventBus._entEventTables[entity];
var table = eventBus._entEventTables[entity.Value];
foreach (var (evType, comps) in table.EventIndices)
{
shell.WriteLine($"{evType}:");

View File

@@ -68,6 +68,7 @@ sealed class RemoveMapCommand : LocalizedCommands
sealed class RemoveGridCommand : LocalizedCommands
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _map = default!;
public override string Command => "rmgrid";
@@ -81,15 +82,15 @@ sealed class RemoveGridCommand : LocalizedCommands
return;
}
var gridId = EntityUid.Parse(args[0]);
var gridIdNet = NetEntity.Parse(args[0]);
if (!_map.GridExists(gridId))
if (!_entManager.TryGetEntity(gridIdNet, out var gridId) || !_map.GridExists(gridId))
{
shell.WriteError($"Grid {gridId} does not exist.");
return;
}
_map.DeleteGrid(gridId);
_map.DeleteGrid(gridId.Value);
shell.WriteLine($"Grid {gridId} was removed.");
}
}

View File

@@ -32,8 +32,8 @@ public sealed class SpawnCommand : LocalizedCommands
}
else if (args.Length == 2)
{
var uid = EntityUid.Parse(args[1]);
var entityCoordinates = _entityManager.GetComponent<TransformComponent>(uid).Coordinates;
var uidNet = NetEntity.Parse(args[1]);
var entityCoordinates = _entityManager.GetComponent<TransformComponent>(_entityManager.GetEntity(uidNet)).Coordinates;
var createdEntity = _entityManager.SpawnEntity(args[0], entityCoordinates);
placementEv = new PlacementEntityEvent(createdEntity, entityCoordinates, PlacementEventAction.Create, shell.Player?.UserId);
}

View File

@@ -125,7 +125,7 @@ public sealed class TeleportToCommand : LocalizedCommands
[NotNullWhen(true)] out EntityUid? victimUid,
[NotNullWhen(true)] out TransformComponent? transform)
{
if (EntityUid.TryParse(str, out var uid) && _entities.TryGetComponent(uid, out transform))
if (NetEntity.TryParse(str, out var uidNet) && _entities.TryGetEntity(uidNet, out var uid) && _entities.TryGetComponent(uid, out transform))
{
victimUid = uid;
return true;
@@ -160,10 +160,10 @@ public sealed class TeleportToCommand : LocalizedCommands
hint = Loc.GetString(hint);
var opts = CompletionResult.FromHintOptions(users, hint);
if (last != string.Empty && !EntityUid.TryParse(last, out _))
if (last != string.Empty && !NetEntity.TryParse(last, out _))
return opts;
return CompletionResult.FromHintOptions(opts.Options.Concat(CompletionHelper.EntityUids(last, _entities)), hint);
return CompletionResult.FromHintOptions(opts.Options.Concat(CompletionHelper.NetEntities(last, _entities)), hint);
}
}
@@ -201,11 +201,11 @@ sealed class TpGridCommand : LocalizedCommands
return;
}
var gridId = EntityUid.Parse(args[0]);
var gridIdNet = NetEntity.Parse(args[0]);
var xPos = float.Parse(args[1], CultureInfo.InvariantCulture);
var yPos = float.Parse(args[2], CultureInfo.InvariantCulture);
if (!_ent.EntityExists(gridId))
if (!_ent.TryGetEntity(gridIdNet, out var gridId) || !_ent.EntityExists(gridId))
{
shell.WriteError($"Entity does not exist: {args[0]}");
return;
@@ -217,7 +217,7 @@ sealed class TpGridCommand : LocalizedCommands
return;
}
var gridXform = _ent.GetComponent<TransformComponent>(gridId);
var gridXform = _ent.GetComponent<TransformComponent>(gridId.Value);
var mapId = args.Length == 4 ? new MapId(int.Parse(args[3])) : gridXform.MapID;
gridXform.Coordinates = new EntityCoordinates(_map.GetMapEntityId(mapId), new Vector2(xPos, yPos));

View File

@@ -155,18 +155,21 @@ public static class CompletionHelper
}
}
public static IEnumerable<CompletionOption> EntityUids(string text, IEntityManager? entManager = null)
public static IEnumerable<CompletionOption> NetEntities(string text, IEntityManager? entManager = null)
{
IoCManager.Resolve(ref entManager);
foreach (var ent in entManager.GetEntities())
{
var entString = ent.ToString();
if (!entString.StartsWith(text))
if (!entManager.TryGetNetEntity(ent, out var netEntity))
continue;
yield return new CompletionOption(entString);
var netString = netEntity.Value.ToString();
if (!netString.StartsWith(text))
continue;
yield return new CompletionOption(netString);
}
}
@@ -174,14 +177,19 @@ public static class CompletionHelper
{
IoCManager.Resolve(ref entManager);
var query = entManager.AllEntityQueryEnumerator<T>();
var query = entManager.AllEntityQueryEnumerator<T, MetaDataComponent>();
while (query.MoveNext(out var uid, out _))
while (query.MoveNext(out var uid, out _, out var metadata))
{
if (!uid.ToString().StartsWith(text))
if (!entManager.TryGetNetEntity(uid, out var netEntity, metadata: metadata))
continue;
yield return new CompletionOption(uid.ToString());
var netString = netEntity.Value.ToString();
if (!netString.StartsWith(text))
continue;
yield return new CompletionOption(netString);
}
}
}

View File

@@ -12,57 +12,85 @@ using Robust.Shared.ViewVariables;
using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Shared.Map.Components;
using Robust.Shared.Serialization;
namespace Robust.Shared.Containers
{
/// <summary>
/// Base container class that all container inherit from.
/// </summary>
public abstract partial class BaseContainer : IContainer
[ImplicitDataDefinitionForInheritors]
public abstract partial class BaseContainer
{
/// <inheritdoc />
/// <summary>
/// Readonly collection of all the entities contained within this specific container
/// </summary>
[ViewVariables]
public abstract IReadOnlyList<EntityUid> ContainedEntities { get; }
[ViewVariables]
public abstract List<EntityUid> ExpectedEntities { get; }
/// <summary>
/// Number of contained entities.
/// </summary>
public abstract int Count { get; }
/// <inheritdoc />
public abstract string ContainerType { get; }
[ViewVariables, NonSerialized]
public List<NetEntity> ExpectedEntities = new();
/// <inheritdoc />
[ViewVariables]
public bool Deleted { get; private set; }
/// <summary>
/// The ID of this container.
/// </summary>
[ViewVariables, NonSerialized, Access(typeof(SharedContainerSystem), typeof(ContainerManagerComponent))]
public string ID = default!;
/// <inheritdoc />
[ViewVariables]
public string ID { get; internal set; } = default!; // Make sure you set me in init
[NonSerialized]
internal ContainerManagerComponent Manager = default!;
/// <inheritdoc />
public ContainerManagerComponent Manager { get; internal set; } = default!; // Make sure you set me in init
/// <inheritdoc />
/// <summary>
/// Prevents light from escaping the container, from ex. a flashlight.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("occludes")]
public bool OccludesLight { get; set; } = true;
/// <inheritdoc />
/// <summary>
/// The entity that owns this container.
/// </summary>
[ViewVariables]
public EntityUid Owner => Manager.Owner;
/// <inheritdoc />
/// <summary>
/// Should the contents of this container be shown? False for closed containers like lockers, true for
/// things like glass display cases.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("showEnts")]
public bool ShowContents { get; set; }
/// <summary>
/// DO NOT CALL THIS METHOD DIRECTLY!
/// You want <see cref="IContainerManager.MakeContainer{T}(string)" /> instead.
/// </summary>
protected BaseContainer() { }
internal void Init(string id, EntityUid owner, ContainerManagerComponent component)
{
DebugTools.AssertNull(ID);
ID = id;
Manager = component;
/// <inheritdoc />
// TODO fix container init.
// Eventually, we want an owner field, but currently it needs to use component.Owner
// Owner = owner;
}
/// <summary>
/// Attempts to insert the entity into this container.
/// </summary>
/// <remarks>
/// If the insertion is successful, the inserted entity will end up parented to the
/// container entity, and the inserted entity's local position will be set to the zero vector.
/// </remarks>
/// <param name="toinsert">The entity to insert.</param>
/// <param name="entMan"></param>
/// <returns>False if the entity could not be inserted.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if this container is a child of the entity,
/// which would cause infinite loops.
/// </exception>
public bool Insert(
EntityUid toinsert,
IEntityManager? entMan = null,
@@ -72,17 +100,12 @@ namespace Robust.Shared.Containers
PhysicsComponent? physics = null,
bool force = false)
{
DebugTools.Assert(!Deleted);
IoCManager.Resolve(ref entMan);
DebugTools.Assert(transform == null || transform.Owner == toinsert);
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
DebugTools.Assert(physics == null || physics.Owner == toinsert);
DebugTools.Assert(!ExpectedEntities.Contains(toinsert));
IoCManager.Resolve(ref entMan);
//Verify we can insert into this container
if (!force && !CanInsert(toinsert, entMan))
return false;
DebugTools.Assert(!ExpectedEntities.Contains(entMan.GetNetEntity(toinsert)));
var physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
var transformQuery = entMan.GetEntityQuery<TransformComponent>();
@@ -91,6 +114,11 @@ namespace Robust.Shared.Containers
// ECS containers when
var physicsSys = entMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
var jointSys = entMan.EntitySysManager.GetEntitySystem<SharedJointSystem>();
var containerSys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
//Verify we can insert into this container
if (!force && !containerSys.CanInsert(toinsert, this))
return false;
// Please somebody ecs containers
var lookupSys = entMan.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
@@ -214,45 +242,22 @@ namespace Robust.Shared.Containers
}
}
/// <inheritdoc />
public virtual bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
{
DebugTools.Assert(!Deleted);
/// <summary>
/// Whether the given entity can be inserted into this container.
/// </summary>
/// <param name="assumeEmpty">Whether to assume that the container is currently empty.</param>
protected internal virtual bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan) => true;
// cannot insert into itself.
if (Owner == toinsert)
return false;
IoCManager.Resolve(ref entMan);
// no, you can't put maps or grids into containers
if (entMan.HasComponent<MapComponent>(toinsert) || entMan.HasComponent<MapGridComponent>(toinsert))
return false;
var xformSystem = entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
var xformQuery = entMan.GetEntityQuery<TransformComponent>();
// Crucial, prevent circular insertion.
if (xformSystem.ContainsEntity(xformQuery.GetComponent(toinsert), Owner, xformQuery))
return false;
//Improvement: Traverse the entire tree to make sure we are not creating a loop.
//raise events
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(this, toinsert);
entMan.EventBus.RaiseLocalEvent(Owner, insertAttemptEvent, true);
if (insertAttemptEvent.Cancelled)
return false;
var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(this, toinsert);
entMan.EventBus.RaiseLocalEvent(toinsert, gettingInsertedAttemptEvent, true);
if (gettingInsertedAttemptEvent.Cancelled)
return false;
return true;
}
/// <inheritdoc />
/// <summary>
/// Attempts to remove the entity from this container.
/// </summary>
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
/// destination is not null</param>
/// <param name="force">If true, this will not perform can-remove checks.</param>
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
/// container, the grid, or the map.</param>
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
public bool Remove(
EntityUid toRemove,
IEntityManager? entMan = null,
@@ -264,7 +269,6 @@ namespace Robust.Shared.Containers
Angle? localRotation = null)
{
IoCManager.Resolve(ref entMan);
DebugTools.Assert(!Deleted);
DebugTools.AssertNotNull(Manager);
DebugTools.Assert(entMan.EntityExists(toRemove));
DebugTools.Assert(xform == null || xform.Owner == toRemove);
@@ -273,7 +277,8 @@ namespace Robust.Shared.Containers
xform ??= entMan.GetComponent<TransformComponent>(toRemove);
meta ??= entMan.GetComponent<MetaDataComponent>(toRemove);
if (!force && !CanRemove(toRemove, entMan))
var sys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
if (!force && !sys.CanRemove(toRemove, this))
return false;
if (force && !Contains(toRemove))
@@ -305,7 +310,7 @@ namespace Robust.Shared.Containers
else if (reparent)
{
// Container ECS when.
entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>().AttachParentToContainerOrGrid(xform);
sys.AttachParentToContainerOrGrid(xform);
if (localRotation != null)
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetLocalRotation(xform, localRotation.Value);
}
@@ -336,40 +341,22 @@ namespace Robust.Shared.Containers
public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null)
=> Remove(toRemove, entMan, meta: meta, reparent: false, force: true);
/// <inheritdoc />
public virtual bool CanRemove(EntityUid toRemove, IEntityManager? entMan = null)
{
DebugTools.Assert(!Deleted);
if (!Contains(toRemove))
return false;
IoCManager.Resolve(ref entMan);
//raise events
var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(this, toRemove);
entMan.EventBus.RaiseLocalEvent(Owner, removeAttemptEvent, true);
if (removeAttemptEvent.Cancelled)
return false;
var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(this, toRemove);
entMan.EventBus.RaiseLocalEvent(toRemove, gettingRemovedAttemptEvent, true);
if (gettingRemovedAttemptEvent.Cancelled)
return false;
return true;
}
/// <inheritdoc />
/// <summary>
/// Checks if the entity is contained in this container.
/// This is not recursive, so containers of children are not checked.
/// </summary>
/// <param name="contained">The entity to check.</param>
/// <returns>True if the entity is immediately contained in this container, false otherwise.</returns>
public abstract bool Contains(EntityUid contained);
/// <inheritdoc />
/// <summary>
/// Clears the container and marks it as deleted.
/// </summary>
public void Shutdown(IEntityManager? entMan = null, INetManager? netMan = null)
{
IoCManager.Resolve(ref entMan, ref netMan);
InternalShutdown(entMan, netMan.IsClient);
Manager.Containers.Remove(ID);
Deleted = true;
}
/// <inheritdoc />

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Shared.Containers
@@ -15,27 +17,21 @@ namespace Robust.Shared.Containers
/// For example, inventory containers should be modified only through an inventory component.
/// </summary>
[UsedImplicitly]
[SerializedType(ClassName)]
[SerializedType(nameof(Container))]
public sealed partial class Container : BaseContainer
{
private const string ClassName = "Container";
/// <summary>
/// The generic container class uses a list of entities
/// </summary>
[DataField("ents")]
[NonSerialized]
private List<EntityUid> _containerList = new();
private readonly List<EntityUid> _expectedEntities = new();
public override int Count => _containerList.Count;
/// <inheritdoc />
public override IReadOnlyList<EntityUid> ContainedEntities => _containerList;
public override List<EntityUid> ExpectedEntities => _expectedEntities;
/// <inheritdoc />
public override string ContainerType => ClassName;
/// <inheritdoc />
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
{
@@ -56,6 +52,9 @@ namespace Robust.Shared.Containers
return false;
#if DEBUG
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
return true;
var entMan = IoCManager.Resolve<IEntityManager>();
var flags = entMan.GetComponent<MetaDataComponent>(contained).Flags;
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, $"Entity has bad container flags. Ent: {entMan.ToPrettyString(contained)}. Container: {ID}, Owner: {entMan.ToPrettyString(Owner)}");

View File

@@ -54,7 +54,7 @@ namespace Robust.Shared.Containers
/// <param name="container">The container that this entity is inside of.</param>
/// <returns>If a container was found.</returns>
[Obsolete("Use ContainerSystem.TryGetContainingContainer() instead")]
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out IContainer? container, IEntityManager? entMan = null)
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out BaseContainer? container, IEntityManager? entMan = null)
{
IoCManager.Resolve(ref entMan);
DebugTools.Assert(entMan.EntityExists(entity));
@@ -100,7 +100,7 @@ namespace Robust.Shared.Containers
/// <see cref="SharedContainerSystem.EmptyContainer"/>
/// </summary>
[Obsolete("Use SharedContainerSystem.EmptyContainer() instead")]
public static void EmptyContainer(this IContainer container, bool force = false, EntityCoordinates? moveTo = null,
public static void EmptyContainer(this BaseContainer container, bool force = false, EntityCoordinates? moveTo = null,
bool attachToGridOrMap = false, IEntityManager? entMan = null)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
@@ -112,7 +112,7 @@ namespace Robust.Shared.Containers
/// <see cref="SharedContainerSystem.CleanContainer"/>
/// </summary>
[Obsolete("Use SharedContainerSystem.CleanContainer() instead")]
public static void CleanContainer(this IContainer container, IEntityManager? entMan = null)
public static void CleanContainer(this BaseContainer container, IEntityManager? entMan = null)
{
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
.CleanContainer(container);
@@ -147,10 +147,10 @@ namespace Robust.Shared.Containers
/// <param name="containerId"></param>
/// <returns>The new container.</returns>
/// <exception cref="ArgumentException">Thrown if there already is a container with the specified ID.</exception>
/// <seealso cref="IContainerManager.MakeContainer{T}(string)" />
/// <seealso cref="BaseContainerManager.MakeContainer{T}(string)" />
[Obsolete("Use ContainerSystem.MakeContainer() instead")]
public static T CreateContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
where T : IContainer
where T : BaseContainer
{
IoCManager.Resolve(ref entMan);
var containermanager = entMan.EnsureComponent<ContainerManagerComponent>(entity);
@@ -159,7 +159,7 @@ namespace Robust.Shared.Containers
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
public static T EnsureContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
where T : IContainer
where T : BaseContainer
{
IoCManager.Resolve(ref entMan);
return EnsureContainer<T>(entity, containerId, out _, entMan);
@@ -167,7 +167,7 @@ namespace Robust.Shared.Containers
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
public static T EnsureContainer<T>(this EntityUid entity, string containerId, out bool alreadyExisted, IEntityManager? entMan = null)
where T : IContainer
where T : BaseContainer
{
IoCManager.Resolve(ref entMan);
var containerManager = entMan.EnsureComponent<ContainerManagerComponent>(entity);

View File

@@ -25,18 +25,16 @@ namespace Robust.Shared.Containers
[Dependency] private readonly INetManager _netMan = default!;
[DataField("containers")]
public Dictionary<string, IContainer> Containers = new();
public Dictionary<string, BaseContainer> Containers = new();
void ISerializationHooks.AfterDeserialization()
{
// TODO remove ISerializationHooks I guess the IDs can be set by a custom serializer for the dictionary? But
// the component??? Maybe other systems need to stop assuming that containers have been initialized during
// their own init.
// TODO custom type serializer
// TODO set owner uid on init.
foreach (var (id, container) in Containers)
{
var baseContainer = (BaseContainer) container;
baseContainer.Manager = this;
baseContainer.ID = id;
container.Manager = this;
container.ID = id;
}
}
@@ -55,13 +53,20 @@ namespace Robust.Shared.Containers
/// <inheritdoc />
public T MakeContainer<T>(string id)
where T : IContainer
where T : BaseContainer
{
return (T) MakeContainer(id, typeof(T));
if (HasContainer(id))
throw new ArgumentException($"Container with specified ID already exists: '{id}'");
var container = _dynFactory.CreateInstanceUnchecked<T>(typeof(T), inject: false);
container.Init(id, Owner, this);
Containers[id] = container;
_entMan.Dirty(this);
return container;
}
/// <inheritdoc />
public IContainer GetContainer(string id)
public BaseContainer GetContainer(string id)
{
return Containers[id];
}
@@ -73,7 +78,7 @@ namespace Robust.Shared.Containers
}
/// <inheritdoc />
public bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container)
public bool TryGetContainer(string id, [NotNullWhen(true)] out BaseContainer? container)
{
var ret = Containers.TryGetValue(id, out var cont);
container = cont!;
@@ -81,11 +86,11 @@ namespace Robust.Shared.Containers
}
/// <inheritdoc />
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out IContainer? container)
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out BaseContainer? container)
{
foreach (var contain in Containers.Values)
{
if (!contain.Deleted && contain.Contains(entity))
if (contain.Contains(entity))
{
container = contain;
return true;
@@ -101,7 +106,7 @@ namespace Robust.Shared.Containers
{
foreach (var container in Containers.Values)
{
if (!container.Deleted && container.Contains(entity)) return true;
if (container.Contains(entity)) return true;
}
return false;
@@ -125,19 +130,6 @@ namespace Robust.Shared.Containers
return true; // If we don't contain the entity, it will always be removed
}
private IContainer MakeContainer(string id, Type type)
{
if (HasContainer(id)) throw new ArgumentException($"Container with specified ID already exists: '{id}'");
var container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
container.ID = id;
container.Manager = this;
Containers[id] = container;
_entMan.Dirty(this);
return container;
}
public AllContainersEnumerable GetAllContainers()
{
return new(this);
@@ -156,25 +148,22 @@ namespace Robust.Shared.Containers
[Serializable, NetSerializable]
public readonly struct ContainerData
{
public readonly string ContainerType;
public readonly string Id;
public readonly string ContainerType; // TODO remove this. We dont have to send a whole string.
public readonly bool ShowContents;
public readonly bool OccludesLight;
public readonly EntityUid[] ContainedEntities;
public readonly NetEntity[] ContainedEntities;
public ContainerData(string containerType, string id, bool showContents, bool occludesLight, EntityUid[] containedEntities)
public ContainerData(string containerType, bool showContents, bool occludesLight, NetEntity[] containedEntities)
{
ContainerType = containerType;
Id = id;
ShowContents = showContents;
OccludesLight = occludesLight;
ContainedEntities = containedEntities;
}
public void Deconstruct(out string type, out string id, out bool showEnts, out bool occludesLight, out EntityUid[] ents)
public void Deconstruct(out string type, out bool showEnts, out bool occludesLight, out NetEntity[] ents)
{
type = ContainerType;
id = Id;
showEnts = ShowContents;
occludesLight = OccludesLight;
ents = ContainedEntities;
@@ -182,24 +171,7 @@ namespace Robust.Shared.Containers
}
}
[DataDefinition]
private partial struct ContainerPrototypeData
{
[DataField("entities")] public List<EntityUid> Entities = new ();
[DataField("type")] public string? Type = null;
// explicit parameterless constructor is required.
public ContainerPrototypeData() { }
public ContainerPrototypeData(List<EntityUid> entities, string type)
{
Entities = entities;
Type = type;
}
}
public readonly struct AllContainersEnumerable : IEnumerable<IContainer>
public readonly struct AllContainersEnumerable : IEnumerable<BaseContainer>
{
private readonly ContainerManagerComponent? _manager;
@@ -213,7 +185,7 @@ namespace Robust.Shared.Containers
return new(_manager);
}
IEnumerator<IContainer> IEnumerable<IContainer>.GetEnumerator()
IEnumerator<BaseContainer> IEnumerable<BaseContainer>.GetEnumerator()
{
return GetEnumerator();
}
@@ -224,9 +196,9 @@ namespace Robust.Shared.Containers
}
}
public struct AllContainersEnumerator : IEnumerator<IContainer>
public struct AllContainersEnumerator : IEnumerator<BaseContainer>
{
private Dictionary<string, IContainer>.ValueCollection.Enumerator _enumerator;
private Dictionary<string, BaseContainer>.ValueCollection.Enumerator _enumerator;
public AllContainersEnumerator(ContainerManagerComponent? manager)
{
@@ -238,11 +210,8 @@ namespace Robust.Shared.Containers
{
while (_enumerator.MoveNext())
{
if (!_enumerator.Current.Deleted)
{
Current = _enumerator.Current;
return true;
}
Current = _enumerator.Current;
return true;
}
return false;
@@ -250,11 +219,11 @@ namespace Robust.Shared.Containers
void IEnumerator.Reset()
{
((IEnumerator<IContainer>) _enumerator).Reset();
((IEnumerator<BaseContainer>) _enumerator).Reset();
}
[AllowNull]
public IContainer Current { get; private set; }
public BaseContainer Current { get; private set; }
object IEnumerator.Current => Current;

View File

@@ -6,24 +6,27 @@ using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.Shared.Containers
{
[UsedImplicitly]
[SerializedType(ClassName)]
[SerializedType(nameof(ContainerSlot))]
public sealed partial class ContainerSlot : BaseContainer
{
private const string ClassName = "ContainerSlot";
public override int Count => ContainedEntity == null ? 0 : 1;
/// <inheritdoc />
public override IReadOnlyList<EntityUid> ContainedEntities
{
get
{
if (ContainedEntity == null)
if (_containedEntity == null)
return Array.Empty<EntityUid>();
_containedEntityArray ??= new[] { _containedEntity.Value };
DebugTools.Assert(_containedEntityArray[0] == _containedEntity);
return _containedEntityArray;
}
}
@@ -36,39 +39,19 @@ namespace Robust.Shared.Containers
{
_containedEntity = value;
if (value != null)
_containedEntityArray[0] = value!.Value;
{
_containedEntityArray ??= new EntityUid[1];
_containedEntityArray[0] = value.Value;
}
}
}
public override List<EntityUid> ExpectedEntities => _expectedEntities;
[NonSerialized]
private EntityUid? _containedEntity;
private readonly List<EntityUid> _expectedEntities = new();
// Used by ContainedEntities to avoid allocating.
private readonly EntityUid[] _containedEntityArray = new EntityUid[1];
/// <inheritdoc />
public override string ContainerType => ClassName;
/// <inheritdoc />
public override bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
{
return (ContainedEntity == null) && CanInsertIfEmpty(toinsert, entMan);
}
/// <summary>
/// Checks if the entity can be inserted into this container, assuming that the container slot is empty.
/// </summary>
/// <remarks>
/// Useful if you need to know whether an item could be inserted into a slot, without having to actually eject
/// the currently contained entity first.
/// </remarks>
/// <param name="toinsert">The entity to attempt to insert.</param>
/// <returns>True if the entity could be inserted into an empty slot, false otherwise.</returns>
public bool CanInsertIfEmpty(EntityUid toinsert, IEntityManager? entMan = null)
{
return base.CanInsert(toinsert, entMan);
}
[NonSerialized]
private EntityUid[]? _containedEntityArray;
/// <inheritdoc />
public override bool Contains(EntityUid contained)
@@ -77,6 +60,9 @@ namespace Robust.Shared.Containers
return false;
#if DEBUG
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
return true;
var entMan = IoCManager.Resolve<IEntityManager>();
var flags = entMan.GetComponent<MetaDataComponent>(contained).Flags;
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, $"Entity has bad container flags. Ent: {entMan.ToPrettyString(contained)}. Container: {ID}, Owner: {entMan.ToPrettyString(Owner)}");
@@ -84,6 +70,9 @@ namespace Robust.Shared.Containers
return true;
}
protected internal override bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan)
=> ContainedEntity == null || assumeEmpty;
/// <inheritdoc />
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
{
@@ -91,7 +80,7 @@ namespace Robust.Shared.Containers
#if DEBUG
// TODO make this a proper debug assert when gun code no longer fudges client-side spawn prediction.
if (toInsert.IsClientSide() && !Owner.IsClientSide() && Manager.NetSyncEnabled)
if (entMan.IsClientSide(toInsert) && !entMan.IsClientSide(Owner) && Manager.NetSyncEnabled)
Logger.Warning("Inserting a client-side entity into a networked container slot. This will block the container slot and may cause issues.");
#endif
ContainedEntity = toInsert;

View File

@@ -6,10 +6,10 @@ namespace Robust.Shared.Containers;
public abstract class ContainerAttemptEventBase : CancellableEntityEventArgs
{
public readonly IContainer Container;
public readonly BaseContainer Container;
public readonly EntityUid EntityUid;
public ContainerAttemptEventBase(IContainer container, EntityUid entityUid)
public ContainerAttemptEventBase(BaseContainer container, EntityUid entityUid)
{
Container = container;
EntityUid = entityUid;
@@ -18,28 +18,44 @@ public abstract class ContainerAttemptEventBase : CancellableEntityEventArgs
public sealed class ContainerIsInsertingAttemptEvent : ContainerAttemptEventBase
{
public ContainerIsInsertingAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
/// <summary>
/// If true, this check should assume that the container is currently empty.
/// I.e., could the entity be inserted if the container doesn't contain anything else?
/// </summary>
public bool AssumeEmpty { get; set; }
public ContainerIsInsertingAttemptEvent(BaseContainer container, EntityUid entityUid, bool assumeEmpty)
: base(container, entityUid)
{
AssumeEmpty = assumeEmpty;
}
}
public sealed class ContainerGettingInsertedAttemptEvent : ContainerAttemptEventBase
{
public ContainerGettingInsertedAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
/// <summary>
/// If true, this check should assume that the container is currently empty.
/// I.e., could the entity be inserted if the container doesn't contain anything else?
/// </summary>
public bool AssumeEmpty { get; set; }
public ContainerGettingInsertedAttemptEvent(BaseContainer container, EntityUid entityUid, bool assumeEmpty)
: base(container, entityUid)
{
AssumeEmpty = assumeEmpty;
}
}
public sealed class ContainerIsRemovingAttemptEvent : ContainerAttemptEventBase
{
public ContainerIsRemovingAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
public ContainerIsRemovingAttemptEvent(BaseContainer container, EntityUid entityUid) : base(container, entityUid)
{
}
}
public sealed class ContainerGettingRemovedAttemptEvent : ContainerAttemptEventBase
{
public ContainerGettingRemovedAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
public ContainerGettingRemovedAttemptEvent(BaseContainer container, EntityUid entityUid) : base(container, entityUid)
{
}
}

View File

@@ -12,14 +12,14 @@ namespace Robust.Shared.Containers
/// <summary>
/// The container being acted upon.
/// </summary>
public IContainer Container { get; }
public BaseContainer Container { get; }
/// <summary>
/// The entity that was removed or inserted from/into the container.
/// </summary>
public EntityUid Entity { get; }
protected ContainerModifiedMessage(EntityUid entity, IContainer container)
protected ContainerModifiedMessage(EntityUid entity, BaseContainer container)
{
Entity = entity;
Container = container;

View File

@@ -9,5 +9,5 @@ namespace Robust.Shared.Containers;
[PublicAPI]
public sealed class EntGotInsertedIntoContainerMessage : ContainerModifiedMessage
{
public EntGotInsertedIntoContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
public EntGotInsertedIntoContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
}

View File

@@ -11,7 +11,7 @@ namespace Robust.Shared.Containers
{
public readonly EntityUid OldParent;
public EntInsertedIntoContainerMessage(EntityUid entity, EntityUid oldParent, IContainer container) : base(entity, container)
public EntInsertedIntoContainerMessage(EntityUid entity, EntityUid oldParent, BaseContainer container) : base(entity, container)
{
OldParent = oldParent;
}

View File

@@ -9,7 +9,7 @@ namespace Robust.Shared.Containers
[PublicAPI]
public sealed class EntRemovedFromContainerMessage : ContainerModifiedMessage
{
public EntRemovedFromContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
public EntRemovedFromContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
}
/// <summary>
@@ -18,6 +18,6 @@ namespace Robust.Shared.Containers
[PublicAPI]
public sealed class EntGotRemovedFromContainerMessage : ContainerModifiedMessage
{
public EntGotRemovedFromContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
public EntGotRemovedFromContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
}
}

View File

@@ -1,147 +0,0 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Physics.Components;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.Containers
{
/// <summary>
/// A container is a way to "contain" entities inside other entities, in a logical way.
/// This is alike BYOND's <c>contents</c> system, except more advanced.
/// </summary>
/// <remarks>
/// <p>
/// Containers are logical separations of entities contained inside another entity.
/// for example, a crate with two separated compartments would have two separate containers.
/// If an entity inside compartment A drops something,
/// the dropped entity would be placed in compartment A too,
/// and compartment B would be completely untouched.
/// </p>
/// <p>
/// Containers are managed by an entity's <see cref="IContainerManager" />,
/// and have an ID to be referenced by.
/// </p>
/// </remarks>
/// <seealso cref="IContainerManager" />
[PublicAPI]
[ImplicitDataDefinitionForInheritors]
public partial interface IContainer
{
/// <summary>
/// Readonly collection of all the entities contained within this specific container
/// </summary>
IReadOnlyList<EntityUid> ContainedEntities { get; }
List<EntityUid> ExpectedEntities { get; }
/// <summary>
/// The type of this container.
/// </summary>
string ContainerType { get; }
/// <summary>
/// True if the container has been shut down via <see cref="Shutdown" />
/// </summary>
bool Deleted { get; }
/// <summary>
/// The ID of this container.
/// </summary>
string ID { get; }
/// <summary>
/// Prevents light from escaping the container, from ex. a flashlight.
/// </summary>
bool OccludesLight { get; set; }
/// <summary>
/// The entity owning this container.
/// </summary>
EntityUid Owner { get; }
/// <summary>
/// Should the contents of this container be shown? False for closed containers like lockers, true for
/// things like glass display cases.
/// </summary>
bool ShowContents { get; set; }
/// <summary>
/// Checks if the entity can be inserted into this container.
/// </summary>
/// <param name="toinsert">The entity to attempt to insert.</param>
/// <param name="entMan"></param>
/// <returns>True if the entity can be inserted, false otherwise.</returns>
bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null);
/// <summary>
/// Attempts to insert the entity into this container.
/// </summary>
/// <remarks>
/// If the insertion is successful, the inserted entity will end up parented to the
/// container entity, and the inserted entity's local position will be set to the zero vector.
/// </remarks>
/// <param name="toinsert">The entity to insert.</param>
/// <param name="entMan"></param>
/// <returns>False if the entity could not be inserted.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if this container is a child of the entity,
/// which would cause infinite loops.
/// </exception>
bool Insert(EntityUid toinsert,
IEntityManager? entMan = null,
TransformComponent? transform = null,
TransformComponent? ownerTransform = null,
MetaDataComponent? meta = null,
PhysicsComponent? physics = null,
bool force = false);
/// <summary>
/// Checks if the entity can be removed from this container.
/// </summary>
/// <param name="toremove">The entity to check.</param>
/// <param name="entMan"></param>
/// <returns>True if the entity can be removed, false otherwise.</returns>
bool CanRemove(EntityUid toremove, IEntityManager? entMan = null);
/// <summary>
/// Attempts to remove the entity from this container.
/// </summary>
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
/// destination is not null</param>
/// <param name="force">If true, this will not perform can-remove checks.</param>
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
/// container, the grid, or the map.</param>
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
bool Remove(
EntityUid toremove,
IEntityManager? entMan = null,
TransformComponent? xform = null,
MetaDataComponent? meta = null,
bool reparent = true,
bool force = false,
EntityCoordinates? destination = null,
Angle? localRotation = null);
[Obsolete("use force option in Remove()")]
void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null);
/// <summary>
/// Checks if the entity is contained in this container.
/// This is not recursive, so containers of children are not checked.
/// </summary>
/// <param name="contained">The entity to check.</param>
/// <returns>True if the entity is immediately contained in this container, false otherwise.</returns>
bool Contains(EntityUid contained);
/// <summary>
/// Clears the container and marks it as deleted.
/// </summary>
void Shutdown(IEntityManager? entMan = null, INetManager? netMan = null);
}
}

View File

@@ -0,0 +1,49 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Utility;
namespace Robust.Shared.Containers;
public abstract partial class SharedContainerSystem
{
/// <summary>
/// Checks if the entity can be inserted into the given container.
/// </summary>
/// <param name="assumeEmpty">If true, this will check whether the entity could be inserted if the container were
/// empty.</param>
public bool CanInsert(
EntityUid toInsert,
BaseContainer container,
TransformComponent? toInsertXform = null,
bool assumeEmpty = false)
{
if (container.Owner == toInsert)
return false;
if (!assumeEmpty && container.Contains(toInsert))
return false;
if (!container.CanInsert(toInsert, assumeEmpty, EntityManager))
return false;
if (!TransformQuery.Resolve(toInsert, ref toInsertXform))
return false;
// no, you can't put maps or grids into containers
if (_mapQuery.HasComponent(toInsert) || _gridQuery.HasComponent(toInsert))
return false;
// Prevent circular insertion.
if (_transform.ContainsEntity(toInsertXform, container.Owner))
return false;
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(container, toInsert, assumeEmpty);
RaiseLocalEvent(container.Owner, insertAttemptEvent, true);
if (insertAttemptEvent.Cancelled)
return false;
var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(container, toInsert, assumeEmpty);
RaiseLocalEvent(toInsert, gettingInsertedAttemptEvent, true);
return !gettingInsertedAttemptEvent.Cancelled;
}
}

View File

@@ -0,0 +1,26 @@
using Robust.Shared.GameObjects;
namespace Robust.Shared.Containers;
public abstract partial class SharedContainerSystem
{
/// <summary>
/// Checks if the entity can be removed from this container.
/// </summary>
/// <returns>True if the entity can be removed, false otherwise.</returns>
public bool CanRemove(EntityUid toRemove, BaseContainer container)
{
if (!container.Contains(toRemove))
return false;
//raise events
var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(container, toRemove);
RaiseLocalEvent(container.Owner, removeAttemptEvent, true);
if (removeAttemptEvent.Cancelled)
return false;
var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(container, toRemove);
RaiseLocalEvent(toRemove, gettingRemovedAttemptEvent, true);
return !gettingRemovedAttemptEvent.Cancelled;
}
}

View File

@@ -13,22 +13,18 @@ public abstract partial class SharedContainerSystem : EntitySystem
{
private void OnStartupValidation(EntityUid uid, ContainerManagerComponent component, ComponentStartup args)
{
var metaQuery = GetEntityQuery<MetaDataComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
var physicsQuery = GetEntityQuery<PhysicsComponent>();
var jointQuery = GetEntityQuery<JointComponent>();
foreach (var cont in component.Containers.Values)
{
foreach (var ent in cont.ContainedEntities)
{
if (!metaQuery.TryGetComponent(ent, out var meta))
if (!MetaQuery.TryGetComponent(ent, out var meta))
{
ValidateMissingEntity(uid, cont, ent);
continue;
}
var xform = xformQuery.GetComponent(ent);
physicsQuery.TryGetComponent(ent, out var physics);
var xform = TransformQuery.GetComponent(ent);
PhysicsQuery.TryGetComponent(ent, out var physics);
DebugTools.Assert(xform.ParentUid == uid,
$"Entity not parented to its container. Entity: {ToPrettyString(ent)}, parent: {ToPrettyString(uid)}");
@@ -46,15 +42,15 @@ public abstract partial class SharedContainerSystem : EntitySystem
// entities in containers without having to "re-insert" them.
meta.Flags |= MetaDataFlags.InContainer;
_lookup.RemoveFromEntityTree(ent, xform);
((BaseContainer)cont).RecursivelyUpdatePhysics(ent, xform, physics, _physics, physicsQuery, xformQuery);
cont.RecursivelyUpdatePhysics(ent, xform, physics, _physics, PhysicsQuery, TransformQuery);
// assert children have correct properties
ValidateChildren(xform, xformQuery, physicsQuery);
ValidateChildren(xform, TransformQuery, PhysicsQuery);
}
}
}
protected abstract void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing);
protected abstract void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing);
private void ValidateChildren(TransformComponent xform, EntityQuery<TransformComponent> xformQuery, EntityQuery<PhysicsComponent> physicsQuery)
{

View File

@@ -6,7 +6,9 @@ using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Utility;
@@ -16,9 +18,13 @@ namespace Robust.Shared.Containers
{
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private EntityQuery<MetaDataComponent> _metas;
private EntityQuery<TransformComponent> _xforms;
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<MapComponent> _mapQuery;
protected EntityQuery<MetaDataComponent> MetaQuery;
protected EntityQuery<PhysicsComponent> PhysicsQuery;
protected EntityQuery<TransformComponent> TransformQuery;
/// <inheritdoc />
public override void Initialize()
@@ -29,25 +35,27 @@ namespace Robust.Shared.Containers
SubscribeLocalEvent<ContainerManagerComponent, ComponentStartup>(OnStartupValidation);
SubscribeLocalEvent<ContainerManagerComponent, ComponentGetState>(OnContainerGetState);
_metas = EntityManager.GetEntityQuery<MetaDataComponent>();
_xforms = EntityManager.GetEntityQuery<TransformComponent>();
_gridQuery = GetEntityQuery<MapGridComponent>();
_mapQuery = GetEntityQuery<MapComponent>();
MetaQuery = GetEntityQuery<MetaDataComponent>();
PhysicsQuery = GetEntityQuery<PhysicsComponent>();
TransformQuery = GetEntityQuery<TransformComponent>();
}
private void OnContainerGetState(EntityUid uid, ContainerManagerComponent component, ref ComponentGetState args)
{
// naive implementation that just sends the full state of the component
Dictionary<string, ContainerManagerComponent.ContainerManagerComponentState.ContainerData> containerSet = new(component.Containers.Count);
foreach (var container in component.Containers.Values)
{
var uidArr = new EntityUid[container.ContainedEntities.Count];
var uidArr = new NetEntity[container.ContainedEntities.Count];
for (var index = 0; index < container.ContainedEntities.Count; index++)
{
uidArr[index] = container.ContainedEntities[index];
uidArr[index] = GetNetEntity(container.ContainedEntities[index]);
}
var sContainer = new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.ContainerType, container.ID, container.ShowContents, container.OccludesLight, uidArr);
var sContainer = new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.GetType().Name, container.ShowContents, container.OccludesLight, uidArr);
containerSet.Add(container.ID, sContainer);
}
@@ -59,7 +67,7 @@ namespace Robust.Shared.Containers
#region Proxy Methods
public T MakeContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
where T : IContainer
where T : BaseContainer
{
if (!Resolve(uid, ref containerManager, false))
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid); // Happy Vera.
@@ -68,7 +76,7 @@ namespace Robust.Shared.Containers
}
public T EnsureContainer<T>(EntityUid uid, string id, out bool alreadyExisted, ContainerManagerComponent? containerManager = null)
where T : IContainer
where T : BaseContainer
{
if (!Resolve(uid, ref containerManager, false))
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid);
@@ -88,12 +96,12 @@ namespace Robust.Shared.Containers
}
public T EnsureContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
where T : IContainer
where T : BaseContainer
{
return EnsureContainer<T>(uid, id, out _, containerManager);
}
public IContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
public BaseContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
{
if (!Resolve(uid, ref containerManager))
throw new ArgumentException("Entity does not have a ContainerManagerComponent!", nameof(uid));
@@ -109,7 +117,7 @@ namespace Robust.Shared.Containers
return containerManager.HasContainer(id);
}
public bool TryGetContainer(EntityUid uid, string id, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null)
public bool TryGetContainer(EntityUid uid, string id, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null)
{
if (Resolve(uid, ref containerManager, false))
return containerManager.TryGetContainer(id, out container);
@@ -118,7 +126,7 @@ namespace Robust.Shared.Containers
return false;
}
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
{
if (Resolve(uid, ref containerManager, false) && (skipExistCheck || EntityManager.EntityExists(containedUid)))
return containerManager.TryGetContainer(containedUid, out container);
@@ -164,7 +172,7 @@ namespace Robust.Shared.Containers
#region Container Helpers
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out IContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out BaseContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
{
container = null;
@@ -227,7 +235,7 @@ namespace Robust.Shared.Containers
}
/// <summary>
/// Finds the first instance of a component on the recursive parented containers that hold an entity
/// Finds the first instance of a component on the recursive parented containers that hold an entity
/// </summary>
public bool TryFindComponentOnEntityContainerOrParent<T>(
EntityUid uid,
@@ -236,13 +244,13 @@ namespace Robust.Shared.Containers
MetaDataComponent? meta = null,
TransformComponent? xform = null) where T : Component
{
if (!_metas.Resolve(uid, ref meta))
if (!MetaQuery.Resolve(uid, ref meta))
return false;
if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
return false;
if (!_xforms.Resolve(uid, ref xform))
if (!TransformQuery.Resolve(uid, ref xform))
return false;
if (!xform.ParentUid.Valid)
@@ -255,7 +263,7 @@ namespace Robust.Shared.Containers
}
/// <summary>
/// Finds all instances of a component on the recursive parented containers that hold an entity
/// Finds all instances of a component on the recursive parented containers that hold an entity
/// </summary>
public bool TryFindComponentsOnEntityContainerOrParent<T>(
EntityUid uid,
@@ -264,13 +272,13 @@ namespace Robust.Shared.Containers
MetaDataComponent? meta = null,
TransformComponent? xform = null) where T : Component
{
if (!_metas.Resolve(uid, ref meta))
if (!MetaQuery.Resolve(uid, ref meta))
return foundComponents.Any();
if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
return foundComponents.Any();
if (!_xforms.Resolve(uid, ref xform))
if (!TransformQuery.Resolve(uid, ref xform))
return foundComponents.Any();
if (!xform.ParentUid.Valid)
@@ -335,8 +343,8 @@ namespace Robust.Shared.Containers
public bool IsInSameOrTransparentContainer(
EntityUid user,
EntityUid other,
IContainer? userContainer = null,
IContainer? otherContainer = null,
BaseContainer? userContainer = null,
BaseContainer? otherContainer = null,
bool userSeeInsideSelf = false)
{
if (userContainer == null)
@@ -371,14 +379,14 @@ namespace Robust.Shared.Containers
/// <summary>
/// Gets the top-most container in the hierarchy for this entity, if it exists.
/// </summary>
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out IContainer? container)
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out BaseContainer? container)
{
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
return TryGetOuterContainer(uid, xform, out container, xformQuery);
}
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform,
[NotNullWhen(true)] out IContainer? container, EntityQuery<TransformComponent> xformQuery)
[NotNullWhen(true)] out BaseContainer? container, EntityQuery<TransformComponent> xformQuery)
{
container = null;
@@ -448,7 +456,7 @@ namespace Robust.Shared.Containers
/// Attempts to remove all entities in a container. Returns removed entities.
/// </summary>
public List<EntityUid> EmptyContainer(
IContainer container,
BaseContainer container,
bool force = false,
EntityCoordinates? destination = null,
bool reparent = true)
@@ -470,7 +478,7 @@ namespace Robust.Shared.Containers
/// <summary>
/// Attempts to remove and delete all entities in a container.
/// </summary>
public void CleanContainer(IContainer container)
public void CleanContainer(BaseContainer container)
{
foreach (var ent in container.ContainedEntities.ToArray())
{
@@ -491,7 +499,7 @@ namespace Robust.Shared.Containers
transform.AttachToGridOrMap();
}
private bool TryInsertIntoContainer(TransformComponent transform, IContainer container)
private bool TryInsertIntoContainer(TransformComponent transform, BaseContainer container)
{
if (container.Insert(transform.Owner)) return true;

View File

@@ -63,6 +63,13 @@ namespace Robust.Shared.GameObjects
[DataField("desc")] internal string? _entityDescription;
internal EntityPrototype? _entityPrototype;
/// <summary>
/// Network identifier for this entity.
/// </summary>
[ViewVariables]
[Access(typeof(EntityManager), Other = AccessPermissions.ReadExecute)]
public NetEntity NetEntity { get; internal set; } = NetEntity.Invalid;
/// <summary>
/// When this entity was paused, if applicable. Note that this is the actual time, not the duration which gets
/// returned by <see cref="MetaDataSystem.GetPauseTime"/>.

View File

@@ -64,21 +64,29 @@ namespace Robust.Shared.GameObjects
{
}
/// <summary>
/// Abstract class for local BUI events.
/// </summary>
public abstract class BaseLocalBoundUserInterfaceEvent : BaseBoundUserInterfaceEvent
{
/// <summary>
/// The Entity receiving the message.
/// Only set when the message is raised as a directed event.
/// </summary>
public EntityUid Entity = EntityUid.Invalid;
}
[NetSerializable, Serializable]
public abstract class BoundUserInterfaceMessage : EntityEventArgs
/// <summary>
/// Abstract class for all BUI events.
/// </summary>
[Serializable, NetSerializable]
public abstract class BaseBoundUserInterfaceEvent : EntityEventArgs
{
/// <summary>
/// The UI of this message.
/// Only set when the message is raised as a directed event.
/// </summary>
public Enum UiKey { get; set; } = default!;
/// <summary>
/// The Entity receiving the message.
/// Only set when the message is raised as a directed event.
/// </summary>
public EntityUid Entity { get; set; } = EntityUid.Invalid;
public Enum UiKey = default!;
/// <summary>
/// The session sending or receiving this message.
@@ -88,6 +96,19 @@ namespace Robust.Shared.GameObjects
public ICommonSession Session = default!;
}
/// <summary>
/// Abstract class for networked BUI events.
/// </summary>
[NetSerializable, Serializable]
public abstract class BoundUserInterfaceMessage : BaseBoundUserInterfaceEvent
{
/// <summary>
/// The Entity receiving the message.
/// Only set when the message is raised as a directed event.
/// </summary>
public NetEntity Entity { get; set; } = NetEntity.Invalid;
}
[NetSerializable, Serializable]
internal sealed class UpdateBoundStateMessage : BoundUserInterfaceMessage
{
@@ -112,11 +133,11 @@ namespace Robust.Shared.GameObjects
[Serializable, NetSerializable]
internal sealed class BoundUIWrapMessage : EntityEventArgs
{
public readonly EntityUid Entity;
public readonly NetEntity Entity;
public readonly BoundUserInterfaceMessage Message;
public readonly Enum UiKey;
public BoundUIWrapMessage(EntityUid entity, BoundUserInterfaceMessage message, Enum uiKey)
public BoundUIWrapMessage(NetEntity entity, BoundUserInterfaceMessage message, Enum uiKey)
{
Message = message;
UiKey = uiKey;
@@ -129,7 +150,7 @@ namespace Robust.Shared.GameObjects
}
}
public sealed class BoundUIOpenedEvent : BoundUserInterfaceMessage
public sealed class BoundUIOpenedEvent : BaseLocalBoundUserInterfaceEvent
{
public BoundUIOpenedEvent(Enum uiKey, EntityUid uid, ICommonSession session)
{
@@ -139,7 +160,7 @@ namespace Robust.Shared.GameObjects
}
}
public sealed class BoundUIClosedEvent : BoundUserInterfaceMessage
public sealed class BoundUIClosedEvent : BaseLocalBoundUserInterfaceEvent
{
public BoundUIClosedEvent(Enum uiKey, EntityUid uid, ICommonSession session)
{

View File

@@ -1065,7 +1065,7 @@ namespace Robust.Shared.GameObjects
where TComp1 : Component
{
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
return new EntityQueryEnumerator<TComp1>(trait1, _metaQuery);
return new EntityQueryEnumerator<TComp1>(trait1, MetaQuery);
}
public EntityQueryEnumerator<TComp1, TComp2> EntityQueryEnumerator<TComp1, TComp2>()
@@ -1074,7 +1074,7 @@ namespace Robust.Shared.GameObjects
{
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
return new EntityQueryEnumerator<TComp1, TComp2>(trait1, trait2, _metaQuery);
return new EntityQueryEnumerator<TComp1, TComp2>(trait1, trait2, MetaQuery);
}
public EntityQueryEnumerator<TComp1, TComp2, TComp3> EntityQueryEnumerator<TComp1, TComp2, TComp3>()
@@ -1085,7 +1085,7 @@ namespace Robust.Shared.GameObjects
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
var trait3 = _entTraitArray[CompIdx.ArrayIndex<TComp3>()];
return new EntityQueryEnumerator<TComp1, TComp2, TComp3>(trait1, trait2, trait3, _metaQuery);
return new EntityQueryEnumerator<TComp1, TComp2, TComp3>(trait1, trait2, trait3, MetaQuery);
}
public EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>()
@@ -1099,7 +1099,7 @@ namespace Robust.Shared.GameObjects
var trait3 = _entTraitArray[CompIdx.ArrayIndex<TComp3>()];
var trait4 = _entTraitArray[CompIdx.ArrayIndex<TComp4>()];
return new EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>(trait1, trait2, trait3, trait4, _metaQuery);
return new EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>(trait1, trait2, trait3, trait4, MetaQuery);
}
/// <inheritdoc />
@@ -1121,7 +1121,7 @@ namespace Robust.Shared.GameObjects
{
foreach (var t1Comp in comps.Values)
{
if (t1Comp.Deleted || !_metaQuery.TryGetComponentInternal(t1Comp.Owner, out var metaComp)) continue;
if (t1Comp.Deleted || !MetaQuery.TryGetComponentInternal(t1Comp.Owner, out var metaComp)) continue;
if (metaComp.EntityPaused) continue;
@@ -1312,7 +1312,7 @@ namespace Robust.Shared.GameObjects
{
foreach (var (uid, comp) in comps)
{
if (comp.Deleted || !_metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) continue;
if (comp.Deleted || !MetaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) continue;
yield return (uid, comp);
}

View File

@@ -0,0 +1,585 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Robust.Shared.Map;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects;
public partial class EntityManager
{
// TODO POOLING
// Just add overrides that take in an existing collection.
/// <summary>
/// Inverse lookup for net entities.
/// Regular lookup uses MetadataComponent.
/// </summary>
protected readonly Dictionary<NetEntity, EntityUid> NetEntityLookup = new(EntityCapacity);
/// <summary>
/// Clears an old inverse lookup for a particular entityuid.
/// Do not call this unless you are sure of what you're doing.
/// </summary>
internal void ClearNetEntity(NetEntity netEntity)
{
NetEntityLookup.Remove(netEntity);
}
/// <summary>
/// Set the inverse lookup for a particular entityuid.
/// Do not call this unless you are sure of what you're doing.
/// </summary>
internal void SetNetEntity(EntityUid uid, NetEntity netEntity, MetaDataComponent component)
{
DebugTools.Assert(!NetEntityLookup.ContainsKey(netEntity));
NetEntityLookup[netEntity] = uid;
component.NetEntity = netEntity;
}
/// <inheritdoc />
public virtual bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null)
{
return false;
}
#region NetEntity
/// <inheritdoc />
public bool TryParseNetEntity(string arg, [NotNullWhen(true)] out EntityUid? entity)
{
if (!NetEntity.TryParse(arg, out var netEntity) ||
!TryGetEntity(netEntity, out entity))
{
entity = null;
return false;
}
return true;
}
/// <inheritdoc />
public bool TryGetEntity(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity)
{
if (NetEntityLookup.TryGetValue(nEntity, out var went))
{
entity = went;
return true;
}
entity = EntityUid.Invalid;
return false;
}
/// <inheritdoc />
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity)
{
if (nEntity == null)
{
entity = EntityUid.Invalid;
return false;
}
return TryGetEntity(nEntity.Value, out entity);
}
/// <inheritdoc />
public bool TryGetNetEntity(EntityUid uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
{
if (uid == EntityUid.Invalid)
{
netEntity = NetEntity.Invalid;
return false;
}
// TODO NetEntity figure out why this happens
// I wanted this to logMissing but it seems to break a loootttt of dodgy stuff on content.
if (MetaQuery.Resolve(uid, ref metadata, false))
{
netEntity = metadata.NetEntity;
return true;
}
netEntity = NetEntity.Invalid;
return false;
}
/// <inheritdoc />
public bool TryGetNetEntity(EntityUid? uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
{
if (uid == null)
{
netEntity = NetEntity.Invalid;
return false;
}
return TryGetNetEntity(uid.Value, out netEntity, metadata);
}
/// <inheritdoc />
public virtual EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity)
{
// On server we don't want to ensure any reserved entities for later or flag for comp state handling
// so this is just GetEntity. Client-side code overrides this method.
return GetEntity(nEntity);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityUid? EnsureEntity<T>(NetEntity? nEntity, EntityUid callerEntity)
{
if (nEntity == null)
return null;
return EnsureEntity<T>(nEntity.Value, callerEntity);
}
/// <inheritdoc />
public EntityUid GetEntity(NetEntity nEntity)
{
if (nEntity == NetEntity.Invalid)
return EntityUid.Invalid;
return NetEntityLookup.GetValueOrDefault(nEntity);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityUid? GetEntity(NetEntity? nEntity)
{
if (nEntity == null)
return null;
return GetEntity(nEntity.Value);
}
/// <inheritdoc />
public NetEntity GetNetEntity(EntityUid uid, MetaDataComponent? metadata = null)
{
if (uid == EntityUid.Invalid)
return NetEntity.Invalid;
if (!MetaQuery.Resolve(uid, ref metadata))
return NetEntity.Invalid;
return metadata.NetEntity;
}
/// <inheritdoc />
public NetEntity? GetNetEntity(EntityUid? uid, MetaDataComponent? metadata = null)
{
if (uid == null)
return null;
return GetNetEntity(uid.Value, metadata);
}
#endregion
#region NetCoordinates
/// <inheritdoc />
public NetCoordinates GetNetCoordinates(EntityCoordinates coordinates, MetaDataComponent? metadata = null)
{
return new NetCoordinates(GetNetEntity(coordinates.EntityId, metadata), coordinates.Position);
}
/// <inheritdoc />
public NetCoordinates? GetNetCoordinates(EntityCoordinates? coordinates, MetaDataComponent? metadata = null)
{
if (coordinates == null)
return null;
return new NetCoordinates(GetNetEntity(coordinates.Value.EntityId, metadata), coordinates.Value.Position);
}
/// <inheritdoc />
public EntityCoordinates GetCoordinates(NetCoordinates coordinates)
{
return new EntityCoordinates(GetEntity(coordinates.NetEntity), coordinates.Position);
}
/// <inheritdoc />
public EntityCoordinates? GetCoordinates(NetCoordinates? coordinates)
{
if (coordinates == null)
return null;
return new EntityCoordinates(GetEntity(coordinates.Value.NetEntity), coordinates.Value.Position);
}
/// <inheritdoc />
public virtual EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
{
// See EnsureEntity
return GetCoordinates(netCoordinates);
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityCoordinates? EnsureCoordinates<T>(NetCoordinates? netCoordinates, EntityUid callerEntity)
{
if (netCoordinates == null)
return null;
return EnsureCoordinates<T>(netCoordinates.Value, callerEntity);
}
#endregion
#region Collection helpers
/// <inheritdoc />
public HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities)
{
var entities = new HashSet<EntityUid>();
entities.EnsureCapacity(netEntities.Count);
foreach (var netEntity in netEntities)
{
entities.Add(GetEntity(netEntity));
}
return entities;
}
/// <inheritdoc />
public List<EntityUid> GetEntityList(List<NetEntity> netEntities)
{
var entities = new List<EntityUid>(netEntities.Count);
foreach (var netEntity in netEntities)
{
entities.Add(GetEntity(netEntity));
}
return entities;
}
public HashSet<EntityUid> EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity)
{
var entities = new HashSet<EntityUid>(netEntities.Count);
foreach (var netEntity in netEntities)
{
entities.Add(EnsureEntity<T>(netEntity, callerEntity));
}
return entities;
}
/// <inheritdoc />
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity)
{
var entities = new List<EntityUid>(netEntities.Count);
foreach (var netEntity in netEntities)
{
entities.Add(EnsureEntity<T>(netEntity, callerEntity));
}
return entities;
}
/// <inheritdoc />
public List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities)
{
var entities = new List<EntityUid>(netEntities.Count);
foreach (var netEntity in netEntities)
{
entities.Add(GetEntity(netEntity));
}
return entities;
}
/// <inheritdoc />
public List<EntityUid?> GetEntityList(List<NetEntity?> netEntities)
{
var entities = new List<EntityUid?>(netEntities.Count);
foreach (var netEntity in netEntities)
{
entities.Add(GetEntity(netEntity));
}
return entities;
}
/// <inheritdoc />
public EntityUid[] GetEntityArray(NetEntity[] netEntities)
{
var entities = new EntityUid[netEntities.Length];
for (var i = 0; i < netEntities.Length; i++)
{
entities[i] = GetEntity(netEntities[i]);
}
return entities;
}
/// <inheritdoc />
public EntityUid?[] GetEntityArray(NetEntity?[] netEntities)
{
var entities = new EntityUid?[netEntities.Length];
for (var i = 0; i < netEntities.Length; i++)
{
entities[i] = GetEntity(netEntities[i]);
}
return entities;
}
/// <inheritdoc />
public HashSet<NetEntity> GetNetEntitySet(HashSet<EntityUid> entities)
{
var newSet = new HashSet<NetEntity>(entities.Count);
foreach (var ent in entities)
{
MetaQuery.TryGetComponent(ent, out var metadata);
newSet.Add(GetNetEntity(ent, metadata));
}
return newSet;
}
/// <inheritdoc />
public List<NetEntity> GetNetEntityList(List<EntityUid> entities)
{
var netEntities = new List<NetEntity>(entities.Count);
foreach (var netEntity in entities)
{
netEntities.Add(GetNetEntity(netEntity));
}
return netEntities;
}
/// <inheritdoc />
public List<NetEntity> GetNetEntityList(IReadOnlyList<EntityUid> entities)
{
var netEntities = new List<NetEntity>(entities.Count);
foreach (var netEntity in entities)
{
netEntities.Add(GetNetEntity(netEntity));
}
return netEntities;
}
/// <inheritdoc />
public List<NetEntity> GetNetEntityList(ICollection<EntityUid> entities)
{
var netEntities = new List<NetEntity>(entities.Count);
foreach (var netEntity in entities)
{
netEntities.Add(GetNetEntity(netEntity));
}
return netEntities;
}
/// <inheritdoc />
public List<NetEntity?> GetNetEntityList(List<EntityUid?> entities)
{
var netEntities = new List<NetEntity?>(entities.Count);
foreach (var netEntity in entities)
{
netEntities.Add(GetNetEntity(netEntity));
}
return netEntities;
}
/// <inheritdoc />
public NetEntity[] GetNetEntityArray(EntityUid[] entities)
{
var netEntities = new NetEntity[entities.Length];
for (var i = 0; i < entities.Length; i++)
{
netEntities[i] = GetNetEntity(entities[i]);
}
return netEntities;
}
/// <inheritdoc />
public NetEntity?[] GetNetEntityArray(EntityUid?[] entities)
{
var netEntities = new NetEntity?[entities.Length];
for (var i = 0; i < entities.Length; i++)
{
netEntities[i] = GetNetEntity(entities[i]);
}
return netEntities;
}
/// <inheritdoc />
public HashSet<EntityCoordinates> GetEntitySet(HashSet<NetCoordinates> netEntities)
{
var entities = new HashSet<EntityCoordinates>(netEntities.Count);
foreach (var netCoordinates in netEntities)
{
entities.Add(GetCoordinates(netCoordinates));
}
return entities;
}
/// <inheritdoc />
public List<EntityCoordinates> GetEntityList(List<NetCoordinates> netEntities)
{
var entities = new List<EntityCoordinates>(netEntities.Count);
foreach (var netCoordinates in netEntities)
{
entities.Add(GetCoordinates(netCoordinates));
}
return entities;
}
/// <inheritdoc />
public List<EntityCoordinates> GetEntityList(ICollection<NetCoordinates> netEntities)
{
var entities = new List<EntityCoordinates>(netEntities.Count);
foreach (var netCoordinates in netEntities)
{
entities.Add(GetCoordinates(netCoordinates));
}
return entities;
}
/// <inheritdoc />
public List<EntityCoordinates?> GetEntityList(List<NetCoordinates?> netEntities)
{
var entities = new List<EntityCoordinates?>(netEntities.Count);
foreach (var netCoordinates in netEntities)
{
entities.Add(GetCoordinates(netCoordinates));
}
return entities;
}
/// <inheritdoc />
public EntityCoordinates[] GetEntityArray(NetCoordinates[] netEntities)
{
var entities = new EntityCoordinates[netEntities.Length];
for (var i = 0; i < netEntities.Length; i++)
{
entities[i] = GetCoordinates(netEntities[i]);
}
return entities;
}
/// <inheritdoc />
public EntityCoordinates?[] GetEntityArray(NetCoordinates?[] netEntities)
{
var entities = new EntityCoordinates?[netEntities.Length];
for (var i = 0; i < netEntities.Length; i++)
{
entities[i] = GetCoordinates(netEntities[i]);
}
return entities;
}
/// <inheritdoc />
public HashSet<NetCoordinates> GetNetCoordinatesSet(HashSet<EntityCoordinates> entities)
{
var newSet = new HashSet<NetCoordinates>(entities.Count);
foreach (var coordinates in entities)
{
newSet.Add(GetNetCoordinates(coordinates));
}
return newSet;
}
/// <inheritdoc />
public List<NetCoordinates> GetNetCoordinatesList(List<EntityCoordinates> entities)
{
var netEntities = new List<NetCoordinates>(entities.Count);
foreach (var netCoordinates in entities)
{
netEntities.Add(GetNetCoordinates(netCoordinates));
}
return netEntities;
}
/// <inheritdoc />
public List<NetCoordinates> GetNetCoordinatesList(ICollection<EntityCoordinates> entities)
{
var netEntities = new List<NetCoordinates>(entities.Count);
foreach (var netCoordinates in entities)
{
netEntities.Add(GetNetCoordinates(netCoordinates));
}
return netEntities;
}
/// <inheritdoc />
public List<NetCoordinates?> GetNetCoordinatesList(List<EntityCoordinates?> entities)
{
var netEntities = new List<NetCoordinates?>(entities.Count);
foreach (var netCoordinates in entities)
{
netEntities.Add(GetNetCoordinates(netCoordinates));
}
return netEntities;
}
/// <inheritdoc />
public NetCoordinates[] GetNetCoordinatesArray(EntityCoordinates[] entities)
{
var netEntities = new NetCoordinates[entities.Length];
for (var i = 0; i < entities.Length; i++)
{
netEntities[i] = GetNetCoordinates(entities[i]);
}
return netEntities;
}
/// <inheritdoc />
public NetCoordinates?[] GetNetCoordinatesArray(EntityCoordinates?[] entities)
{
var netEntities = new NetCoordinates?[entities.Length];
for (var i = 0; i < entities.Length; i++)
{
netEntities[i] = GetNetCoordinates(entities[i]);
}
return netEntities;
}
#endregion
}

View File

@@ -13,11 +13,11 @@ public partial class EntityManager
// This method will soon be marked as obsolete.
public EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
=> SpawnAttachedTo(protoName, coordinates, overrides);
// This method will soon be marked as obsolete.
public EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
=> Spawn(protoName, coordinates, overrides);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames)
{
@@ -100,8 +100,8 @@ public partial class EntityManager
if (!xform.ParentUid.IsValid())
return false;
if (!_metaQuery.TryGetComponent(target, out var meta))
if (!MetaQuery.TryGetComponent(target, out var meta))
return false;
if ((meta.Flags & MetaDataFlags.InContainer) == 0)
@@ -135,7 +135,7 @@ public partial class EntityManager
EntityUid containerUid,
string containerId,
[NotNullWhen(true)] out EntityUid? uid,
ContainerManagerComponent? containerComp = null,
ContainerManagerComponent? containerComp = null,
ComponentRegistry? overrides = null)
{
uid = null;
@@ -160,7 +160,7 @@ public partial class EntityManager
xform ??= _xformQuery.GetComponent(target);
if (!xform.ParentUid.IsValid())
return Spawn(protoName);
var uid = Spawn(protoName, overrides);
_xforms.PlaceNextToOrDrop(uid, target);
return uid;
@@ -180,7 +180,7 @@ public partial class EntityManager
|| !containerComp.Containers.TryGetValue(containerId, out var container)
|| !container.Insert(uid, this))
{
xform ??= _xformQuery.GetComponent(containerUid);
if (xform.ParentUid.IsValid())
_xforms.PlaceNextToOrDrop(uid, containerUid, targetXform: xform);

View File

@@ -11,7 +11,6 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Robust.Shared.Physics;
using Robust.Shared.Serialization.Markdown.Mapping;
namespace Robust.Shared.GameObjects
@@ -38,7 +37,7 @@ namespace Robust.Shared.GameObjects
// positions on spawn....
private SharedTransformSystem _xforms = default!;
private EntityQuery<MetaDataComponent> _metaQuery;
protected EntityQuery<MetaDataComponent> MetaQuery;
private EntityQuery<TransformComponent> _xformQuery;
#endregion Dependencies
@@ -68,14 +67,16 @@ namespace Robust.Shared.GameObjects
private EntityEventBus _eventBus = null!;
protected virtual int NextEntityUid { get; set; } = (int) EntityUid.FirstUid;
protected int NextEntityUid = (int) EntityUid.FirstUid;
protected int NextNetworkId = (int) NetEntity.First;
/// <inheritdoc />
public IEventBus EventBus => _eventBus;
public event Action<EntityUid>? EntityAdded;
public event Action<EntityUid>? EntityInitialized;
public event Action<EntityUid>? EntityDeleted;
public event Action<EntityUid, MetaDataComponent>? EntityDeleted;
/// <summary>
/// Raised when an entity is queued for deletion. Not raised if an entity is deleted.
@@ -123,7 +124,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
public bool IsDefault(EntityUid uid)
{
if (!_metaQuery.TryGetComponent(uid, out var metadata) || metadata.EntityPrototype == null)
if (!MetaQuery.TryGetComponent(uid, out var metadata) || metadata.EntityPrototype == null)
return false;
var prototype = metadata.EntityPrototype;
@@ -232,7 +233,7 @@ namespace Robust.Shared.GameObjects
_eventBus.CalcOrdering();
_mapSystem = System<SharedMapSystem>();
_xforms = System<SharedTransformSystem>();
_metaQuery = GetEntityQuery<MetaDataComponent>();
MetaQuery = GetEntityQuery<MetaDataComponent>();
_xformQuery = GetEntityQuery<TransformComponent>();
}
@@ -304,19 +305,19 @@ namespace Robust.Shared.GameObjects
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
{
return CreateEntity(prototypeName, euid, overrides);
return CreateEntity(prototypeName, overrides);
}
/// <inheritdoc />
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null)
{
return CreateEntity(prototypeName, default, overrides);
return CreateEntity(prototypeName, overrides);
}
/// <inheritdoc />
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
{
var newEntity = CreateEntity(prototypeName, default, overrides);
var newEntity = CreateEntity(prototypeName, overrides);
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
return newEntity;
}
@@ -324,7 +325,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
{
var newEntity = CreateEntity(prototypeName, default, overrides);
var newEntity = CreateEntity(prototypeName, overrides);
var transform = _xformQuery.GetComponent(newEntity);
if (coordinates.MapId == MapId.Nullspace)
@@ -415,14 +416,10 @@ namespace Robust.Shared.GameObjects
if (!Started)
return;
var metaQuery = GetEntityQuery<MetaDataComponent>();
var xformQuery = GetEntityQuery<TransformComponent>();
var xformSys = EntitySysManager.GetEntitySystem<SharedTransformSystem>();
// Networking blindly spams entities at this function, they can already be
// deleted from being a child of a previously deleted entity
// TODO: Why does networking need to send deletes for child entities?
if (!metaQuery.TryGetComponent(e, out var meta) || meta.EntityDeleted)
if (!MetaQuery.TryGetComponent(e, out var meta) || meta.EntityDeleted)
return;
if (meta.EntityLifeStage == EntityLifeStage.Terminating)
@@ -436,19 +433,17 @@ namespace Robust.Shared.GameObjects
}
// Notify all entities they are being terminated prior to detaching & deleting
RecursiveFlagEntityTermination(e, meta, metaQuery, xformQuery);
RecursiveFlagEntityTermination(e, meta);
// Then actually delete them
RecursiveDeleteEntity(e, meta, metaQuery, xformQuery, xformSys);
RecursiveDeleteEntity(e, meta);
}
private void RecursiveFlagEntityTermination(
EntityUid uid,
MetaDataComponent metadata,
EntityQuery<MetaDataComponent> metaQuery,
EntityQuery<TransformComponent> xformQuery)
MetaDataComponent metadata)
{
var transform = xformQuery.GetComponent(uid);
var transform = _xformQuery.GetComponent(uid);
metadata.EntityLifeStage = EntityLifeStage.Terminating;
try
@@ -463,27 +458,24 @@ namespace Robust.Shared.GameObjects
foreach (var child in transform._children)
{
if (!metaQuery.TryGetComponent(child, out var childMeta) || childMeta.EntityDeleted)
if (!MetaQuery.TryGetComponent(child, out var childMeta) || childMeta.EntityDeleted)
{
_sawmill.Error($"A deleted entity was still the transform child of another entity. Parent: {ToPrettyString(uid, metadata)}.");
transform._children.Remove(child);
continue;
}
RecursiveFlagEntityTermination(child, childMeta, metaQuery, xformQuery);
RecursiveFlagEntityTermination(child, childMeta);
}
}
private void RecursiveDeleteEntity(
EntityUid uid,
MetaDataComponent metadata,
EntityQuery<MetaDataComponent> metaQuery,
EntityQuery<TransformComponent> xformQuery,
SharedTransformSystem xformSys)
MetaDataComponent metadata)
{
// Note about this method: #if EXCEPTION_TOLERANCE is not used here because we're gonna it in the future...
var transform = xformQuery.GetComponent(uid);
var netEntity = GetNetEntity(uid, metadata);
var transform = _xformQuery.GetComponent(uid);
// 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).
@@ -491,7 +483,7 @@ namespace Robust.Shared.GameObjects
{
try
{
xformSys.DetachParentToNull(uid, transform);
_xforms.DetachParentToNull(uid, transform);
}
catch (Exception e)
{
@@ -503,7 +495,7 @@ namespace Robust.Shared.GameObjects
{
try
{
RecursiveDeleteEntity(child, metaQuery.GetComponent(child), metaQuery, xformQuery, xformSys);
RecursiveDeleteEntity(child, MetaQuery.GetComponent(child));
}
catch(Exception e)
{
@@ -536,7 +528,7 @@ namespace Robust.Shared.GameObjects
try
{
EntityDeleted?.Invoke(uid);
EntityDeleted?.Invoke(uid, metadata);
}
catch (Exception e)
{
@@ -545,6 +537,8 @@ namespace Robust.Shared.GameObjects
_eventBus.OnEntityDeleted(uid);
Entities.Remove(uid);
// Need to get the ID above before MetadataComponent shutdown but only remove it after everything else is done.
NetEntityLookup.Remove(netEntity);
}
public virtual void QueueDeleteEntity(EntityUid uid)
@@ -574,7 +568,7 @@ namespace Robust.Shared.GameObjects
if (uid == null)
return false;
return _metaQuery.Resolve(uid.Value, ref metadata) && metadata.EntityPaused;
return MetaQuery.Resolve(uid.Value, ref metadata) && metadata.EntityPaused;
}
public bool Deleted(EntityUid uid)
@@ -608,10 +602,9 @@ namespace Robust.Shared.GameObjects
/// </summary>
private protected EntityUid AllocEntity(
EntityPrototype? prototype,
out MetaDataComponent metadata,
EntityUid uid = default)
out MetaDataComponent metadata)
{
var entity = AllocEntity(out metadata, uid);
var entity = AllocEntity(out metadata);
metadata._entityPrototype = prototype;
Dirty(entity, metadata, metadata);
return entity;
@@ -620,25 +613,30 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Allocates an entity and stores it but does not load components or do initialization.
/// </summary>
private EntityUid AllocEntity(out MetaDataComponent metadata, EntityUid uid = default)
private EntityUid AllocEntity(out MetaDataComponent metadata)
{
if (uid == default)
{
uid = GenerateEntityUid();
}
var uid = GenerateEntityUid();
#if DEBUG
if (EntityExists(uid))
{
throw new InvalidOperationException($"UID already taken: {uid}");
}
#endif
// we want this called before adding components
EntityAdded?.Invoke(uid);
_eventBus.OnEntityAdded(uid);
var netEntity = GenerateNetEntity();
metadata = new MetaDataComponent
{
#pragma warning disable CS0618
metadata = new MetaDataComponent { Owner = uid };
Owner = uid,
#pragma warning restore CS0618
};
SetNetEntity(uid, netEntity, metadata);
Entities.Add(uid);
// add the required MetaDataComponent directly.
@@ -653,23 +651,23 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Allocates an entity and loads components but does not do initialization.
/// </summary>
private protected virtual EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
private protected virtual EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
{
if (prototypeName == null)
return AllocEntity(out _, uid);
return AllocEntity(out _);
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
return CreateEntity(prototype, uid, context);
return CreateEntity(prototype, context);
}
/// <summary>
/// Allocates an entity and loads components but does not do initialization.
/// </summary>
private protected EntityUid CreateEntity(EntityPrototype prototype, EntityUid uid = default, IEntityLoadContext? context = null)
private protected EntityUid CreateEntity(EntityPrototype prototype, IEntityLoadContext? context = null)
{
var entity = AllocEntity(prototype, out var metadata, uid);
var entity = AllocEntity(prototype, out var metadata);
try
{
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);
@@ -686,7 +684,7 @@ namespace Robust.Shared.GameObjects
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context)
{
EntityPrototype.LoadEntity(_metaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
EntityPrototype.LoadEntity(MetaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
}
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context, EntityPrototype? prototype)
@@ -698,7 +696,7 @@ namespace Robust.Shared.GameObjects
{
try
{
var meta = _metaQuery.GetComponent(entity);
var meta = MetaQuery.GetComponent(entity);
InitializeEntity(entity, meta);
StartEntity(entity);
@@ -747,6 +745,12 @@ namespace Robust.Shared.GameObjects
return ToPrettyString(uid, metadata);
}
/// <inheritdoc />
public EntityStringRepresentation ToPrettyString(NetEntity netEntity)
{
return ToPrettyString(GetEntity(netEntity));
}
private EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent metadata)
{
return new EntityStringRepresentation(uid, metadata.EntityDeleted, metadata.EntityName, metadata.EntityPrototype?.ID);
@@ -764,12 +768,16 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Factory for generating a new EntityUid for an entity currently being created.
/// </summary>
/// <inheritdoc />
protected virtual EntityUid GenerateEntityUid()
internal EntityUid GenerateEntityUid()
{
return new(NextEntityUid++);
return new EntityUid(NextEntityUid++);
}
/// <summary>
/// Generates a unique network id and increments <see cref="NextNetworkId"/>
/// </summary>
protected virtual NetEntity GenerateNetEntity() => new(NextNetworkId++);
private sealed class EntityDiffContext : ISerializationContext
{
public SerializationManager.SerializerProvider SerializerProvider { get; }

View File

@@ -9,7 +9,10 @@ namespace Robust.Shared.GameObjects
[Serializable, NetSerializable]
public sealed class EntityState
{
public EntityUid Uid { get; }
/// <summary>
/// Network identifier for the entity.
/// </summary>
public NetEntity NetEntity;
public NetListAsArray<ComponentChange> ComponentChanges { get; }
@@ -23,9 +26,9 @@ namespace Robust.Shared.GameObjects
/// </summary>
public HashSet<ushort>? NetComponents;
public EntityState(EntityUid uid, NetListAsArray<ComponentChange> changedComponents, GameTick lastModified, HashSet<ushort>? netComps = null)
public EntityState(NetEntity netEntity, NetListAsArray<ComponentChange> changedComponents, GameTick lastModified, HashSet<ushort>? netComps = null)
{
Uid = uid;
NetEntity = netEntity;
ComponentChanges = changedComponents;
EntityLastModified = lastModified;
NetComponents = netComps;

View File

@@ -403,6 +403,13 @@ public partial class EntitySystem
return EntityManager.ToPrettyString(uid);
}
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityStringRepresentation ToPrettyString(NetEntity netEntity)
{
return EntityManager.ToPrettyString(netEntity);
}
#endregion
#region Component Get
@@ -886,4 +893,344 @@ public partial class EntitySystem
}
#endregion
#region NetEntities
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected bool IsClientSide(EntityUid entity)
{
return EntityManager.IsClientSide(entity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEntity(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity)
{
return EntityManager.TryGetEntity(nEntity, out entity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity)
{
return EntityManager.TryGetEntity(nEntity, out entity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetNetEntity(EntityUid uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
{
return EntityManager.TryGetNetEntity(uid, out netEntity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetNetEntity(EntityUid? uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
{
return EntityManager.TryGetNetEntity(uid, out netEntity);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> of an entity. Returns <see cref="NetEntity.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected NetEntity GetNetEntity(EntityUid uid, MetaDataComponent? metadata = null)
{
return EntityManager.GetNetEntity(uid, metadata);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> of an entity. Logs an error if the entity does not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected NetEntity? GetNetEntity(EntityUid? uid, MetaDataComponent? metadata = null)
{
return EntityManager.GetNetEntity(uid, metadata);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> of an entity or creates a new entity if none exists.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid EnsureEntity<T>(NetEntity netEntity, EntityUid callerEntity)
{
return EntityManager.EnsureEntity<T>(netEntity, callerEntity);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> of an entity or creates a new one if not null.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid? EnsureEntity<T>(NetEntity? netEntity, EntityUid callerEntity)
{
return EntityManager.EnsureEntity<T>(netEntity, callerEntity);
}
/// <summary>
/// Returns the <see cref="NetCoordinates"/> of an entity or creates a new entity if none exists.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
{
return EntityManager.EnsureCoordinates<T>(netCoordinates, callerEntity);
}
/// <summary>
/// Returns the <see cref="NetCoordinates"/> of an entity or creates a new one if not null.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityCoordinates? EnsureCoordinates<T>(NetCoordinates? netCoordinates, EntityUid callerEntity)
{
return EntityManager.EnsureCoordinates<T>(netCoordinates, callerEntity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HashSet<EntityUid> EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity)
{
return EntityManager.EnsureEntitySet<T>(netEntities, callerEntity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity)
{
return EntityManager.EnsureEntityList<T>(netEntities, callerEntity);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid GetEntity(NetEntity netEntity)
{
return EntityManager.GetEntity(netEntity);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid? GetEntity(NetEntity? netEntity)
{
return EntityManager.GetEntity(netEntity);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected HashSet<NetEntity> GetNetEntitySet(HashSet<EntityUid> uids)
{
return EntityManager.GetNetEntitySet(uids);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities)
{
return EntityManager.GetEntitySet(netEntities);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<NetEntity> GetNetEntityList(ICollection<EntityUid> uids)
{
return EntityManager.GetNetEntityList(uids);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<NetEntity> GetNetEntityList(IReadOnlyList<EntityUid> uids)
{
return EntityManager.GetNetEntityList(uids);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities)
{
return EntityManager.GetEntityList(netEntities);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<NetEntity> GetNetEntityList(List<EntityUid> uids)
{
return EntityManager.GetNetEntityList(uids);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<EntityUid> GetEntityList(List<NetEntity> netEntities)
{
return EntityManager.GetEntityList(netEntities);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<NetEntity?> GetNetEntityList(List<EntityUid?> uids)
{
return EntityManager.GetNetEntityList(uids);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected List<EntityUid?> GetEntityList(List<NetEntity?> netEntities)
{
return EntityManager.GetEntityList(netEntities);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected NetEntity[] GetNetEntityArray(EntityUid[] uids)
{
return EntityManager.GetNetEntityArray(uids);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid[] GetEntityArray(NetEntity[] netEntities)
{
return EntityManager.GetEntityArray(netEntities);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected NetEntity?[] GetNetEntityArray(EntityUid?[] uids)
{
return EntityManager.GetNetEntityArray(uids);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityUid?[] GetEntityArray(NetEntity?[] netEntities)
{
return EntityManager.GetEntityArray(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected NetCoordinates GetNetCoordinates(EntityCoordinates coordinates, MetaDataComponent? metadata = null)
{
return EntityManager.GetNetCoordinates(coordinates, metadata);
}
/// <summary>
/// Returns the <see cref="NetEntity"/> of an entity. Returns <see cref="NetEntity.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected NetCoordinates? GetNetCoordinates(EntityCoordinates? coordinates, MetaDataComponent? metadata = null)
{
return EntityManager.GetNetCoordinates(coordinates, metadata);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityCoordinates GetCoordinates(NetCoordinates netEntity)
{
return EntityManager.GetCoordinates(netEntity);
}
/// <summary>
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
protected EntityCoordinates? GetCoordinates(NetCoordinates? netEntity)
{
return EntityManager.GetCoordinates(netEntity);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HashSet<EntityCoordinates> GetEntitySet(HashSet<NetCoordinates> netEntities)
{
return EntityManager.GetEntitySet(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<EntityCoordinates> GetEntityList(List<NetCoordinates> netEntities)
{
return EntityManager.GetEntityList(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<EntityCoordinates> GetEntityList(ICollection<NetCoordinates> netEntities)
{
return EntityManager.GetEntityList(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<EntityCoordinates?> GetEntityList(List<NetCoordinates?> netEntities)
{
return EntityManager.GetEntityList(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityCoordinates[] GetEntityArray(NetCoordinates[] netEntities)
{
return EntityManager.GetEntityArray(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityCoordinates?[] GetEntityArray(NetCoordinates?[] netEntities)
{
return EntityManager.GetEntityArray(netEntities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HashSet<NetCoordinates> GetNetCoordinatesSet(HashSet<EntityCoordinates> entities)
{
return EntityManager.GetNetCoordinatesSet(entities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<NetCoordinates> GetNetCoordinatesList(List<EntityCoordinates> entities)
{
return EntityManager.GetNetCoordinatesList(entities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<NetCoordinates> GetNetCoordinatesList(ICollection<EntityCoordinates> entities)
{
return EntityManager.GetNetCoordinatesList(entities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public List<NetCoordinates?> GetNetCoordinatesList(List<EntityCoordinates?> entities)
{
return EntityManager.GetNetCoordinatesList(entities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetCoordinates[] GetNetCoordinatesArray(EntityCoordinates[] entities)
{
return EntityManager.GetNetCoordinatesArray(entities);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public NetCoordinates?[] GetNetCoordinatesArray(EntityCoordinates?[] entities)
{
return EntityManager.GetNetCoordinatesArray(entities);
}
#endregion
}

View File

@@ -31,14 +31,14 @@ public sealed class PlayAudioGlobalMessage : AudioMessage
[Serializable, NetSerializable]
public sealed class PlayAudioPositionalMessage : AudioMessage
{
public EntityCoordinates Coordinates { get; set; }
public EntityCoordinates FallbackCoordinates { get; set; }
public NetCoordinates Coordinates { get; set; }
public NetCoordinates FallbackCoordinates { get; set; }
}
[Serializable, NetSerializable]
public sealed class PlayAudioEntityMessage : AudioMessage
{
public EntityUid EntityUid { get; set; }
public EntityCoordinates Coordinates { get; set; }
public EntityCoordinates FallbackCoordinates { get; set; }
public NetEntity NetEntity { get; set; }
public NetCoordinates Coordinates { get; set; }
public NetCoordinates FallbackCoordinates { get; set; }
}

View File

@@ -2,7 +2,6 @@ using System;
using JetBrains.Annotations;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -14,15 +13,10 @@ namespace Robust.Shared.GameObjects
/// This type contains a network identification number of an entity.
/// This can be used by the EntityManager to access an entity
/// </summary>
[Serializable, NetSerializable, CopyByRef]
[CopyByRef]
public readonly struct EntityUid : IEquatable<EntityUid>, IComparable<EntityUid>, ISpanFormattable
{
/// <summary>
/// If this bit is set on a UID, it's client sided.
/// Use <see cref="IsClientSide" /> to check this.
/// </summary>
internal const int ClientUid = 2 << 29;
readonly int _uid;
public readonly int Id;
/// <summary>
/// An Invalid entity UID you can compare against.
@@ -37,9 +31,9 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Creates an instance of this structure, with the given network ID.
/// </summary>
public EntityUid(int uid)
public EntityUid(int id)
{
_uid = uid;
Id = id;
}
public bool Valid => IsValid();
@@ -49,14 +43,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
public static EntityUid Parse(ReadOnlySpan<char> uid)
{
if (uid.StartsWith("c"))
{
return new EntityUid(int.Parse(uid[1..]) | ClientUid);
}
else
{
return new EntityUid(int.Parse(uid));
}
return new EntityUid(int.Parse(uid));
}
public static bool TryParse(ReadOnlySpan<char> uid, out EntityUid entityUid)
@@ -80,19 +67,13 @@ namespace Robust.Shared.GameObjects
[Pure]
public bool IsValid()
{
return _uid > 0;
}
[Pure]
public bool IsClientSide()
{
return (_uid & (2 << 29)) != 0;
return Id > 0;
}
/// <inheritdoc />
public bool Equals(EntityUid other)
{
return _uid == other._uid;
return Id == other.Id;
}
/// <inheritdoc />
@@ -105,7 +86,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override int GetHashCode()
{
return _uid;
return Id;
}
/// <summary>
@@ -113,7 +94,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
public static bool operator ==(EntityUid a, EntityUid b)
{
return a._uid == b._uid;
return a.Id == b.Id;
}
/// <summary>
@@ -130,17 +111,13 @@ namespace Robust.Shared.GameObjects
/// </summary>
public static explicit operator int(EntityUid self)
{
return self._uid;
return self.Id;
}
/// <inheritdoc />
public override string ToString()
{
if (IsClientSide())
{
return $"c{_uid & ~ClientUid}";
}
return _uid.ToString();
return Id.ToString();
}
public string ToString(string? format, IFormatProvider? formatProvider)
@@ -154,21 +131,13 @@ namespace Robust.Shared.GameObjects
ReadOnlySpan<char> format,
IFormatProvider? provider)
{
if (IsClientSide())
{
return FormatHelpers.TryFormatInto(
destination,
out charsWritten,
$"c{_uid & ~ClientUid}");
}
return _uid.TryFormat(destination, out charsWritten);
return Id.TryFormat(destination, out charsWritten);
}
/// <inheritdoc />
public int CompareTo(EntityUid other)
{
return _uid.CompareTo(other._uid);
return Id.CompareTo(other.Id);
}
#region ViewVariables

View File

@@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Map;
namespace Robust.Shared.GameObjects;
public partial interface IEntityManager
{
/// <summary>
/// Tries to parse a string as a NetEntity and return the relevant EntityUid.
/// </summary>
public bool TryParseNetEntity(string arg, [NotNullWhen(true)] out EntityUid? entity);
/// <summary>
/// TryGet version of <see cref="GetEntity"/>
/// </summary>
public bool TryGetEntity(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity);
/// <summary>
/// TryGet version of <see cref="GetEntity"/>
/// </summary>
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity);
/// <summary>
/// TryGet version of <see cref="GetNetEntity"/>
/// </summary>
public bool TryGetNetEntity(EntityUid uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null);
/// <summary>
/// TryGet version of <see cref="GetNetEntity"/>
/// </summary>
public bool TryGetNetEntity(EntityUid? uid, [NotNullWhen(true)] out NetEntity? netEntity,
MetaDataComponent? metadata = null);
/// <summary>
/// Returns true if the entity only exists on the client.
/// </summary>
public bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null);
/// <summary>
/// Tries to get a corresponding <see cref="EntityUid"/> if it exists, otherwise creates an entity for it.
/// </summary>
/// <param name="nEntity">The net entity we're trying to resolve.</param>
/// <param name="T">The type of the component that may need its state handling run later.</param>
/// <param name="callerEntity">The entity trying to resolve the net entity. This may be flagged for later component state handling.</param>
public EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity);
/// <summary>
/// Tries to get a corresponding <see cref="EntityUid"/> if it exists and nEntity is not null.
/// </summary>
public EntityUid? EnsureEntity<T>(NetEntity? nEntity, EntityUid callerEntity);
/// <summary>
/// Returns the corresponding local <see cref="EntityUid"/>.
/// </summary>
public EntityUid GetEntity(NetEntity nEntity);
/// <summary>
/// Returns the corresponding local <see cref="EntityUid"/>.
/// </summary>
public EntityUid? GetEntity(NetEntity? nEntity);
/// <summary>
/// Returns the corresponding <see cref="NetEntity"/> for the local entity.
/// </summary>
public NetEntity GetNetEntity(EntityUid uid, MetaDataComponent? metadata = null);
/// <summary>
/// Returns the corresponding <see cref="NetEntity"/> for the local entity.
/// </summary>
public NetEntity? GetNetEntity(EntityUid? uid, MetaDataComponent? metadata = null);
/// <summary>
/// HashSet version of <see cref="GetEntity"/>
/// </summary>
public HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities);
/// <summary>
/// List version of <see cref="GetEntity"/>
/// </summary>
public List<EntityUid> GetEntityList(List<NetEntity> netEntities);
/// <summary>
/// List version of <see cref="GetEntity"/>
/// </summary>
public List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities);
/// <summary>
/// List version of <see cref="GetEntity"/>
/// </summary>
public List<EntityUid?> GetEntityList(List<NetEntity?> netEntities);
/// <summary>
/// List version of <see cref="GetEntity"/>
/// </summary>
EntityUid[] GetEntityArray(NetEntity[] netEntities);
/// <summary>
/// List version of <see cref="GetEntity"/>
/// </summary>
EntityUid?[] GetEntityArray(NetEntity?[] netEntities);
/// <summary>
/// HashSet version of <see cref="GetNetEntity"/>
/// </summary>
public HashSet<NetEntity> GetNetEntitySet(HashSet<EntityUid> entities);
/// <summary>
/// List version of <see cref="GetNetEntity"/>
/// </summary>
public List<NetEntity> GetNetEntityList(List<EntityUid> entities);
/// <summary>
/// List version of <see cref="GetNetEntity"/>
/// </summary>
public List<NetEntity> GetNetEntityList(ICollection<EntityUid> entities);
/// <summary>
/// List version of <see cref="GetNetEntity"/>
/// </summary>
public List<NetEntity?> GetNetEntityList(List<EntityUid?> entities);
/// <summary>
/// List version of <see cref="GetNetEntity"/>
/// </summary>
NetEntity[] GetNetEntityArray(EntityUid[] entities);
/// <summary>
/// List version of <see cref="GetNetEntity"/>
/// </summary>
NetEntity?[] GetNetEntityArray(EntityUid?[] entities);
/// <summary>
/// Returns the corresponding <see cref="NetCoordinates"/> for the specified local coordinates.
/// </summary>
public NetCoordinates GetNetCoordinates(EntityCoordinates coordinates, MetaDataComponent? metadata = null);
/// <summary>
/// Returns the corresponding <see cref="NetCoordinates"/> for the specified local coordinates.
/// </summary>
public NetCoordinates? GetNetCoordinates(EntityCoordinates? coordinates, MetaDataComponent? metadata = null);
/// <summary>
/// Returns the corresponding <see cref="EntityCoordinates"/> for the specified network coordinates.
/// </summary>
public EntityCoordinates GetCoordinates(NetCoordinates coordinates);
/// <summary>
/// Returns the corresponding <see cref="EntityCoordinates"/> for the specified network coordinates.
/// </summary>
public EntityCoordinates? GetCoordinates(NetCoordinates? coordinates);
/// <summary>
/// Tries to get a corresponding <see cref="EntityCoordinates"/> if it exists, otherwise creates an entity for it.
/// </summary>
/// <param name="netCoordinates">The net coordinates we're trying to resolve.</param>
/// <param name="T">The type of the component that may need its state handling run later.</param>
/// <param name="callerEntity">The entity trying to resolve the net entity. This may be flagged for later component state handling.</param>
public EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity);
/// <summary>
/// Tries to get a corresponding <see cref="EntityCoordinates"/> if it exists and nEntity is not null.
/// </summary>
public EntityCoordinates? EnsureCoordinates<T>(NetCoordinates? netCoordinates, EntityUid callerEntity);
public HashSet<EntityCoordinates> GetEntitySet(HashSet<NetCoordinates> netEntities);
public List<EntityCoordinates> GetEntityList(List<NetCoordinates> netEntities);
public HashSet<EntityUid> EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity);
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity);
public List<EntityCoordinates> GetEntityList(ICollection<NetCoordinates> netEntities);
public List<EntityCoordinates?> GetEntityList(List<NetCoordinates?> netEntities);
public EntityCoordinates[] GetEntityArray(NetCoordinates[] netEntities);
public EntityCoordinates?[] GetEntityArray(NetCoordinates?[] netEntities);
public HashSet<NetCoordinates> GetNetCoordinatesSet(HashSet<EntityCoordinates> entities);
public List<NetCoordinates> GetNetCoordinatesList(List<EntityCoordinates> entities);
public List<NetCoordinates> GetNetCoordinatesList(ICollection<EntityCoordinates> entities);
public List<NetCoordinates?> GetNetCoordinatesList(List<EntityCoordinates?> entities);
public NetCoordinates[] GetNetCoordinatesArray(EntityCoordinates[] entities);
public NetCoordinates?[] GetNetCoordinatesArray(EntityCoordinates?[] entities);
}

View File

@@ -53,7 +53,7 @@ namespace Robust.Shared.GameObjects
event Action<EntityUid>? EntityAdded;
event Action<EntityUid>? EntityInitialized;
event Action<EntityUid>? EntityDeleted;
event Action<EntityUid, MetaDataComponent>? EntityDeleted;
event Action<EntityUid>? EntityDirtied; // only raised after initialization
EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null);

View File

@@ -0,0 +1,226 @@
using System;
using JetBrains.Annotations;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects;
/// <summary>
/// Network identifier for entities; used by client and server to refer to the same entity where their local <see cref="EntityUid"/> may differ.
/// </summary>
[Serializable, NetSerializable]
public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>, ISpanFormattable
{
public readonly int Id;
public const int ClientEntity = 2 << 29;
/*
* Differed to EntityUid to be more consistent with Arch.
*/
/// <summary>
/// An Invalid entity UID you can compare against.
/// </summary>
public static readonly NetEntity Invalid = new(0);
/// <summary>
/// The first entity UID the entityManager should use when the manager is initialized.
/// </summary>
public static readonly NetEntity First = new(1);
/// <summary>
/// Creates an instance of this structure, with the given network ID.
/// </summary>
public NetEntity(int id)
{
Id = id;
}
public bool Valid => IsValid();
/// <summary>
/// Creates a network entity UID by parsing a string number.
/// </summary>
public static NetEntity Parse(ReadOnlySpan<char> uid)
{
return new NetEntity(int.Parse(uid));
}
public static bool TryParse(ReadOnlySpan<char> uid, out NetEntity entity)
{
try
{
entity = Parse(uid);
return true;
}
catch (FormatException)
{
entity = Invalid;
return false;
}
}
/// <summary>
/// Checks if the ID value is valid. Does not check if it identifies
/// a valid Entity.
/// </summary>
[Pure]
public bool IsValid()
{
return Id > 0;
}
/// <inheritdoc />
public bool Equals(NetEntity other)
{
return Id == other.Id;
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is EntityUid id && Equals(id);
}
/// <inheritdoc />
public override int GetHashCode()
{
return Id;
}
/// <summary>
/// Check for equality by value between two objects.
/// </summary>
public static bool operator ==(NetEntity a, NetEntity b)
{
return a.Id == b.Id;
}
/// <summary>
/// Check for inequality by value between two objects.
/// </summary>
public static bool operator !=(NetEntity a, NetEntity b)
{
return !(a == b);
}
/// <summary>
/// Explicit conversion of EntityId to int. This should only be used in special
/// cases like serialization. Do NOT use this in content.
/// </summary>
public static explicit operator int(NetEntity self)
{
return self.Id;
}
/// <inheritdoc />
public override string ToString()
{
return Id.ToString();
}
public string ToString(string? format, IFormatProvider? formatProvider)
{
return ToString();
}
public bool TryFormat(
Span<char> destination,
out int charsWritten,
ReadOnlySpan<char> format,
IFormatProvider? provider)
{
return Id.TryFormat(destination, out charsWritten);
}
/// <inheritdoc />
public int CompareTo(NetEntity other)
{
return Id.CompareTo(other.Id);
}
public bool IsClientSide() => (Id & ClientEntity) == ClientEntity;
#region ViewVariables
[ViewVariables]
private string Representation
{
get
{
var entManager = IoCManager.Resolve<IEntityManager>();
return entManager.ToPrettyString(entManager.GetEntity(this));
}
}
[ViewVariables(VVAccess.ReadWrite)]
private string Name
{
get => MetaData?.EntityName ?? string.Empty;
set
{
if (MetaData is {} metaData)
metaData.EntityName = value;
}
}
[ViewVariables(VVAccess.ReadWrite)]
private string Description
{
get => MetaData?.EntityDescription ?? string.Empty;
set
{
if (MetaData is {} metaData)
metaData.EntityDescription = value;
}
}
[ViewVariables]
private EntityPrototype? Prototype => MetaData?.EntityPrototype;
[ViewVariables]
private GameTick LastModifiedTick => MetaData?.EntityLastModifiedTick ?? GameTick.Zero;
[ViewVariables]
private bool Paused => MetaData?.EntityPaused ?? false;
[ViewVariables]
private EntityLifeStage LifeStage => MetaData?.EntityLifeStage ?? EntityLifeStage.Deleted;
[ViewVariables]
private MetaDataComponent? MetaData
{
get
{
var entManager = IoCManager.Resolve<IEntityManager>();
return entManager.GetComponentOrNull<MetaDataComponent>(entManager.GetEntity(this));
}
}
[ViewVariables]
private TransformComponent? Transform
{
get
{
var entManager = IoCManager.Resolve<IEntityManager>();
return entManager.GetComponentOrNull<TransformComponent>(entManager.GetEntity(this));
}
}
[ViewVariables]
private EntityUid Uid
{
get
{
return IoCManager.Resolve<IEntityManager>().GetEntity(this);
}
}
#endregion
}

View File

@@ -230,7 +230,7 @@ namespace Robust.Shared.GameObjects
[Serializable, NetSerializable]
public sealed class ChunkSplitDebugMessage : EntityEventArgs
{
public EntityUid Grid;
public NetEntity Grid;
public Dictionary<Vector2i, List<List<Vector2i>>> Nodes = new ();
public List<(Vector2 Start, Vector2 End)> Connections = new();
}

View File

@@ -480,7 +480,7 @@ public abstract partial class SharedTransformSystem
if (value.EntityId == uid)
{
DetachParentToNull(uid, xform);
if (_netMan.IsServer || uid.IsClientSide())
if (_netMan.IsServer || IsClientSide(uid))
QueueDel(uid);
throw new InvalidOperationException($"Attempted to parent an entity to itself: {ToPrettyString(uid)}");
}
@@ -490,7 +490,7 @@ public abstract partial class SharedTransformSystem
if (!_xformQuery.Resolve(value.EntityId, ref newParent, false))
{
DetachParentToNull(uid, xform);
if (_netMan.IsServer || uid.IsClientSide())
if (_netMan.IsServer || IsClientSide(uid))
QueueDel(uid);
throw new InvalidOperationException($"Attempted to parent entity {ToPrettyString(uid)} to non-existent entity {value.EntityId}");
}
@@ -498,7 +498,7 @@ public abstract partial class SharedTransformSystem
if (newParent.LifeStage > ComponentLifeStage.Running || LifeStage(value.EntityId) > EntityLifeStage.MapInitialized)
{
DetachParentToNull(uid, xform);
if (_netMan.IsServer || uid.IsClientSide())
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)}");
}
@@ -625,7 +625,7 @@ public abstract partial class SharedTransformSystem
public TransformComponent? GetParent(TransformComponent xform)
{
if (!xform.ParentUid.IsValid())
if (!xform.ParentUid.IsValid())
return null;
return _xformQuery.GetComponent(xform.ParentUid);
}
@@ -678,10 +678,12 @@ public abstract partial class SharedTransformSystem
internal void OnGetState(EntityUid uid, TransformComponent component, ref ComponentGetState args)
{
DebugTools.Assert(!component.ParentUid.IsValid() || (!Deleted(component.ParentUid) && !EntityManager.IsQueuedForDeletion(component.ParentUid)));
var parent = GetNetEntity(component.ParentUid);
args.State = new TransformComponentState(
component.LocalPosition,
component.LocalRotation,
component.ParentUid,
parent,
component.NoLocalRotation,
component.Anchored);
}
@@ -690,13 +692,16 @@ public abstract partial class SharedTransformSystem
{
if (args.Current is TransformComponentState newState)
{
var newParentId = newState.ParentID;
var parent = GetEntity(newState.ParentID);
if (!parent.IsValid() && newState.ParentID.IsValid())
Log.Error($"Received transform component state with an unknown parent Id. Entity: {ToPrettyString(uid)}. Net parent: {newState.ParentID}");
var oldAnchored = xform.Anchored;
// update actual position data, if required
if (!xform.LocalPosition.EqualsApprox(newState.LocalPosition)
|| !xform.LocalRotation.EqualsApprox(newState.Rotation)
|| xform.ParentUid != newParentId)
|| xform.ParentUid != parent)
{
// remove from any old grid lookups
if (xform.Anchored && TryComp(xform.ParentUid, out MapGridComponent? grid))
@@ -709,7 +714,7 @@ public abstract partial class SharedTransformSystem
xform._anchored |= newState.Anchored;
// Update the action position, rotation, and parent (and hence also map, grid, etc).
SetCoordinates(uid, xform, new EntityCoordinates(newParentId, newState.LocalPosition), newState.Rotation, unanchor: false);
SetCoordinates(uid, xform, new EntityCoordinates(parent, newState.LocalPosition), newState.Rotation, unanchor: false);
xform._anchored = newState.Anchored;
@@ -744,7 +749,7 @@ public abstract partial class SharedTransformSystem
xform.PrevRotation = newState.Rotation;
xform._noLocalRotation = newState.NoLocalRotation;
DebugTools.Assert(xform.ParentUid == newState.ParentID, "Transform state failed to set parent");
DebugTools.Assert(xform.ParentUid == parent, "Transform state failed to set parent");
DebugTools.Assert(xform.Anchored == newState.Anchored, "Transform state failed to set anchored");
}
@@ -752,7 +757,7 @@ public abstract partial class SharedTransformSystem
{
xform.NextPosition = nextTransform.LocalPosition;
xform.NextRotation = nextTransform.Rotation;
xform.LerpParent = nextTransform.ParentID;
xform.LerpParent = GetEntity(nextTransform.ParentID);
ActivateLerp(xform);
}
else
@@ -1375,12 +1380,12 @@ public abstract partial class SharedTransformSystem
/// Attempts to place one entity next to another entity. If the target entity is in a container, this will attempt
/// to insert that entity into the same container.
/// </summary>
public void PlaceNextToOrDrop(EntityUid uid, EntityUid target,
public void PlaceNextToOrDrop(EntityUid uid, EntityUid target,
TransformComponent? xform = null, TransformComponent? targetXform = null)
{
if (!_xformQuery.Resolve(target, ref targetXform))
return;
if (!_xformQuery.Resolve(uid, ref xform))
return;

View File

@@ -281,7 +281,7 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Current parent entity of this entity.
/// </summary>
public readonly EntityUid ParentID;
public readonly NetEntity ParentID;
/// <summary>
/// Current position offset of the entity.
@@ -310,7 +310,7 @@ namespace Robust.Shared.GameObjects
/// <param name="rotation">Current direction offset of this entity.</param>
/// <param name="parentId">Current parent transform of this entity.</param>
/// <param name="noLocalRotation"></param>
public TransformComponentState(Vector2 localPosition, Angle rotation, EntityUid parentId, bool noLocalRotation, bool anchored)
public TransformComponentState(Vector2 localPosition, Angle rotation, NetEntity parentId, bool noLocalRotation, bool anchored)
{
LocalPosition = localPosition;
Rotation = rotation;

View File

@@ -26,7 +26,7 @@ namespace Robust.Shared.GameStates
uint lastInput,
NetListAsArray<EntityState> entities,
NetListAsArray<PlayerState> players,
NetListAsArray<EntityUid> deletions)
NetListAsArray<NetEntity> deletions)
{
FromSequence = fromSequence;
ToSequence = toSequence;
@@ -43,6 +43,6 @@ namespace Robust.Shared.GameStates
public readonly NetListAsArray<EntityState> EntityStates;
public readonly NetListAsArray<PlayerState> PlayerStates;
public readonly NetListAsArray<EntityUid> EntityDeletions;
public readonly NetListAsArray<NetEntity> EntityDeletions;
}
}

View File

@@ -17,7 +17,7 @@ namespace Robust.Shared.GameStates
public SessionStatus Status { get; set; }
public short Ping { get; set; }
public EntityUid? ControlledEntity { get; set; }
public NetEntity? ControlledEntity { get; set; }
public PlayerState Clone()
{

View File

@@ -1,4 +1,5 @@
using Robust.Shared.GameObjects;
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Players;
@@ -18,7 +19,7 @@ namespace Robust.Shared.Input.Binding
{
}
public abstract bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message);
public abstract bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message);
/// <summary>
/// Makes a quick input command from enabled and disabled delegates.
@@ -56,12 +57,9 @@ namespace Robust.Shared.Input.Binding
DisabledDelegate?.Invoke(session);
}
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
{
if (!(message is FullInputCmdMessage msg))
return false;
switch (msg.State)
switch (message.State)
{
case BoundKeyState.Up:
Disabled(session);
@@ -111,14 +109,28 @@ namespace Robust.Shared.Input.Binding
FireOutsidePrediction = outsidePrediction;
}
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
{
if (!(message is FullInputCmdMessage msg) || (_ignoreUp && msg.State != BoundKeyState.Down))
if ((_ignoreUp && message.State != BoundKeyState.Down))
return false;
var handled = _callback?.Invoke(new PointerInputCmdArgs(session, msg.Coordinates,
msg.ScreenCoordinates, msg.Uid, msg.State, msg));
return handled.HasValue && handled.Value;
switch (message)
{
case ClientFullInputCmdMessage clientInput:
{
var handled = _callback?.Invoke(new PointerInputCmdArgs(session, clientInput.Coordinates,
clientInput.ScreenCoordinates, clientInput.Uid, message.State, message));
return handled.HasValue && handled.Value;
}
case FullInputCmdMessage fullInput:
{
var handled = _callback?.Invoke(new PointerInputCmdArgs(session, entManager.GetCoordinates(fullInput.Coordinates),
fullInput.ScreenCoordinates, entManager.GetEntity(fullInput.Uid), fullInput.State, message));
return handled.HasValue && handled.Value;
}
default:
throw new ArgumentOutOfRangeException();
}
}
public readonly struct PointerInputCmdArgs
@@ -128,11 +140,11 @@ namespace Robust.Shared.Input.Binding
public readonly ScreenCoordinates ScreenCoordinates;
public readonly EntityUid EntityUid;
public readonly BoundKeyState State;
public readonly FullInputCmdMessage OriginalMessage;
public readonly IFullInputCmdMessage OriginalMessage;
public PointerInputCmdArgs(ICommonSession? session, EntityCoordinates coordinates,
ScreenCoordinates screenCoordinates, EntityUid entityUid, BoundKeyState state,
FullInputCmdMessage originalMessage)
IFullInputCmdMessage originalMessage)
{
Session = session;
Coordinates = coordinates;
@@ -158,17 +170,28 @@ namespace Robust.Shared.Input.Binding
}
/// <inheritdoc />
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
{
if (!(message is FullInputCmdMessage msg))
return false;
switch (msg.State)
switch (message)
{
case BoundKeyState.Up:
return _disabled?.Invoke(session, msg.Coordinates, msg.Uid) == true;
case BoundKeyState.Down:
return _enabled?.Invoke(session, msg.Coordinates, msg.Uid) == true;
case ClientFullInputCmdMessage clientInput:
switch (clientInput.State)
{
case BoundKeyState.Up:
return _disabled?.Invoke(session, clientInput.Coordinates, clientInput.Uid) == true;
case BoundKeyState.Down:
return _enabled?.Invoke(session, clientInput.Coordinates, clientInput.Uid) == true;
}
break;
case FullInputCmdMessage fullInput:
switch (fullInput.State)
{
case BoundKeyState.Up:
return _disabled?.Invoke(session, entManager.GetCoordinates(fullInput.Coordinates), entManager.GetEntity(fullInput.Uid)) == true;
case BoundKeyState.Down:
return _enabled?.Invoke(session, entManager.GetCoordinates(fullInput.Coordinates), entManager.GetEntity(fullInput.Uid)) == true;
}
break;
}
//Client Sanitization: unknown key state, just ignore
@@ -183,7 +206,7 @@ namespace Robust.Shared.Input.Binding
public sealed class NullInputCmdHandler : InputCmdHandler
{
/// <inheritdoc />
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
{
return true;
}

View File

@@ -113,12 +113,12 @@ namespace Robust.Shared.Input
/// <summary>
/// Local Coordinates of the pointer when the command was created.
/// </summary>
public EntityCoordinates Coordinates { get; }
public NetCoordinates Coordinates { get; }
/// <summary>
/// Entity that was under the pointer when the command was created (if any).
/// </summary>
public EntityUid Uid { get; }
public NetEntity Uid { get; }
/// <summary>
/// Creates an instance of <see cref="PointerInputCmdMessage"/>.
@@ -126,8 +126,8 @@ namespace Robust.Shared.Input
/// <param name="tick">Client tick this was created.</param>
/// <param name="inputFunctionId">Function this command is changing.</param>
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, EntityCoordinates coordinates)
: this(tick, subTick, inputFunctionId, coordinates, EntityUid.Invalid) { }
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, NetCoordinates coordinates)
: this(tick, subTick, inputFunctionId, coordinates, NetEntity.Invalid) { }
/// <summary>
/// Creates an instance of <see cref="PointerInputCmdMessage"/> with an optional Entity reference.
@@ -136,7 +136,7 @@ namespace Robust.Shared.Input
/// <param name="inputFunctionId">Function this command is changing.</param>
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
/// <param name="uid">Entity that was under the pointer when the command was created.</param>
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, EntityCoordinates coordinates, EntityUid uid)
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, NetCoordinates coordinates, NetEntity uid)
: base(tick, subTick, inputFunctionId)
{
Coordinates = coordinates;
@@ -144,11 +144,51 @@ namespace Robust.Shared.Input
}
}
/// <summary>
/// Handles inputs clientside. This is used so the client can still interact with client-only entities without relying on
/// <see cref="NetEntity"/>.
/// </summary>
public sealed class ClientFullInputCmdMessage : InputCmdMessage, IFullInputCmdMessage
{
/// <summary>
/// New state of the Input Function.
/// </summary>
public BoundKeyState State { get; init; }
/// <summary>
/// Local Coordinates of the pointer when the command was created.
/// </summary>
public EntityCoordinates Coordinates { get; init; }
/// <summary>
/// Screen Coordinates of the pointer when the command was created.
/// </summary>
public ScreenCoordinates ScreenCoordinates { get; init; }
/// <summary>
/// Entity that was under the pointer when the command was created (if any).
/// </summary>
public EntityUid Uid { get; init; }
public ClientFullInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId) : base(tick, subTick, inputFunctionId)
{
}
}
public interface IFullInputCmdMessage
{
GameTick Tick { get; }
BoundKeyState State { get; }
KeyFunctionId InputFunctionId { get; }
ushort SubTick { get; }
uint InputSequence { get; set; }
}
/// <summary>
/// An input command that has both state and pointer info.
/// </summary>
[Serializable, NetSerializable]
public sealed class FullInputCmdMessage : InputCmdMessage
public sealed class FullInputCmdMessage : InputCmdMessage, IFullInputCmdMessage
{
/// <summary>
/// New state of the Input Function.
@@ -158,7 +198,7 @@ namespace Robust.Shared.Input
/// <summary>
/// Local Coordinates of the pointer when the command was created.
/// </summary>
public EntityCoordinates Coordinates { get; }
public NetCoordinates Coordinates { get; }
/// <summary>
/// Screen Coordinates of the pointer when the command was created.
@@ -168,7 +208,7 @@ namespace Robust.Shared.Input
/// <summary>
/// Entity that was under the pointer when the command was created (if any).
/// </summary>
public EntityUid Uid { get; }
public NetEntity Uid { get; init; }
/// <summary>
/// Creates an instance of <see cref="FullInputCmdMessage"/>.
@@ -179,8 +219,8 @@ namespace Robust.Shared.Input
/// <param name="state">New state of the Input Function.</param>
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
/// <param name="screenCoordinates"></param>
public FullInputCmdMessage(GameTick tick, ushort subTick, int inputSequence, KeyFunctionId inputFunctionId, BoundKeyState state, EntityCoordinates coordinates, ScreenCoordinates screenCoordinates)
: this(tick, subTick, inputFunctionId, state, coordinates, screenCoordinates, EntityUid.Invalid) { }
public FullInputCmdMessage(GameTick tick, ushort subTick, int inputSequence, KeyFunctionId inputFunctionId, BoundKeyState state, NetCoordinates coordinates, ScreenCoordinates screenCoordinates)
: this(tick, subTick, inputFunctionId, state, coordinates, screenCoordinates, NetEntity.Invalid) { }
/// <summary>
/// Creates an instance of <see cref="FullInputCmdMessage"/> with an optional Entity reference.
@@ -191,7 +231,7 @@ namespace Robust.Shared.Input
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
/// <param name="screenCoordinates"></param>
/// <param name="uid">Entity that was under the pointer when the command was created.</param>
public FullInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, BoundKeyState state, EntityCoordinates coordinates, ScreenCoordinates screenCoordinates, EntityUid uid)
public FullInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, BoundKeyState state, NetCoordinates coordinates, ScreenCoordinates screenCoordinates, NetEntity uid)
: base(tick, subTick, inputFunctionId)
{
State = state;

View File

@@ -13,7 +13,6 @@ namespace Robust.Shared.Map
/// A set of coordinates relative to another entity.
/// </summary>
[PublicAPI]
[Serializable, NetSerializable]
public readonly struct EntityCoordinates : IEquatable<EntityCoordinates>, ISpanFormattable
{
public static readonly EntityCoordinates Invalid = new(EntityUid.Invalid, Vector2.Zero);

View File

@@ -0,0 +1,102 @@
using System;
using System.Numerics;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Robust.Shared.Map;
/// <summary>
/// A networked version of <see cref="EntityCoordinates"/>
/// </summary>
[PublicAPI]
[Serializable, NetSerializable]
public readonly struct NetCoordinates : IEquatable<NetCoordinates>, ISpanFormattable
{
public static readonly NetCoordinates Invalid = new(NetEntity.Invalid, Vector2.Zero);
/// <summary>
/// Networked ID of the entity that this position is relative to.
/// </summary>
public readonly NetEntity NetEntity;
/// <summary>
/// Position in the entity's local space.
/// </summary>
public readonly Vector2 Position;
/// <summary>
/// Location of the X axis local to the entity.
/// </summary>
public float X => Position.X;
/// <summary>
/// Location of the Y axis local to the entity.
/// </summary>
public float Y => Position.Y;
public NetCoordinates(NetEntity netEntity, Vector2 position)
{
NetEntity = netEntity;
Position = position;
}
public NetCoordinates(NetEntity netEntity, float x, float y)
{
NetEntity = netEntity;
Position = new Vector2(x, y);
}
#region IEquatable
/// <inheritdoc />
public bool Equals(NetCoordinates other)
{
return NetEntity.Equals(other.NetEntity) && Position.Equals(other.Position);
}
/// <inheritdoc />
public override bool Equals(object? obj)
{
return obj is NetCoordinates other && Equals(other);
}
/// <inheritdoc />
public override int GetHashCode()
{
return HashCode.Combine(NetEntity, Position);
}
#endregion
/// <summary>
/// Deconstructs the object into it's fields.
/// </summary>
/// <param name="entId">ID of the entity that this position is relative to.</param>
/// <param name="localPos">Position in the entity's local space.</param>
public void Deconstruct(out NetEntity entId, out Vector2 localPos)
{
entId = NetEntity;
localPos = Position;
}
/// <inheritdoc />
public override string ToString()
{
return $"NetEntity={NetEntity}, X={Position.X:N2}, Y={Position.Y:N2}";
}
public string ToString(string? format, IFormatProvider? formatProvider) => ToString();
public bool TryFormat(
Span<char> destination,
out int charsWritten,
ReadOnlySpan<char> format,
IFormatProvider? provider)
{
return FormatHelpers.TryFormatInto(
destination,
out charsWritten,
$"NetEntity={NetEntity}, X={Position.X:N2}, Y={Position.Y:N2}");
}
}

Some files were not shown because too many files have changed in this diff Show More