diff --git a/Robust.Client/Console/Commands/AddCompCommand.cs b/Robust.Client/Console/Commands/AddCompCommand.cs
index 49f259f3b..9c92e738d 100644
--- a/Robust.Client/Console/Commands/AddCompCommand.cs
+++ b/Robust.Client/Console/Commands/AddCompCommand.cs
@@ -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);
diff --git a/Robust.Client/Console/Commands/Debug.cs b/Robust.Client/Console/Commands/Debug.cs
index 6e8444de0..5e3c0dae3 100644
--- a/Robust.Client/Console/Commands/Debug.cs
+++ b/Robust.Client/Console/Commands/Debug.cs
@@ -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;
diff --git a/Robust.Client/GameObjects/ClientEntityManager.Network.cs b/Robust.Client/GameObjects/ClientEntityManager.Network.cs
new file mode 100644
index 000000000..28eac343e
--- /dev/null
+++ b/Robust.Client/GameObjects/ClientEntityManager.Network.cs
@@ -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);
+
+ ///
+ /// 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.
+ ///
+ ///
+ internal readonly Dictionary> 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(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(NetCoordinates netCoordinates, EntityUid callerEntity)
+ {
+ var entity = EnsureEntity(netCoordinates.NetEntity, callerEntity);
+ return new EntityCoordinates(entity, netCoordinates.Position);
+ }
+}
diff --git a/Robust.Client/GameObjects/ClientEntityManager.cs b/Robust.Client/GameObjects/ClientEntityManager.cs
index 3a7829b32..13014b490 100644
--- a/Robust.Client/GameObjects/ClientEntityManager.cs
+++ b/Robust.Client/GameObjects/ClientEntityManager.cs
@@ -16,7 +16,7 @@ namespace Robust.Client.GameObjects
///
/// Manager for entities -- controls things like template loading and instantiation
///
- 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;
diff --git a/Robust.Client/GameObjects/EntitySystems/AnimationPlayerSystem.cs b/Robust.Client/GameObjects/EntitySystems/AnimationPlayerSystem.cs
index 08acd32c0..4f2655bda 100644
--- a/Robust.Client/GameObjects/EntitySystems/AnimationPlayerSystem.cs
+++ b/Robust.Client/GameObjects/EntitySystems/AnimationPlayerSystem.cs
@@ -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);
diff --git a/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs b/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs
index 7e26c062d..32db78501 100644
--- a/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs
+++ b/Robust.Client/GameObjects/EntitySystems/AudioSystem.cs
@@ -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
});
}
diff --git a/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs b/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs
index 713a0f4d6..8fe68c3e7 100644
--- a/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs
+++ b/Robust.Client/GameObjects/EntitySystems/ContainerSystem.cs
@@ -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 _pointLightQuery;
+ private EntityQuery _spriteQuery;
+
private readonly HashSet _updateQueue = new();
- public readonly Dictionary ExpectedEntities = new();
+ public readonly Dictionary ExpectedEntities = new();
public override void Initialize()
{
base.Initialize();
+ _pointLightQuery = GetEntityQuery();
+ _spriteQuery = GetEntityQuery();
+
EntityManager.EntityInitialized += HandleEntityInitialized;
SubscribeLocalEvent(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();
- var xformQuery = GetEntityQuery();
- var xform = xformQuery.GetComponent(uid);
+ var xform = TransformQuery.GetComponent(uid);
// Delete now-gone containers.
var toDelete = new ValueList();
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(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();
+
+ 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();
- foreach (var entityUid in container.ExpectedEntities)
+ var removedExpected = new ValueList();
+ 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(type);
- newContainer.ID = id;
- newContainer.Manager = component;
- return newContainer;
- }
+ if (TryComp(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();
- var spriteQuery = EntityManager.GetEntityQuery();
- var xformQuery = EntityManager.GetEntityQuery();
foreach (var toUpdate in _updateQueue)
{
if (Deleted(toUpdate))
continue;
- UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
+ UpdateEntityRecursively(toUpdate);
}
_updateQueue.Clear();
}
- private void UpdateEntityRecursively(
- EntityUid entity,
- EntityQuery xformQuery,
- EntityQuery pointQuery,
- EntityQuery 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(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 xformQuery,
- EntityQuery pointQuery,
- EntityQuery 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);
}
}
}
diff --git a/Robust.Client/GameObjects/EntitySystems/InputSystem.cs b/Robust.Client/GameObjects/EntitySystems/InputSystem.cs
index a96cbd7f4..f4691116c 100644
--- a/Robust.Client/GameObjects/EntitySystems/InputSystem.cs
+++ b/Robust.Client/GameObjects/EntitySystems/InputSystem.cs
@@ -52,7 +52,7 @@ namespace Robust.Client.GameObjects
/// Arguments for this event.
/// 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
- 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.
///
/// Input command to handle as predicted.
- 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);
}
diff --git a/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs b/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs
index 55ea080d9..fb38f67a2 100644
--- a/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs
+++ b/Robust.Client/GameObjects/EntitySystems/UserInterfaceSystem.cs
@@ -43,7 +43,8 @@ namespace Robust.Client.GameObjects
private void MessageReceived(BoundUIWrapMessage ev)
{
- var uid = ev.Entity;
+ var uid = GetEntity(ev.Entity);
+
if (!TryComp(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));
}
}
}
diff --git a/Robust.Client/GameObjects/IClientEntityManagerInternal.cs b/Robust.Client/GameObjects/IClientEntityManagerInternal.cs
index 9d93068f6..e3d3d3fbc 100644
--- a/Robust.Client/GameObjects/IClientEntityManagerInternal.cs
+++ b/Robust.Client/GameObjects/IClientEntityManagerInternal.cs
@@ -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);
diff --git a/Robust.Client/GameStates/ClientDirtySystem.cs b/Robust.Client/GameStates/ClientDirtySystem.cs
index a7bf599e3..723188485 100644
--- a/Robust.Client/GameStates/ClientDirtySystem.cs
+++ b/Robust.Client/GameStates/ClientDirtySystem.cs
@@ -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> 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);
}
}
diff --git a/Robust.Client/GameStates/ClientGameStateManager.cs b/Robust.Client/GameStates/ClientGameStateManager.cs
index 0865dff9f..991b8dfeb 100644
--- a/Robust.Client/GameStates/ClientGameStateManager.cs
+++ b/Robust.Client/GameStates/ClientGameStateManager.cs
@@ -47,8 +47,10 @@ namespace Robust.Client.GameStates
= new();
// Game state dictionaries that get used every tick.
- private readonly Dictionary _toApply = new();
- private readonly Dictionary _toCreate = new();
+ private readonly Dictionary _toApply = new();
+ private readonly Dictionary _toCreate = new();
+ private readonly Dictionary _compStateWork = new();
+ private readonly Dictionary> _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> GetFullRep()
+ public Dictionary> GetFullRep()
=> _processor.GetFullRep();
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
@@ -210,7 +212,7 @@ namespace Robust.Client.GameStates
PvsLeave?.Invoke(message);
}
- public void QueuePvsDetach(List entities, GameTick tick)
+ public void QueuePvsDetach(List entities, GameTick tick)
{
_processor.AddLeavePvsMessage(entities, tick);
if (_replayRecording.IsRecording)
@@ -298,7 +300,7 @@ namespace Robust.Client.GameStates
_processor.UpdateFullRep(curState);
}
- IEnumerable createdEntities;
+ IEnumerable 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();
- var query = _entityManager.GetEntityQuery();
+ var metaQuery = _entityManager.GetEntityQuery();
RemQueue 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 .
///
- private void MergeImplicitData(IEnumerable createdEntities)
+ private void MergeImplicitData(IEnumerable createdEntities)
{
- var outputData = new Dictionary>();
+ var outputData = new Dictionary>();
var bus = _entityManager.EventBus;
- foreach (var createdEntity in createdEntities)
+ foreach (var netEntity in createdEntities)
{
+ var createdEntity = _entityManager.GetEntity(netEntity);
var compData = new Dictionary();
- 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 ApplyGameState(GameState curState, GameState? nextState)
+ public IEnumerable ApplyGameState(GameState curState, GameState? nextState)
{
using var _ = _timing.StartStateApplicationArea();
@@ -637,7 +640,7 @@ namespace Robust.Client.GameStates
_config.TickProcessMessages();
}
- (IEnumerable Created, List Detached) output;
+ (IEnumerable Created, List Detached) output;
using (_prof.Group("Entity"))
{
output = ApplyEntityStates(curState, nextState);
@@ -656,7 +659,7 @@ namespace Robust.Client.GameStates
return output.Created;
}
- private (IEnumerable Created, List Detached) ApplyEntityStates(GameState curState, GameState? nextState)
+ private (IEnumerable Created, List Detached) ApplyEntityStates(GameState curState, GameState? nextState)
{
var metas = _entities.GetEntityQuery();
var xforms = _entities.GetEntityQuery();
@@ -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(entityStates.Length);
+ var stateEnts = new HashSet();
foreach (var entState in entityStates)
{
- stateEnts.Add(entState.Uid);
+ stateEnts.Add(entState.NetEntity);
}
- var metas = _entities.GetEntityQuery();
var xforms = _entities.GetEntityQuery();
var xformSys = _entitySystemManager.GetEntitySystem();
- var currentEnts = _entities.GetEntities();
var toDelete = new List(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();
+
+ 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 delSpan,
+ ReadOnlySpan delSpan,
EntityQuery xforms,
EntityQuery 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 entities)
+ public void DetachImmediate(List entities)
{
var metas = _entities.GetEntityQuery();
var xforms = _entities.GetEntityQuery();
@@ -935,7 +976,7 @@ namespace Robust.Client.GameStates
Detach(GameTick.MaxValue, null, entities, metas, xforms, xformSys, containerSys, lookupSys);
}
- private List ProcessPvsDeparture(
+ private List ProcessPvsDeparture(
GameTick toTick,
EntityQuery metas,
EntityQuery xforms,
@@ -944,7 +985,7 @@ namespace Robust.Client.GameStates
EntityLookupSystem lookupSys)
{
var toDetach = _processor.GetEntitiesToDetach(toTick, _pvsDetachBudget);
- var detached = new List();
+ var detached = new List();
if (toDetach.Count == 0)
return detached;
@@ -966,16 +1007,18 @@ namespace Robust.Client.GameStates
private void Detach(GameTick maxTick,
GameTick? lastStateApplied,
- List entities,
+ List entities,
EntityQuery metas,
EntityQuery xforms,
SharedTransformSystem xformSys,
ContainerSystem containerSys,
EntityLookupSystem lookupSys,
- List? detached = null)
+ List? 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 toCreate)
+ private void InitializeAndStart(Dictionary toCreate)
{
+ var metaQuery = _entityManager.GetEntityQuery();
+
#if EXCEPTION_TOLERANCE
- HashSet brokenEnts = new HashSet();
+ var brokenEnts = new List();
#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(size);
+ _compStateWork.Clear();
+ var meta = _entityManager.GetComponent(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(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().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 query)
+ void _recursiveRemoveState(NetEntity netEntity, TransformComponent xform, EntityQuery metaQuery, EntityQuery 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());
+ if (!_entities.IsClientSide(uid) && _entities.TryGetComponent(uid, out TransformComponent? xform))
+ _recursiveRemoveState(meta.NetEntity, xform, _entities.GetEntityQuery(), _entities.GetEntityQuery());
// 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 Detached;
+ public readonly List Detached;
- public GameStateAppliedArgs(GameState appliedState, List detached)
+ public GameStateAppliedArgs(GameState appliedState, List 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;
}
}
}
diff --git a/Robust.Client/GameStates/GameStateProcessor.cs b/Robust.Client/GameStates/GameStateProcessor.cs
index a929bf9d2..694e99aaa 100644
--- a/Robust.Client/GameStates/GameStateProcessor.cs
+++ b/Robust.Client/GameStates/GameStateProcessor.cs
@@ -20,7 +20,7 @@ namespace Robust.Client.GameStates
private readonly List _stateBuffer = new();
- private readonly Dictionary> _pvsDetachMessages = new();
+ private readonly Dictionary> _pvsDetachMessages = new();
private ISawmill _logger = default!;
private ISawmill _stateLogger = default!;
@@ -44,7 +44,7 @@ namespace Robust.Client.GameStates
///
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
///
- internal readonly Dictionary> _lastStateFullRep
+ internal readonly Dictionary> _lastStateFullRep
= new();
///
@@ -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();
- _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 entities, GameTick tick)
+ internal void AddLeavePvsMessage(List 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 Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
+ public List<(GameTick Tick, List Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
{
- var result = new List<(GameTick Tick, List Entities)>();
+ var result = new List<(GameTick Tick, List 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> implicitData)
+ public void MergeImplicitData(Dictionary> 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 GetLastServerStates(EntityUid entity)
+ public Dictionary GetLastServerStates(NetEntity netEntity)
{
- return _lastStateFullRep[entity];
+ return _lastStateFullRep[netEntity];
}
- public Dictionary> GetFullRep()
+ public Dictionary> GetFullRep()
{
return _lastStateFullRep;
}
- public bool TryGetLastServerStates(EntityUid entity,
+ public bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary? dictionary)
{
return _lastStateFullRep.TryGetValue(entity, out dictionary);
diff --git a/Robust.Client/GameStates/IClientGameStateManager.cs b/Robust.Client/GameStates/IClientGameStateManager.cs
index 4e63d9f7f..36feb53d5 100644
--- a/Robust.Client/GameStates/IClientGameStateManager.cs
+++ b/Robust.Client/GameStates/IClientGameStateManager.cs
@@ -75,7 +75,7 @@ namespace Robust.Client.GameStates
///
/// Applies a given set of game states.
///
- IEnumerable ApplyGameState(GameState curState, GameState? nextState);
+ IEnumerable ApplyGameState(GameState curState, GameState? nextState);
///
/// Resets any entities that have changed while predicting future ticks.
@@ -86,12 +86,12 @@ namespace Robust.Client.GameStates
/// An input command has been dispatched.
///
/// Message being dispatched.
- void InputCommandDispatched(FullInputCmdMessage message);
+ void InputCommandDispatched(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message);
///
/// Requests a full state from the server. This should override even implicit entity data.
///
- void RequestFullState(EntityUid? missingEntity = null);
+ void RequestFullState(NetEntity? missingEntity = null);
uint SystemMessageDispatched(T message) where T : EntityEventArgs;
@@ -105,7 +105,7 @@ namespace Robust.Client.GameStates
///
/// Returns the full collection of cached game states that are used to reset predicted entities.
///
- Dictionary> GetFullRep();
+ Dictionary> GetFullRep();
///
/// 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.
///
- void QueuePvsDetach(List entities, GameTick tick);
+ void QueuePvsDetach(List entities, GameTick tick);
///
/// Immediately detach several entities.
///
- void DetachImmediate(List entities);
+ void DetachImmediate(List entities);
///
/// Clears the PVS detach queue.
diff --git a/Robust.Client/GameStates/IGameStateProcessor.cs b/Robust.Client/GameStates/IGameStateProcessor.cs
index ebf519c8e..fd366ceb2 100644
--- a/Robust.Client/GameStates/IGameStateProcessor.cs
+++ b/Robust.Client/GameStates/IGameStateProcessor.cs
@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
/// The data to merge.
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
///
- void MergeImplicitData(Dictionary> data);
+ void MergeImplicitData(Dictionary> data);
///
/// Get the last state data from the server for an entity.
///
/// Dictionary (net ID -> ComponentState)
- Dictionary GetLastServerStates(EntityUid entity);
+ Dictionary GetLastServerStates(NetEntity entity);
///
/// Calculate the number of applicable states in the game state buffer from a given tick.
@@ -98,7 +98,7 @@ namespace Robust.Client.GameStates
/// The tick to calculate from.
int CalculateBufferSize(GameTick fromTick);
- bool TryGetLastServerStates(EntityUid entity,
+ bool TryGetLastServerStates(NetEntity entity,
[NotNullWhen(true)] out Dictionary? dictionary);
}
}
diff --git a/Robust.Client/GameStates/NetEntityOverlay.cs b/Robust.Client/GameStates/NetEntityOverlay.cs
index f5638b9f1..e63c3e33a 100644
--- a/Robust.Client/GameStates/NetEntityOverlay.cs
+++ b/Robust.Client/GameStates/NetEntityOverlay.cs
@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
private readonly Font _font;
private readonly int _lineHeight;
- private readonly Dictionary _netEnts = new();
+ private readonly Dictionary _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;
}
diff --git a/Robust.Client/GameStates/NetGraphOverlay.cs b/Robust.Client/GameStates/NetGraphOverlay.cs
index 9b02d1db2..a045da24d 100644
--- a/Robust.Client/GameStates/NetGraphOverlay.cs
+++ b/Robust.Client/GameStates/NetGraphOverlay.cs
@@ -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().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();
-
- 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;
}
}
}
diff --git a/Robust.Client/Physics/GridFixtureSystem.cs b/Robust.Client/Physics/GridFixtureSystem.cs
index 0b4669b64..d0d6eeb38 100644
--- a/Robust.Client/Physics/GridFixtureSystem.cs
+++ b/Robust.Client/Physics/GridFixtureSystem.cs
@@ -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
diff --git a/Robust.Client/Physics/JointSystem.cs b/Robust.Client/Physics/JointSystem.cs
index d17ff7175..3ee18c133 100644
--- a/Robust.Client/Physics/JointSystem.cs
+++ b/Robust.Client/Physics/JointSystem.cs
@@ -20,7 +20,7 @@ namespace Robust.Client.Physics
{
if (args.Current is not JointComponentState jointState) return;
- component.Relay = jointState.Relay;
+ component.Relay = EnsureEntity(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));
}
}
}
diff --git a/Robust.Client/Placement/PlacementManager.cs b/Robust.Client/Placement/PlacementManager.cs
index 55d4a1daf..0de25afa7 100644
--- a/Robust.Client/Placement/PlacementManager.cs
+++ b/Robust.Client/Placement/PlacementManager.cs
@@ -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;
diff --git a/Robust.Client/Player/PlayerManager.cs b/Robust.Client/Player/PlayerManager.cs
index 77c668f7a..42c7e0522 100644
--- a/Robust.Client/Player/PlayerManager.cs
+++ b/Robust.Client/Player/PlayerManager.cs
@@ -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!;
///
/// 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? LocalPlayerChanged;
///
@@ -82,6 +86,7 @@ namespace Robust.Client.Player
{
_client.RunLevelChanged += OnRunLevelChanged;
+ _sawmill = _logMan.GetSawmill("player");
_network.RegisterNetMessage();
_network.RegisterNetMessage(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)
diff --git a/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs b/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs
index cc420e9eb..b426b7b46 100644
--- a/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs
+++ b/Robust.Client/Replays/Loading/ReplayLoadManager.Checkpoints.cs
@@ -83,8 +83,8 @@ public sealed partial class ReplayLoadManager
}
HashSet uploadedFiles = new();
- var detached = new HashSet();
- var detachQueue = new Dictionary>();
+ var detached = new HashSet();
+ var detachQueue = new Dictionary>();
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 entStates = new(entSpan.Count);
+ Dictionary 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());
+ Array.Empty());
checkPoints.Add(new CheckpointState(state0, timeBase, cvars, 0, detached));
DebugTools.Assert(state0.EntityDeletions.Value.Count == 0);
- var empty = Array.Empty();
+ var empty = Array.Empty();
TimeSpan GetTime(GameTick tick)
{
@@ -176,8 +176,8 @@ public sealed partial class ReplayLoadManager
private void ProcessQueue(
GameTick curTick,
- Dictionary> detachQueue,
- HashSet detached)
+ Dictionary> detachQueue,
+ HashSet detached)
{
foreach (var (tick, ents) in detachQueue)
{
@@ -192,7 +192,7 @@ public sealed partial class ReplayLoadManager
HashSet uploadedFiles,
Dictionary> prototypes,
Dictionary cvars,
- Dictionary> detachQueue,
+ Dictionary> detachQueue,
ref (TimeSpan, GameTick) timeBase,
bool ignoreDuplicates = false)
{
@@ -301,8 +301,8 @@ public sealed partial class ReplayLoadManager
_locMan.ReloadLocalizations();
}
- private void UpdateDeletions(NetListAsArray entityDeletions,
- Dictionary entStates, HashSet detached)
+ private void UpdateDeletions(NetListAsArray entityDeletions,
+ Dictionary entStates, HashSet detached)
{
foreach (var ent in entityDeletions.Span)
{
@@ -311,16 +311,16 @@ public sealed partial class ReplayLoadManager
}
}
- private void UpdateEntityStates(ReadOnlySpan span, Dictionary entStates,
- ref int spawnedTracker, ref int stateTracker, HashSet detached)
+ private void UpdateEntityStates(ReadOnlySpan span, Dictionary entStates,
+ ref int spawnedTracker, ref int stateTracker, HashSet 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 span, Dictionary playerStates)
diff --git a/Robust.Client/Replays/Loading/ReplayLoadManager.Implicit.cs b/Robust.Client/Replays/Loading/ReplayLoadManager.Implicit.cs
index d1a30103e..dafae76c1 100644
--- a/Robust.Client/Replays/Loading/ReplayLoadManager.Implicit.cs
+++ b/Robust.Client/Replays/Loading/ReplayLoadManager.Implicit.cs
@@ -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;
}
}
diff --git a/Robust.Client/Replays/Loading/ReplayLoadManager.Start.cs b/Robust.Client/Replays/Loading/ReplayLoadManager.Start.cs
index 0a19f0336..42728535f 100644
--- a/Robust.Client/Replays/Loading/ReplayLoadManager.Start.cs
+++ b/Robust.Client/Replays/Loading/ReplayLoadManager.Start.cs
@@ -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(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)
{
diff --git a/Robust.Client/Replays/Loading/ReplayLoadManager.cs b/Robust.Client/Replays/Loading/ReplayLoadManager.cs
index 1e4973bb0..84dbd1871 100644
--- a/Robust.Client/Replays/Loading/ReplayLoadManager.cs
+++ b/Robust.Client/Replays/Loading/ReplayLoadManager.cs
@@ -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!;
diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs
index fc6b05892..cfd9f77e0 100644
--- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs
+++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Checkpoint.cs
@@ -82,19 +82,28 @@ internal sealed partial class ReplayPlaybackManager
var metas = _entMan.GetEntityQuery();
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;
}
}
diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs
index 623edf2d9..b14a36b36 100644
--- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs
+++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.Update.cs
@@ -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(leavePvs.Entities);
+ var clone = new List(leavePvs.Entities);
_gameState.QueuePvsDetach(clone, leavePvs.Tick);
continue;
diff --git a/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs b/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs
index 653e740e7..22f9f1a24 100644
--- a/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs
+++ b/Robust.Client/Replays/Playback/ReplayPlaybackManager.cs
@@ -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!;
diff --git a/Robust.Client/Replays/ReplayRecordingManager.cs b/Robust.Client/Replays/ReplayRecordingManager.cs
index 026103d53..e9dd7ff2a 100644
--- a/Robust.Client/Replays/ReplayRecordingManager.cs
+++ b/Robust.Client/Replays/ReplayRecordingManager.cs
@@ -95,12 +95,12 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
{
var tick = _timing.LastRealTick;
var players = _player.Sessions.Select(GetPlayerState).ToArray();
- var deletions = Array.Empty();
+ var deletions = Array.Empty();
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(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();
+ var detached = new List();
var query = _entMan.AllEntityQueryEnumerator();
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),
};
}
}
diff --git a/Robust.Client/ViewVariables/ClientViewVariablesManager.cs b/Robust.Client/ViewVariables/ClientViewVariablesManager.cs
index 935ff4611..ea7ee680b 100644
--- a/Robust.Client/ViewVariables/ClientViewVariablesManager.cs
+++ b/Robust.Client/ViewVariables/ClientViewVariablesManager.cs
@@ -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);
}
diff --git a/Robust.Client/ViewVariables/Instances/ViewVariablesInstanceEntity.cs b/Robust.Client/ViewVariables/Instances/ViewVariablesInstanceEntity.cs
index d44f53e88..aa1149ef8 100644
--- a/Robust.Client/ViewVariables/Instances/ViewVariablesInstanceEntity.cs
+++ b/Robust.Client/ViewVariables/Instances/ViewVariablesInstanceEntity.cs
@@ -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().RemoteExecuteCommand(null, $"rmcomp {_entity} {componentType.ComponentName}");
+ IoCManager.Resolve().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().RemoteExecuteCommand(null, $"addcomp {_entity} {eventArgs.Entry}");
+ IoCManager.Resolve().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)
{
diff --git a/Robust.Client/ViewVariables/ViewVariablesCommand.cs b/Robust.Client/ViewVariables/ViewVariablesCommand.cs
index b24b6b684..c9567f3b2 100644
--- a/Robust.Client/ViewVariables/ViewVariablesCommand.cs
+++ b/Robust.Client/ViewVariables/ViewVariablesCommand.cs
@@ -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);
}
}
}
diff --git a/Robust.Server/Console/Commands/AddComponentCommand.cs b/Robust.Server/Console/Commands/AddComponentCommand.cs
index 15006569f..18f2639ba 100644
--- a/Robust.Server/Console/Commands/AddComponentCommand.cs
+++ b/Robust.Server/Console/Commands/AddComponentCommand.cs
@@ -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(uid).EntityName} already has a {componentName} component.");
+ shell.WriteLine($"Entity {_entityManager.GetComponent(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(uid).EntityName}.");
+ shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent(uid.Value).EntityName}.");
}
}
}
diff --git a/Robust.Server/Console/Commands/DeleteCommand.cs b/Robust.Server/Console/Commands/DeleteCommand.cs
index b3a9931b2..5482447ec 100644
--- a/Robust.Server/Console/Commands/DeleteCommand.cs
+++ b/Robust.Server/Console/Commands/DeleteCommand.cs
@@ -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);
}
}
}
diff --git a/Robust.Server/Console/Commands/MapCommands.cs b/Robust.Server/Console/Commands/MapCommands.cs
index 4733e5145..83a1bcf47 100644
--- a/Robust.Server/Console/Commands/MapCommands.cs
+++ b/Robust.Server/Console/Commands/MapCommands.cs
@@ -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))
{
diff --git a/Robust.Server/Console/Commands/RemoveComponentCommand.cs b/Robust.Server/Console/Commands/RemoveComponentCommand.cs
index 35e2e4cee..79522d00e 100644
--- a/Robust.Server/Console/Commands/RemoveComponentCommand.cs
+++ b/Robust.Server/Console/Commands/RemoveComponentCommand.cs
@@ -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}.");
diff --git a/Robust.Server/Console/Commands/ScaleCommand.cs b/Robust.Server/Console/Commands/ScaleCommand.cs
index c3a0ee73d..601e775c3 100644
--- a/Robust.Server/Console/Commands/ScaleCommand.cs
+++ b/Robust.Server/Console/Commands/ScaleCommand.cs
@@ -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();
var appearance = _entityManager.System();
+ var uid = _entityManager.GetEntity(netEntity);
_entityManager.EnsureComponent(uid);
var @event = new ScaleEntityEvent();
_entityManager.EventBus.RaiseLocalEvent(uid, ref @event);
diff --git a/Robust.Server/Console/Commands/SpinCommand.cs b/Robust.Server/Console/Commands/SpinCommand.cs
index fe2038f92..9f48332a9 100644
--- a/Robust.Server/Console/Commands/SpinCommand.cs
+++ b/Robust.Server/Console/Commands/SpinCommand.cs
@@ -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();
physicsSystem.SetAngularDamping(physics, drag);
- physicsSystem.SetAngularVelocity(target, speed, body: physics);
+ physicsSystem.SetAngularVelocity(target.Value, speed, body: physics);
}
}
diff --git a/Robust.Server/Console/Commands/ViewSubscriberCommand.cs b/Robust.Server/Console/Commands/ViewSubscriberCommand.cs
index 8d9109fcb..963170a83 100644
--- a/Robust.Server/Console/Commands/ViewSubscriberCommand.cs
+++ b/Robust.Server/Console/Commands/ViewSubscriberCommand.cs
@@ -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().AddViewSubscriber(uid, playerSession);
+ _entities.EntitySysManager.GetEntitySystem().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().RemoveViewSubscriber(uid, playerSession);
+ _entities.EntitySysManager.GetEntitySystem().RemoveViewSubscriber(uid.Value, playerSession);
}
}
}
diff --git a/Robust.Server/Containers/ContainerSystem.cs b/Robust.Server/Containers/ContainerSystem.cs
index 1a632e2a3..d298fc2b4 100644
--- a/Robust.Server/Containers/ContainerSystem.cs
+++ b/Robust.Server/Containers/ContainerSystem.cs
@@ -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);
diff --git a/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs b/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs
index 2c63635bb..fe2fa1280 100644
--- a/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs
+++ b/Robust.Server/GameObjects/EntitySystems/AudioSystem.cs
@@ -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
};
diff --git a/Robust.Server/GameObjects/EntitySystems/InputSystem.cs b/Robust.Server/GameObjects/EntitySystems/InputSystem.cs
index 87b169b18..e74a0205f 100644
--- a/Robust.Server/GameObjects/EntitySystems/InputSystem.cs
+++ b/Robust.Server/GameObjects/EntitySystems/InputSystem.cs
@@ -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;
}
}
diff --git a/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs b/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs
index 169545d0c..513476385 100644
--- a/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs
+++ b/Robust.Server/GameObjects/EntitySystems/UserInterfaceSystem.cs
@@ -83,7 +83,7 @@ namespace Robust.Server.GameObjects
///
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.
///
- 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
///
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;
}
diff --git a/Robust.Server/GameObjects/IServerEntityManagerInternal.cs b/Robust.Server/GameObjects/IServerEntityManagerInternal.cs
index ee682d362..1717eb130 100644
--- a/Robust.Server/GameObjects/IServerEntityManagerInternal.cs
+++ b/Robust.Server/GameObjects/IServerEntityManagerInternal.cs
@@ -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);
diff --git a/Robust.Server/GameObjects/ServerEntityManager.cs b/Robust.Server/GameObjects/ServerEntityManager.cs
index a496f4927..918bda7ef 100644
--- a/Robust.Server/GameObjects/ServerEntityManager.cs
+++ b/Robust.Server/GameObjects/ServerEntityManager.cs
@@ -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(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,
diff --git a/Robust.Server/GameStates/IServerGameStateManager.cs b/Robust.Server/GameStates/IServerGameStateManager.cs
index 281d0ab0f..f2757abe0 100644
--- a/Robust.Server/GameStates/IServerGameStateManager.cs
+++ b/Robust.Server/GameStates/IServerGameStateManager.cs
@@ -24,6 +24,6 @@ namespace Robust.Server.GameStates
Action? ClientAck { get; set; }
- Action? ClientRequestFull { get; set; }
+ Action? ClientRequestFull { get; set; }
}
}
diff --git a/Robust.Server/GameStates/PvsOverrideSystem.cs b/Robust.Server/GameStates/PvsOverrideSystem.cs
index 20be0bec3..81eff401a 100644
--- a/Robust.Server/GameStates/PvsOverrideSystem.cs
+++ b/Robust.Server/GameStates/PvsOverrideSystem.cs
@@ -18,7 +18,7 @@ public sealed class PvsOverrideSystem : EntitySystem
/// If true, this will also recursively send any children of the given index.
public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false)
{
- _pvs.EntityPVSCollection.AddGlobalOverride(uid, removeExistingOverride, recursive);
+ _pvs.EntityPVSCollection.AddGlobalOverride(GetNetEntity(uid), removeExistingOverride, recursive);
}
///
@@ -28,7 +28,7 @@ public sealed class PvsOverrideSystem : EntitySystem
/// Whether or not to supersede existing overrides.
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
{
- _pvs.EntityPVSCollection.UpdateIndex(uid, session, removeExistingOverride);
+ _pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), session, removeExistingOverride);
}
///
@@ -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);
}
}
diff --git a/Robust.Server/GameStates/PvsSystem.Ack.cs b/Robust.Server/GameStates/PvsSystem.Ack.cs
index 7ef278e2c..ba2680d4a 100644
--- a/Robust.Server/GameStates/PvsSystem.Ack.cs
+++ b/Robust.Server/GameStates/PvsSystem.Ack.cs
@@ -45,7 +45,7 @@ internal sealed partial class PvsSystem
return;
var ackedTick = sessionData.LastReceivedAck;
- Dictionary? ackedData;
+ Dictionary? ackedData;
if (sessionData.Overflow != null && sessionData.Overflow.Value.Tick <= ackedTick)
{
diff --git a/Robust.Server/GameStates/PvsSystem.cs b/Robust.Server/GameStates/PvsSystem.cs
index 44c4e2136..6037a25cd 100644
--- a/Robust.Server/GameStates/PvsSystem.cs
+++ b/Robust.Server/GameStates/PvsSystem.cs
@@ -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 PlayerData = new();
- private PVSCollection _entityPvsCollection = default!;
- public PVSCollection EntityPVSCollection => _entityPvsCollection;
+ private PVSCollection _entityPvsCollection = default!;
+ public PVSCollection EntityPVSCollection => _entityPvsCollection;
private readonly List _pvsCollections = new();
- private readonly ObjectPool> _visSetPool
- = new DefaultObjectPool>(
- new DictPolicy(), MaxVisPoolSize);
+ private readonly ObjectPool> _visSetPool
+ = new DefaultObjectPool>(
+ new DictPolicy(), MaxVisPoolSize);
- private readonly ObjectPool> _stackPool
- = new DefaultObjectPool>(
- new StackPolicy(), MaxVisPoolSize);
+ private readonly ObjectPool> _stackPool
+ = new DefaultObjectPool>(
+ new StackPolicy(), MaxVisPoolSize);
- private readonly ObjectPool> _uidSetPool
- = new DefaultObjectPool>(new SetPolicy(), MaxVisPoolSize);
-
- private readonly ObjectPool> _chunkCachePool =
- new DefaultObjectPool>(
- new DictPolicy(), MaxVisPoolSize);
+ private readonly ObjectPool> _chunkCachePool =
+ new DefaultObjectPool>(
+ new DictPolicy(), MaxVisPoolSize);
private readonly ObjectPool> _playerChunkPool =
new DefaultObjectPool>(new SetPolicy(), MaxVisPoolSize);
- private readonly ObjectPool> _treePool =
- new DefaultObjectPool>(new TreePolicy(), MaxVisPoolSize);
+ private readonly ObjectPool> _treePool =
+ new DefaultObjectPool>(new TreePolicy(), MaxVisPoolSize);
private readonly ObjectPool> _mapChunkPool =
new DefaultObjectPool>(
@@ -102,19 +98,25 @@ internal sealed partial class PvsSystem : EntitySystem
private readonly List<(uint, IChunkIndexLocation)> _chunkList = new(64);
internal readonly HashSet PendingAcks = new();
+ private readonly Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary metadata,
+ RobustTree tree)?> _previousTrees = new();
+
+ private readonly HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
+
private EntityQuery _eyeQuery;
- private EntityQuery _xformQuery;
private EntityQuery _metaQuery;
+ private EntityQuery _xformQuery;
public override void Initialize()
{
base.Initialize();
_eyeQuery = GetEntityQuery();
+ _metaQuery = GetEntityQuery();
_xformQuery = GetEntityQuery();
_metaQuery = GetEntityQuery();
- _entityPvsCollection = RegisterPVSCollection();
+ _entityPvsCollection = RegisterPVSCollection();
SubscribeLocalEvent(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.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 metadata,
- RobustTree tree)?> _previousTrees = new();
-
- private HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
-
public void RegisterNewPreviousChunkTrees(
List<(uint, IChunkIndexLocation)> chunks,
- (Dictionary metadata, RobustTree tree)?[] trees,
+ (Dictionary metadata, RobustTree 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 transform,
- EntityQuery metadata,
- out (Dictionary mData, RobustTree tree)? result)
+ out (Dictionary mData, RobustTree 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 tree, Dictionary set, EntityQuery transform,
- EntityQuery metadata)
+ private bool AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, uint visMask, RobustTree tree, Dictionary 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? updates, List? deletions, List? leftPvs, GameTick fromTick)
+ internal (List? updates, List? deletions, List? leftPvs, GameTick fromTick)
CalculateEntityStates(IPlayerSession session,
GameTick fromTick,
GameTick toTick,
- EntityQuery mQuery,
- EntityQuery tQuery,
- (Dictionary metadata, RobustTree tree)?[] chunks,
+ (Dictionary metadata, RobustTree tree)?[] chunks,
HashSet 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(ent) || HasComp(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(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.
///
- private List? ProcessLeavePVS(
- Dictionary visibleEnts,
- Dictionary? lastSent)
+ private List? ProcessLeavePVS(
+ Dictionary visibleEnts,
+ Dictionary? lastSent)
{
if (lastSent == null)
return null;
- var leftView = new List();
- foreach (var uid in lastSent.Keys)
+ var leftView = new List();
+ 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 tree,
- Dictionary? lastAcked,
- Dictionary? lastSent,
- Dictionary toSend,
- Dictionary lastSeen,
- Dictionary metaDataCache,
- Stack stack,
+ private void RecursivelyAddTreeNode(in NetEntity nodeIndex,
+ RobustTree tree,
+ Dictionary? lastAcked,
+ Dictionary? lastSent,
+ Dictionary toSend,
+ Dictionary lastSeen,
+ Stack 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? lastAcked,
- Dictionary? lastSent,
- Dictionary toSend,
- Dictionary lastSeen,
- in EntityQuery metaQuery,
- in EntityQuery transQuery,
+ Dictionary? lastAcked,
+ Dictionary? lastSent,
+ Dictionary toSend,
+ Dictionary 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? lastAcked,
- Dictionary? lastSent,
- Dictionary toSend,
- Dictionary lastSeen,
+ Dictionary? lastAcked,
+ Dictionary? lastSent,
+ Dictionary toSend,
+ Dictionary 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? lastAcked,
- Dictionary? lastSent,
- Dictionary lastSeen,
+ private (bool Entered, bool ShouldAdd) ProcessEntry(
+ in NetEntity netEntity,
+ Dictionary? lastAcked,
+ Dictionary? lastSent,
+ Dictionary 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 toSend, GameTick fromTick, in bool entered, ref int entStateCount)
+ private void AddToSendSet(in NetEntity netEntity, MetaDataComponent metaDataComponent, Dictionary 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++;
}
///
/// Gets all entity states that have been modified after and including the provided tick.
///
- public (List?, List?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
+ public (List?, List?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
{
List? stateEntities;
- var toSend = _uidSetPool.Get();
+ var toSend = new HashSet();
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();
- var viewers = _uidSetPool.Get();
+ var viewers = new HashSet();
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}");
///
/// All s that this session saw during the last ticks.
///
- public readonly OverflowDictionary> SentEntities = new(DirtyBufferSize);
+ public readonly OverflowDictionary> SentEntities = new(DirtyBufferSize);
///
/// The most recently acked entities
///
- public (GameTick Tick, Dictionary Data)? LastAcked;
+ public (GameTick Tick, Dictionary Data)? LastAcked;
///
/// 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.
///
- public readonly Dictionary LastSeenAt = new();
+ public readonly Dictionary LastSeenAt = new();
///
/// overflow in case a player's last ack is more than ticks behind the current tick.
///
- public (GameTick Tick, Dictionary SentEnts)? Overflow;
+ public (GameTick Tick, Dictionary SentEnts)? Overflow;
///
/// If true, the client has explicitly requested a full state. Unlike the first state, we will send them
diff --git a/Robust.Server/GameStates/ServerGameStateManager.cs b/Robust.Server/GameStates/ServerGameStateManager.cs
index 857908504..d6029e5ee 100644
--- a/Robust.Server/GameStates/ServerGameStateManager.cs
+++ b/Robust.Server/GameStates/ServerGameStateManager.cs
@@ -63,7 +63,7 @@ namespace Robust.Server.GameStates
public ushort TransformNetId { get; set; }
public Action? ClientAck { get; set; }
- public Action? ClientRequestFull { get; set; }
+ public Action? 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[] PlayerChunks;
public EntityUid[][] ViewerEntities;
- public (Dictionary metadata, RobustTree tree)?[] ChunkCache;
+ public (Dictionary metadata, RobustTree 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 metadata, RobustTree tree)?[chunksCount];
+ new (Dictionary metadata, RobustTree tree)?[chunksCount];
// Update the reused trees sequentially to avoid having to lock the dictionary per chunk.
var reuse = ArrayPool.Shared.Rent(chunksCount);
- var transformQuery = _entityManager.GetEntityQuery();
- var metadataQuery = _entityManager.GetEntityQuery();
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(ent)
|| _entityManager.HasComponent(ent));
@@ -313,9 +311,9 @@ namespace Robust.Server.GameStates
var channel = session.ConnectedClient;
var sessionData = _pvs.PlayerData[session];
var lastAck = sessionData.LastReceivedAck;
- List? leftPvs = null;
+ List? leftPvs = null;
List? entStates;
- List? deletions;
+ List? 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]);
diff --git a/Robust.Server/Physics/GridFixtureSystem.cs b/Robust.Server/Physics/GridFixtureSystem.cs
index 62436e285..22cf6c2bc 100644
--- a/Robust.Server/Physics/GridFixtureSystem.cs
+++ b/Robust.Server/Physics/GridFixtureSystem.cs
@@ -130,7 +130,7 @@ namespace Robust.Server.Physics
var msg = new ChunkSplitDebugMessage
{
- Grid = uid,
+ Grid = GetNetEntity(uid),
};
foreach (var (index, group) in _nodes[uid])
diff --git a/Robust.Server/Physics/JointSystem.cs b/Robust.Server/Physics/JointSystem.cs
index 7ab7f69ba..ef05188c2 100644
--- a/Robust.Server/Physics/JointSystem.cs
+++ b/Robust.Server/Physics/JointSystem.cs
@@ -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);
}
}
diff --git a/Robust.Server/Placement/PlacementManager.cs b/Robust.Server/Placement/PlacementManager.cs
index b2ab64a91..b18970d9a 100644
--- a/Robust.Server/Placement/PlacementManager.cs
+++ b/Robust.Server/Placement/PlacementManager.cs
@@ -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(msg.EntityUid).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
+ var placementEraseEvent = new PlacementEntityEvent(entity, _entityManager.GetComponent(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().GetEntitiesIntersecting(start.GetMapId(_entityManager),
new Box2(start.Position, start.Position + rectSize)))
diff --git a/Robust.Server/Player/PlayerSession.cs b/Robust.Server/Player/PlayerSession.cs
index b5f217ac0..abec3d8c2 100644
--- a/Robust.Server/Player/PlayerSession.cs
+++ b/Robust.Server/Player/PlayerSession.cs
@@ -210,7 +210,7 @@ namespace Robust.Server.Player
{
PlayerState.Status = Status;
PlayerState.Name = Name;
- PlayerState.ControlledEntity = AttachedEntity;
+ PlayerState.ControlledEntity = IoCManager.Resolve().GetNetEntity(AttachedEntity);
_playerManager.Dirty();
}
diff --git a/Robust.Server/ViewVariables/ServerViewVariablesManager.cs b/Robust.Server/ViewVariables/ServerViewVariablesManager.cs
index 6b2e68ebf..cbf1a5342 100644
--- a/Robust.Server/ViewVariables/ServerViewVariablesManager.cs
+++ b/Robust.Server/ViewVariables/ServerViewVariablesManager.cs
@@ -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;
diff --git a/Robust.Server/ViewVariables/Traits/ViewVariablesTraitEntity.cs b/Robust.Server/ViewVariables/Traits/ViewVariablesTraitEntity.cs
index 726f10cec..fc52df020 100644
--- a/Robust.Server/ViewVariables/Traits/ViewVariablesTraitEntity.cs
+++ b/Robust.Server/ViewVariables/Traits/ViewVariablesTraitEntity.cs
@@ -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().GetEntity(netEntity);
}
public override ViewVariablesBlob? DataRequest(ViewVariablesRequest viewVariablesRequest)
diff --git a/Robust.Server/ViewVariables/Traits/ViewVariablesTraitMembers.cs b/Robust.Server/ViewVariables/Traits/ViewVariablesTraitMembers.cs
index 436b9971d..62255aa8a 100644
--- a/Robust.Server/ViewVariables/Traits/ViewVariablesTraitMembers.cs
+++ b/Robust.Server/ViewVariables/Traits/ViewVariablesTraitMembers.cs
@@ -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));
diff --git a/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs b/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs
index 20e1da16a..892c59f69 100644
--- a/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs
+++ b/Robust.Shared.CompNetworkGenerator/ComponentNetworkGenerator.cs
@@ -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};
diff --git a/Robust.Shared/Console/Commands/DumpEventTablesCommand.cs b/Robust.Shared/Console/Commands/DumpEventTablesCommand.cs
index 516fb2a0d..aa185506a 100644
--- a/Robust.Shared/Console/Commands/DumpEventTablesCommand.cs
+++ b/Robust.Shared/Console/Commands/DumpEventTablesCommand.cs
@@ -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}:");
diff --git a/Robust.Shared/Console/Commands/MapCommands.cs b/Robust.Shared/Console/Commands/MapCommands.cs
index 225808a4c..17d9a671d 100644
--- a/Robust.Shared/Console/Commands/MapCommands.cs
+++ b/Robust.Shared/Console/Commands/MapCommands.cs
@@ -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.");
}
}
diff --git a/Robust.Shared/Console/Commands/SpawnCommand.cs b/Robust.Shared/Console/Commands/SpawnCommand.cs
index e00a75924..28f82a05e 100644
--- a/Robust.Shared/Console/Commands/SpawnCommand.cs
+++ b/Robust.Shared/Console/Commands/SpawnCommand.cs
@@ -32,8 +32,8 @@ public sealed class SpawnCommand : LocalizedCommands
}
else if (args.Length == 2)
{
- var uid = EntityUid.Parse(args[1]);
- var entityCoordinates = _entityManager.GetComponent(uid).Coordinates;
+ var uidNet = NetEntity.Parse(args[1]);
+ var entityCoordinates = _entityManager.GetComponent(_entityManager.GetEntity(uidNet)).Coordinates;
var createdEntity = _entityManager.SpawnEntity(args[0], entityCoordinates);
placementEv = new PlacementEntityEvent(createdEntity, entityCoordinates, PlacementEventAction.Create, shell.Player?.UserId);
}
diff --git a/Robust.Shared/Console/Commands/TeleportCommands.cs b/Robust.Shared/Console/Commands/TeleportCommands.cs
index 4b0e64b61..b9234c6f5 100644
--- a/Robust.Shared/Console/Commands/TeleportCommands.cs
+++ b/Robust.Shared/Console/Commands/TeleportCommands.cs
@@ -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(gridId);
+ var gridXform = _ent.GetComponent(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));
diff --git a/Robust.Shared/Console/CompletionHelper.cs b/Robust.Shared/Console/CompletionHelper.cs
index 625c8ea73..464b7a1d4 100644
--- a/Robust.Shared/Console/CompletionHelper.cs
+++ b/Robust.Shared/Console/CompletionHelper.cs
@@ -155,18 +155,21 @@ public static class CompletionHelper
}
}
- public static IEnumerable EntityUids(string text, IEntityManager? entManager = null)
+ public static IEnumerable 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();
+ var query = entManager.AllEntityQueryEnumerator();
- 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);
}
}
}
diff --git a/Robust.Shared/Containers/BaseContainer.cs b/Robust.Shared/Containers/BaseContainer.cs
index 088182304..d4b5f132b 100644
--- a/Robust.Shared/Containers/BaseContainer.cs
+++ b/Robust.Shared/Containers/BaseContainer.cs
@@ -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
{
///
/// Base container class that all container inherit from.
///
- public abstract partial class BaseContainer : IContainer
+ [ImplicitDataDefinitionForInheritors]
+ public abstract partial class BaseContainer
{
- ///
+ ///
+ /// Readonly collection of all the entities contained within this specific container
+ ///
[ViewVariables]
public abstract IReadOnlyList ContainedEntities { get; }
- [ViewVariables]
- public abstract List ExpectedEntities { get; }
+ ///
+ /// Number of contained entities.
+ ///
+ public abstract int Count { get; }
- ///
- public abstract string ContainerType { get; }
+ [ViewVariables, NonSerialized]
+ public List ExpectedEntities = new();
- ///
- [ViewVariables]
- public bool Deleted { get; private set; }
+ ///
+ /// The ID of this container.
+ ///
+ [ViewVariables, NonSerialized, Access(typeof(SharedContainerSystem), typeof(ContainerManagerComponent))]
+ public string ID = default!;
- ///
- [ViewVariables]
- public string ID { get; internal set; } = default!; // Make sure you set me in init
+ [NonSerialized]
+ internal ContainerManagerComponent Manager = default!;
- ///
- public ContainerManagerComponent Manager { get; internal set; } = default!; // Make sure you set me in init
-
- ///
+ ///
+ /// Prevents light from escaping the container, from ex. a flashlight.
+ ///
[ViewVariables(VVAccess.ReadWrite)]
[DataField("occludes")]
public bool OccludesLight { get; set; } = true;
- ///
+ ///
+ /// The entity that owns this container.
+ ///
[ViewVariables]
public EntityUid Owner => Manager.Owner;
- ///
+ ///
+ /// Should the contents of this container be shown? False for closed containers like lockers, true for
+ /// things like glass display cases.
+ ///
[ViewVariables(VVAccess.ReadWrite)]
[DataField("showEnts")]
public bool ShowContents { get; set; }
- ///
- /// DO NOT CALL THIS METHOD DIRECTLY!
- /// You want