mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Network entity ids (#4252)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
53
Robust.Client/GameObjects/ClientEntityManager.Network.cs
Normal file
53
Robust.Client/GameObjects/ClientEntityManager.Network.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Client.GameObjects;
|
||||
|
||||
public sealed partial class ClientEntityManager
|
||||
{
|
||||
protected override NetEntity GenerateNetEntity() => new(NextNetworkId++ | NetEntity.ClientEntity);
|
||||
|
||||
/// <summary>
|
||||
/// If the client fails to resolve a NetEntity then during component state handling or the likes we
|
||||
/// flag that comp state as requiring re-running if that NetEntity comes in.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal readonly Dictionary<NetEntity, List<(Type type, EntityUid Owner)>> PendingNetEntityStates = new();
|
||||
|
||||
public override bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
// Can't log false because some content code relies on invalid UIDs.
|
||||
if (!MetaQuery.Resolve(uid, ref metadata, false))
|
||||
return false;
|
||||
|
||||
return metadata.NetEntity.IsClientSide();
|
||||
}
|
||||
|
||||
public override EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity)
|
||||
{
|
||||
if (!nEntity.Valid)
|
||||
{
|
||||
return EntityUid.Invalid;
|
||||
}
|
||||
|
||||
if (NetEntityLookup.TryGetValue(nEntity, out var entity))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
// Flag the callerEntity to have their state potentially re-run later.
|
||||
var pending = PendingNetEntityStates.GetOrNew(nEntity);
|
||||
pending.Add((typeof(T), callerEntity));
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
public override EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
var entity = EnsureEntity<T>(netCoordinates.NetEntity, callerEntity);
|
||||
return new EntityCoordinates(entity, netCoordinates.Position);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <summary>
|
||||
/// Manager for entities -- controls things like template loading and instantiation
|
||||
/// </summary>
|
||||
public sealed class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
public sealed partial class ClientEntityManager : EntityManager, IClientEntityManagerInternal
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientNetManager _networkManager = default!;
|
||||
@@ -25,8 +25,6 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
|
||||
|
||||
protected override int NextEntityUid { get; set; } = EntityUid.ClientUid + 1;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetupNetworking();
|
||||
@@ -37,13 +35,16 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void FlushEntities()
|
||||
{
|
||||
// Server doesn't network deletions on client shutdown so we need to
|
||||
// manually clear these out or risk stale data getting used.
|
||||
PendingNetEntityStates.Clear();
|
||||
using var _ = _gameTiming.StartStateApplicationArea();
|
||||
base.FlushEntities();
|
||||
}
|
||||
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, EntityUid uid)
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName)
|
||||
{
|
||||
return base.CreateEntity(prototypeName, uid);
|
||||
return base.CreateEntity(prototypeName);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
@@ -66,7 +67,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
public override void QueueDeleteEntity(EntityUid uid)
|
||||
{
|
||||
if (uid.IsClientSide())
|
||||
if (IsClientSide(uid))
|
||||
{
|
||||
base.QueueDeleteEntity(uid);
|
||||
return;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -5,13 +6,11 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
using static Robust.Shared.Containers.ContainerManagerComponent;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
@@ -23,14 +22,20 @@ namespace Robust.Client.GameObjects
|
||||
[Dependency] private readonly IDynamicTypeFactoryInternal _dynFactory = default!;
|
||||
[Dependency] private readonly PointLightSystem _lightSys = default!;
|
||||
|
||||
private EntityQuery<PointLightComponent> _pointLightQuery;
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
private readonly HashSet<EntityUid> _updateQueue = new();
|
||||
|
||||
public readonly Dictionary<EntityUid, IContainer> ExpectedEntities = new();
|
||||
public readonly Dictionary<NetEntity, BaseContainer> ExpectedEntities = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_pointLightQuery = GetEntityQuery<PointLightComponent>();
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
EntityManager.EntityInitialized += HandleEntityInitialized;
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentHandleState>(HandleComponentState);
|
||||
|
||||
@@ -43,20 +48,18 @@ namespace Robust.Client.GameObjects
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
protected override void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing)
|
||||
protected override void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing)
|
||||
{
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(missing, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(missing));
|
||||
var netEntity = GetNetEntity(missing);
|
||||
DebugTools.Assert(ExpectedEntities.TryGetValue(netEntity, out var expectedContainer) && expectedContainer == cont && cont.ExpectedEntities.Contains(netEntity));
|
||||
}
|
||||
|
||||
private void HandleEntityInitialized(EntityUid uid)
|
||||
{
|
||||
if (!RemoveExpectedEntity(uid, out var container))
|
||||
if (!RemoveExpectedEntity(GetNetEntity(uid), out var container))
|
||||
return;
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(uid);
|
||||
container.Insert(uid, EntityManager, transform: TransformQuery.GetComponent(uid), meta: MetaQuery.GetComponent(uid));
|
||||
}
|
||||
|
||||
private void HandleComponentState(EntityUid uid, ContainerManagerComponent component, ref ComponentHandleState args)
|
||||
@@ -64,23 +67,24 @@ namespace Robust.Client.GameObjects
|
||||
if (args.Current is not ContainerManagerComponentState cast)
|
||||
return;
|
||||
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xform = xformQuery.GetComponent(uid);
|
||||
var xform = TransformQuery.GetComponent(uid);
|
||||
|
||||
// Delete now-gone containers.
|
||||
var toDelete = new ValueList<string>();
|
||||
foreach (var (id, container) in component.Containers)
|
||||
{
|
||||
if (cast.Containers.ContainsKey(id))
|
||||
{
|
||||
DebugTools.Assert(cast.Containers[id].ContainerType == container.GetType().Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var entity in container.ContainedEntities.ToArray())
|
||||
{
|
||||
container.Remove(entity,
|
||||
EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
metaQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true,
|
||||
reparent: false);
|
||||
|
||||
@@ -98,26 +102,32 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
// Add new containers and update existing contents.
|
||||
|
||||
foreach (var (containerType, id, showEnts, occludesLight, entityUids) in cast.Containers.Values)
|
||||
foreach (var (id, data) in cast.Containers)
|
||||
{
|
||||
if (!component.Containers.TryGetValue(id, out var container))
|
||||
{
|
||||
container = ContainerFactory(component, containerType, id);
|
||||
var type = _serializer.FindSerializedType(typeof(BaseContainer), data.ContainerType);
|
||||
container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type!, inject:false);
|
||||
container.Init(id, uid, component);
|
||||
component.Containers.Add(id, container);
|
||||
}
|
||||
|
||||
// sync show flag
|
||||
container.ShowContents = showEnts;
|
||||
container.OccludesLight = occludesLight;
|
||||
DebugTools.Assert(container.ID == id);
|
||||
container.ShowContents = data.ShowContents;
|
||||
container.OccludesLight = data.OccludesLight;
|
||||
|
||||
// Remove gone entities.
|
||||
var toRemove = new ValueList<EntityUid>();
|
||||
|
||||
DebugTools.Assert(!container.Contains(EntityUid.Invalid));
|
||||
|
||||
var stateNetEnts = data.ContainedEntities;
|
||||
var stateEnts = GetEntityArray(stateNetEnts); // No need to ensure entities.
|
||||
|
||||
foreach (var entity in container.ContainedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entity))
|
||||
{
|
||||
if (!stateEnts.Contains(entity))
|
||||
toRemove.Add(entity);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in toRemove)
|
||||
@@ -125,8 +135,8 @@ namespace Robust.Client.GameObjects
|
||||
container.Remove(
|
||||
entity,
|
||||
EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
metaQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true,
|
||||
reparent: false);
|
||||
|
||||
@@ -134,13 +144,11 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Remove entities that were expected, but have been removed from the container.
|
||||
var removedExpected = new ValueList<EntityUid>();
|
||||
foreach (var entityUid in container.ExpectedEntities)
|
||||
var removedExpected = new ValueList<NetEntity>();
|
||||
foreach (var netEntity in container.ExpectedEntities)
|
||||
{
|
||||
if (!entityUids.Contains(entityUid))
|
||||
{
|
||||
removedExpected.Add(entityUid);
|
||||
}
|
||||
if (!stateNetEnts.Contains(netEntity))
|
||||
removedExpected.Add(netEntity);
|
||||
}
|
||||
|
||||
foreach (var entityUid in removedExpected)
|
||||
@@ -149,14 +157,20 @@ namespace Robust.Client.GameObjects
|
||||
}
|
||||
|
||||
// Add new entities.
|
||||
foreach (var entity in entityUids)
|
||||
for (var i = 0; i < stateNetEnts.Length; i++)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(entity, out MetaDataComponent? meta))
|
||||
var entity = stateEnts[i];
|
||||
var netEnt = stateNetEnts[i];
|
||||
if (!entity.IsValid())
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
DebugTools.Assert(netEnt.IsValid());
|
||||
AddExpectedEntity(netEnt, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
var meta = MetaData(entity);
|
||||
DebugTools.Assert(meta.NetEntity == netEnt);
|
||||
|
||||
// If an entity is currently in the shadow realm, it means we probably left PVS and are now getting
|
||||
// back into range. We do not want to directly insert this entity, as IF the container and entity
|
||||
// transform states did not get sent simultaneously, the entity's transform will be modified by the
|
||||
@@ -166,18 +180,18 @@ namespace Robust.Client.GameObjects
|
||||
// containers/players.
|
||||
if ((meta.Flags & MetaDataFlags.Detached) != 0)
|
||||
{
|
||||
AddExpectedEntity(entity, container);
|
||||
AddExpectedEntity(netEnt, container);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (container.Contains(entity))
|
||||
continue;
|
||||
|
||||
RemoveExpectedEntity(entity, out _);
|
||||
RemoveExpectedEntity(netEnt, out _);
|
||||
container.Insert(entity, EntityManager,
|
||||
xformQuery.GetComponent(entity),
|
||||
TransformQuery.GetComponent(entity),
|
||||
xform,
|
||||
metaQuery.GetComponent(entity),
|
||||
MetaQuery.GetComponent(entity),
|
||||
force: true);
|
||||
|
||||
DebugTools.Assert(container.Contains(entity));
|
||||
@@ -198,7 +212,7 @@ namespace Robust.Client.GameObjects
|
||||
if (message.OldParent != null && message.OldParent.Value.IsValid())
|
||||
return;
|
||||
|
||||
if (!RemoveExpectedEntity(message.Entity, out var container))
|
||||
if (!RemoveExpectedEntity(GetNetEntity(message.Entity), out var container))
|
||||
return;
|
||||
|
||||
if (xform.ParentUid != container.Owner)
|
||||
@@ -208,84 +222,69 @@ namespace Robust.Client.GameObjects
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.Deleted)
|
||||
return;
|
||||
|
||||
container.Insert(message.Entity, EntityManager);
|
||||
}
|
||||
|
||||
private IContainer ContainerFactory(ContainerManagerComponent component, string containerType, string id)
|
||||
public void AddExpectedEntity(NetEntity netEntity, BaseContainer container)
|
||||
{
|
||||
var type = _serializer.FindSerializedType(typeof(IContainer), containerType);
|
||||
if (type is null) throw new ArgumentException($"Container of type {containerType} for id {id} cannot be found.");
|
||||
#if DEBUG
|
||||
var uid = GetEntity(netEntity);
|
||||
|
||||
var newContainer = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
|
||||
newContainer.ID = id;
|
||||
newContainer.Manager = component;
|
||||
return newContainer;
|
||||
}
|
||||
if (TryComp<MetaDataComponent>(uid, out var meta))
|
||||
{
|
||||
DebugTools.Assert((meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
|
||||
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
|
||||
}
|
||||
#endif
|
||||
|
||||
public void AddExpectedEntity(EntityUid uid, IContainer container)
|
||||
{
|
||||
DebugTools.Assert(!TryComp(uid, out MetaDataComponent? meta) ||
|
||||
(meta.Flags & ( MetaDataFlags.Detached | MetaDataFlags.InContainer) ) == MetaDataFlags.Detached,
|
||||
$"Adding entity {ToPrettyString(uid)} to list of expected entities for container {container.ID} in {ToPrettyString(container.Owner)}, despite it already being in a container.");
|
||||
|
||||
if (!ExpectedEntities.TryAdd(uid, container))
|
||||
if (!ExpectedEntities.TryAdd(netEntity, container))
|
||||
{
|
||||
// It is possible that we were expecting this entity in one container, but it has now moved to another
|
||||
// container, and this entity's state is just being applied before the old container is getting updated.
|
||||
var oldContainer = ExpectedEntities[uid];
|
||||
ExpectedEntities[uid] = container;
|
||||
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(uid),
|
||||
$"Entity {ToPrettyString(uid)} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
|
||||
oldContainer.ExpectedEntities.Remove(uid);
|
||||
var oldContainer = ExpectedEntities[netEntity];
|
||||
ExpectedEntities[netEntity] = container;
|
||||
DebugTools.Assert(oldContainer.ExpectedEntities.Contains(netEntity),
|
||||
$"Entity {netEntity} is expected, but not expected in the given container? Container: {oldContainer.ID} in {ToPrettyString(oldContainer.Owner)}");
|
||||
oldContainer.ExpectedEntities.Remove(netEntity);
|
||||
}
|
||||
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(uid),
|
||||
$"Contained entity {ToPrettyString(uid)} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Add(uid);
|
||||
DebugTools.Assert(!container.ExpectedEntities.Contains(netEntity),
|
||||
$"Contained entity {netEntity} was not yet expected by the system, but was already expected by the container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Add(netEntity);
|
||||
}
|
||||
|
||||
public bool RemoveExpectedEntity(EntityUid uid, [NotNullWhen(true)] out IContainer? container)
|
||||
public bool RemoveExpectedEntity(NetEntity netEntity, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
if (!ExpectedEntities.Remove(uid, out container))
|
||||
if (!ExpectedEntities.Remove(netEntity, out container))
|
||||
return false;
|
||||
|
||||
DebugTools.Assert(container.ExpectedEntities.Contains(uid),
|
||||
$"While removing expected contained entity {ToPrettyString(uid)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Remove(uid);
|
||||
DebugTools.Assert(container.ExpectedEntities.Contains(netEntity),
|
||||
$"While removing expected contained entity {ToPrettyString(netEntity)}, the entity was missing from the container expected set. Container: {container.ID} in {ToPrettyString(container.Owner)}");
|
||||
container.ExpectedEntities.Remove(netEntity);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
var pointQuery = EntityManager.GetEntityQuery<PointLightComponent>();
|
||||
var spriteQuery = EntityManager.GetEntityQuery<SpriteComponent>();
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var toUpdate in _updateQueue)
|
||||
{
|
||||
if (Deleted(toUpdate))
|
||||
continue;
|
||||
|
||||
UpdateEntityRecursively(toUpdate, xformQuery, pointQuery, spriteQuery);
|
||||
UpdateEntityRecursively(toUpdate);
|
||||
}
|
||||
|
||||
_updateQueue.Clear();
|
||||
}
|
||||
|
||||
private void UpdateEntityRecursively(
|
||||
EntityUid entity,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery)
|
||||
private void UpdateEntityRecursively(EntityUid entity)
|
||||
{
|
||||
// Recursively go up parents and containers to see whether both sprites and lights need to be occluded
|
||||
// Could maybe optimise this more by checking nearest parent that has sprite / light and whether it's container
|
||||
// occluded but this probably isn't a big perf issue.
|
||||
var xform = xformQuery.GetComponent(entity);
|
||||
var xform = TransformQuery.GetComponent(entity);
|
||||
var parent = xform.ParentUid;
|
||||
var child = entity;
|
||||
var spriteOccluded = false;
|
||||
@@ -293,7 +292,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
while (parent.IsValid() && (!spriteOccluded || !lightOccluded))
|
||||
{
|
||||
var parentXform = xformQuery.GetComponent(parent);
|
||||
var parentXform = TransformQuery.GetComponent(parent);
|
||||
if (TryComp<ContainerManagerComponent>(parent, out var manager) && manager.TryGetContainer(child, out var container))
|
||||
{
|
||||
spriteOccluded = spriteOccluded || !container.ShowContents;
|
||||
@@ -308,24 +307,21 @@ namespace Robust.Client.GameObjects
|
||||
// This is the CBT bit.
|
||||
// The issue is we need to go through the children and re-check whether they are or are not contained.
|
||||
// if they are contained then the occlusion values may need updating for all those children
|
||||
UpdateEntity(entity, xform, xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
UpdateEntity(entity, xform, spriteOccluded, lightOccluded);
|
||||
}
|
||||
|
||||
private void UpdateEntity(
|
||||
EntityUid entity,
|
||||
TransformComponent xform,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
EntityQuery<PointLightComponent> pointQuery,
|
||||
EntityQuery<SpriteComponent> spriteQuery,
|
||||
bool spriteOccluded,
|
||||
bool lightOccluded)
|
||||
{
|
||||
if (spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
if (_spriteQuery.TryGetComponent(entity, out var sprite))
|
||||
{
|
||||
sprite.ContainerOccluded = spriteOccluded;
|
||||
}
|
||||
|
||||
if (pointQuery.TryGetComponent(entity, out var light))
|
||||
if (_pointLightQuery.TryGetComponent(entity, out var light))
|
||||
_lightSys.SetContainerOccluded(entity, lightOccluded, light);
|
||||
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
@@ -346,14 +342,14 @@ namespace Robust.Client.GameObjects
|
||||
childLightOccluded = childLightOccluded || container.OccludesLight;
|
||||
}
|
||||
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, childSpriteOccluded, childLightOccluded);
|
||||
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), childSpriteOccluded, childLightOccluded);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (childEnumerator.MoveNext(out var child))
|
||||
{
|
||||
UpdateEntity(child.Value, xformQuery.GetComponent(child.Value), xformQuery, pointQuery, spriteQuery, spriteOccluded, lightOccluded);
|
||||
UpdateEntity(child.Value, TransformQuery.GetComponent(child.Value), spriteOccluded, lightOccluded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace Robust.Client.GameObjects
|
||||
/// <param name="message">Arguments for this event.</param>
|
||||
/// <param name="replay">if true, current cmd state will not be checked or updated - use this for "replaying" an
|
||||
/// old input that was saved or buffered until further processing could be done</param>
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, FullInputCmdMessage message, bool replay = false)
|
||||
public bool HandleInputCommand(ICommonSession? session, BoundKeyFunction function, IFullInputCmdMessage message, bool replay = false)
|
||||
{
|
||||
#if DEBUG
|
||||
|
||||
@@ -78,14 +78,27 @@ namespace Robust.Client.GameObjects
|
||||
continue;
|
||||
|
||||
// local handlers can block sending over the network.
|
||||
if (handler.HandleCmdMessage(session, message))
|
||||
if (handler.HandleCmdMessage(EntityManager, session, message))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// send it off to the server
|
||||
DispatchInputCommand(message);
|
||||
var clientMsg = (ClientFullInputCmdMessage)message;
|
||||
var fullMsg = new FullInputCmdMessage(
|
||||
clientMsg.Tick,
|
||||
clientMsg.SubTick,
|
||||
(int)clientMsg.InputSequence,
|
||||
clientMsg.InputFunctionId,
|
||||
clientMsg.State,
|
||||
GetNetCoordinates(clientMsg.Coordinates),
|
||||
clientMsg.ScreenCoordinates)
|
||||
{
|
||||
Uid = GetNetEntity(clientMsg.Uid)
|
||||
};
|
||||
|
||||
DispatchInputCommand(clientMsg, fullMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -93,7 +106,7 @@ namespace Robust.Client.GameObjects
|
||||
/// Handle a predicted input command.
|
||||
/// </summary>
|
||||
/// <param name="inputCmd">Input command to handle as predicted.</param>
|
||||
public void PredictInputCommand(FullInputCmdMessage inputCmd)
|
||||
public void PredictInputCommand(IFullInputCmdMessage inputCmd)
|
||||
{
|
||||
DebugTools.AssertNotNull(_playerManager.LocalPlayer);
|
||||
|
||||
@@ -103,15 +116,16 @@ namespace Robust.Client.GameObjects
|
||||
var session = _playerManager.LocalPlayer!.Session;
|
||||
foreach (var handler in BindRegistry.GetHandlers(keyFunc))
|
||||
{
|
||||
if (handler.HandleCmdMessage(session, inputCmd)) break;
|
||||
if (handler.HandleCmdMessage(EntityManager, session, inputCmd))
|
||||
break;
|
||||
}
|
||||
Predicted = false;
|
||||
|
||||
}
|
||||
|
||||
private void DispatchInputCommand(FullInputCmdMessage message)
|
||||
private void DispatchInputCommand(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message)
|
||||
{
|
||||
_stateManager.InputCommandDispatched(message);
|
||||
_stateManager.InputCommandDispatched(clientMsg, message);
|
||||
EntityManager.EntityNetManager?.SendSystemNetworkMessage(message, message.InputSequence);
|
||||
}
|
||||
|
||||
@@ -152,7 +166,7 @@ namespace Robust.Client.GameObjects
|
||||
var funcId = _inputManager.NetworkBindMap.KeyFunctionID(keyFunction);
|
||||
|
||||
var message = new FullInputCmdMessage(_timing.CurTick, _timing.TickFraction, funcId, state,
|
||||
coords, new ScreenCoordinates(0, 0, default), EntityUid.Invalid);
|
||||
GetNetCoordinates(coords), new ScreenCoordinates(0, 0, default), NetEntity.Invalid);
|
||||
|
||||
HandleInputCommand(localPlayer.Session, keyFunction, message);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,8 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
private void MessageReceived(BoundUIWrapMessage ev)
|
||||
{
|
||||
var uid = ev.Entity;
|
||||
var uid = GetEntity(ev.Entity);
|
||||
|
||||
if (!TryComp<ClientUserInterfaceComponent>(uid, out var cmp))
|
||||
return;
|
||||
|
||||
@@ -53,7 +54,7 @@ namespace Robust.Client.GameObjects
|
||||
if(_playerManager.LocalPlayer != null)
|
||||
message.Session = _playerManager.LocalPlayer.Session;
|
||||
|
||||
message.Entity = uid;
|
||||
message.Entity = GetNetEntity(uid);
|
||||
message.UiKey = uiKey;
|
||||
|
||||
// Raise as object so the correct type is used.
|
||||
@@ -125,7 +126,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
internal void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage msg)
|
||||
{
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, msg, bui.UiKey));
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), msg, bui.UiKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IClientGameTiming _timing = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
|
||||
|
||||
// Entities that have removed networked components
|
||||
// could pool the ushort sets, but predicted component changes are rare... soo...
|
||||
internal readonly Dictionary<EntityUid, HashSet<ushort>> RemovedComponents = new();
|
||||
@@ -40,11 +40,11 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnTerminate(ref EntityTerminatingEvent ev)
|
||||
{
|
||||
if (!_timing.InPrediction || ev.Entity.IsClientSide())
|
||||
if (!_timing.InPrediction || IsClientSide(ev.Entity))
|
||||
return;
|
||||
|
||||
// Client-side entity deletion is not supported and will cause errors.
|
||||
Logger.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
Log.Error($"Predicting the deletion of a networked entity: {ToPrettyString(ev.Entity)}. Trace: {Environment.StackTrace}");
|
||||
}
|
||||
|
||||
private void OnCompRemoved(RemovedComponentEventArgs args)
|
||||
@@ -52,8 +52,9 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
if (args.Terminating)
|
||||
return;
|
||||
|
||||
var uid = args.BaseArgs.Owner;
|
||||
var comp = args.BaseArgs.Component;
|
||||
if (!_timing.InPrediction || comp.Owner.IsClientSide() || !comp.NetSyncEnabled)
|
||||
if (!_timing.InPrediction || !comp.NetSyncEnabled || IsClientSide(uid))
|
||||
return;
|
||||
|
||||
// Was this component added during prediction? If yes, then there is no need to re-add it when resetting.
|
||||
@@ -62,7 +63,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
var netId = _compFact.GetRegistration(comp).NetID;
|
||||
if (netId != null)
|
||||
RemovedComponents.GetOrNew(comp.Owner).Add(netId.Value);
|
||||
RemovedComponents.GetOrNew(uid).Add(netId.Value);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
@@ -73,7 +74,7 @@ public sealed class ClientDirtySystem : EntitySystem
|
||||
|
||||
private void OnEntityDirty(EntityUid e)
|
||||
{
|
||||
if (_timing.InPrediction && !e.IsClientSide())
|
||||
if (_timing.InPrediction && !IsClientSide(e))
|
||||
DirtyEntities.Add(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +47,10 @@ namespace Robust.Client.GameStates
|
||||
= new();
|
||||
|
||||
// Game state dictionaries that get used every tick.
|
||||
private readonly Dictionary<EntityUid, (bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState?nextState)> _toApply = new();
|
||||
private readonly Dictionary<EntityUid, EntityState> _toCreate = new();
|
||||
private readonly Dictionary<EntityUid, (bool EnteringPvs, GameTick LastApplied, EntityState? curState, EntityState? nextState)> _toApply = new();
|
||||
private readonly Dictionary<NetEntity, EntityState> _toCreate = new();
|
||||
private readonly Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)> _compStateWork = new();
|
||||
private readonly Dictionary<EntityUid, HashSet<Type>> _pendingReapplyNetStates = new();
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
@@ -157,7 +159,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public void InputCommandDispatched(FullInputCmdMessage message)
|
||||
public void InputCommandDispatched(ClientFullInputCmdMessage clientMessage, FullInputCmdMessage message)
|
||||
{
|
||||
if (!IsPredictionEnabled)
|
||||
{
|
||||
@@ -201,7 +203,7 @@ namespace Robust.Client.GameStates
|
||||
public void UpdateFullRep(GameState state, bool cloneDelta = false)
|
||||
=> _processor.UpdateFullRep(state, cloneDelta);
|
||||
|
||||
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
=> _processor.GetFullRep();
|
||||
|
||||
private void HandlePvsLeaveMessage(MsgStateLeavePvs message)
|
||||
@@ -210,7 +212,7 @@ namespace Robust.Client.GameStates
|
||||
PvsLeave?.Invoke(message);
|
||||
}
|
||||
|
||||
public void QueuePvsDetach(List<EntityUid> entities, GameTick tick)
|
||||
public void QueuePvsDetach(List<NetEntity> entities, GameTick tick)
|
||||
{
|
||||
_processor.AddLeavePvsMessage(entities, tick);
|
||||
if (_replayRecording.IsRecording)
|
||||
@@ -298,7 +300,7 @@ namespace Robust.Client.GameStates
|
||||
_processor.UpdateFullRep(curState);
|
||||
}
|
||||
|
||||
IEnumerable<EntityUid> createdEntities;
|
||||
IEnumerable<NetEntity> createdEntities;
|
||||
using (_prof.Group("ApplyGameState"))
|
||||
{
|
||||
if (_timing.LastProcessedTick < targetProcessedTick && nextState != null)
|
||||
@@ -323,7 +325,7 @@ namespace Robust.Client.GameStates
|
||||
catch (MissingMetadataException e)
|
||||
{
|
||||
// Something has gone wrong. Probably a missing meta-data component. Perhaps a full server state will fix it.
|
||||
RequestFullState(e.Uid);
|
||||
RequestFullState(e.NetEntity);
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
@@ -392,10 +394,10 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public void RequestFullState(EntityUid? missingEntity = null)
|
||||
public void RequestFullState(NetEntity? missingEntity = null)
|
||||
{
|
||||
_sawmill.Info("Requesting full server state");
|
||||
_network.ClientSendMessage(new MsgStateRequestFull { Tick = _timing.LastRealTick , MissingEntity = missingEntity ?? EntityUid.Invalid });
|
||||
_network.ClientSendMessage(new MsgStateRequestFull { Tick = _timing.LastRealTick , MissingEntity = missingEntity ?? NetEntity.Invalid });
|
||||
_processor.RequestFullState();
|
||||
}
|
||||
|
||||
@@ -472,7 +474,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
var countReset = 0;
|
||||
var system = _entitySystemManager.GetEntitySystem<ClientDirtySystem>();
|
||||
var query = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
RemQueue<Component> toRemove = new();
|
||||
|
||||
// This is terrible, and I hate it.
|
||||
@@ -486,7 +488,8 @@ namespace Robust.Client.GameStates
|
||||
if (_sawmill.Level <= LogLevel.Debug)
|
||||
_sawmill.Debug($"Entity {entity} was made dirty.");
|
||||
|
||||
if (!_processor.TryGetLastServerStates(entity, out var last))
|
||||
if (!metaQuery.TryGetComponent(entity, out var meta) ||
|
||||
!_processor.TryGetLastServerStates(meta.NetEntity, out var last))
|
||||
{
|
||||
// Entity was probably deleted on the server so do nothing.
|
||||
continue;
|
||||
@@ -564,7 +567,6 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
var meta = query.GetComponent(entity);
|
||||
DebugTools.Assert(meta.EntityLastModifiedTick > _timing.LastRealTick);
|
||||
meta.EntityLastModifiedTick = _timing.LastRealTick;
|
||||
}
|
||||
@@ -588,15 +590,16 @@ namespace Robust.Client.GameStates
|
||||
/// initial server state for any newly created entity. It does this by simply using the standard <see
|
||||
/// cref="IEntityManager.GetComponentState(IEventBus, IComponent)"/>.
|
||||
/// </remarks>
|
||||
private void MergeImplicitData(IEnumerable<EntityUid> createdEntities)
|
||||
private void MergeImplicitData(IEnumerable<NetEntity> createdEntities)
|
||||
{
|
||||
var outputData = new Dictionary<EntityUid, Dictionary<ushort, ComponentState>>();
|
||||
var outputData = new Dictionary<NetEntity, Dictionary<ushort, ComponentState>>();
|
||||
var bus = _entityManager.EventBus;
|
||||
|
||||
foreach (var createdEntity in createdEntities)
|
||||
foreach (var netEntity in createdEntities)
|
||||
{
|
||||
var createdEntity = _entityManager.GetEntity(netEntity);
|
||||
var compData = new Dictionary<ushort, ComponentState>();
|
||||
outputData.Add(createdEntity, compData);
|
||||
outputData.Add(netEntity, compData);
|
||||
|
||||
foreach (var (netId, component) in _entityManager.GetNetComponents(createdEntity))
|
||||
{
|
||||
@@ -617,7 +620,7 @@ namespace Robust.Client.GameStates
|
||||
_network.ClientSendMessage(new MsgStateAck() { Sequence = sequence });
|
||||
}
|
||||
|
||||
public IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState)
|
||||
public IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState)
|
||||
{
|
||||
using var _ = _timing.StartStateApplicationArea();
|
||||
|
||||
@@ -637,7 +640,7 @@ namespace Robust.Client.GameStates
|
||||
_config.TickProcessMessages();
|
||||
}
|
||||
|
||||
(IEnumerable<EntityUid> Created, List<EntityUid> Detached) output;
|
||||
(IEnumerable<NetEntity> Created, List<NetEntity> Detached) output;
|
||||
using (_prof.Group("Entity"))
|
||||
{
|
||||
output = ApplyEntityStates(curState, nextState);
|
||||
@@ -656,7 +659,7 @@ namespace Robust.Client.GameStates
|
||||
return output.Created;
|
||||
}
|
||||
|
||||
private (IEnumerable<EntityUid> Created, List<EntityUid> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
private (IEnumerable<NetEntity> Created, List<NetEntity> Detached) ApplyEntityStates(GameState curState, GameState? nextState)
|
||||
{
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
@@ -665,6 +668,7 @@ namespace Robust.Client.GameStates
|
||||
var enteringPvs = 0;
|
||||
_toApply.Clear();
|
||||
_toCreate.Clear();
|
||||
_pendingReapplyNetStates.Clear();
|
||||
var curSpan = curState.EntityStates.Span;
|
||||
|
||||
// Create new entities
|
||||
@@ -675,21 +679,40 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var es in curSpan)
|
||||
{
|
||||
if (metas.HasComponent(es.Uid))
|
||||
if (_entityManager.TryGetEntity(es.NetEntity, out var nUid))
|
||||
{
|
||||
DebugTools.Assert(_entityManager.EntityExists(nUid));
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
var uid = es.Uid;
|
||||
var metaState = (MetaDataComponentState?)es.ComponentChanges.Value?.FirstOrDefault(c => c.NetID == _metaCompNetId).State;
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(uid);
|
||||
throw new MissingMetadataException(es.NetEntity);
|
||||
|
||||
_entities.CreateEntity(metaState.PrototypeId, uid);
|
||||
_toCreate.Add(uid, es);
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId);
|
||||
_toCreate.Add(es.NetEntity, es);
|
||||
_toApply.Add(uid, (false, GameTick.Zero, es, null));
|
||||
|
||||
var newMeta = metas.GetComponent(uid);
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
_entityManager.ClearNetEntity(newMeta.NetEntity);
|
||||
|
||||
_entityManager.SetNetEntity(uid, es.NetEntity, newMeta);
|
||||
newMeta.LastStateApplied = curState.ToSequence;
|
||||
|
||||
// Check if there's any component states awaiting this entity.
|
||||
if (_entityManager.PendingNetEntityStates.TryGetValue(es.NetEntity, out var value))
|
||||
{
|
||||
foreach (var (type, owner) in value)
|
||||
{
|
||||
var pending = _pendingReapplyNetStates.GetOrNew(owner);
|
||||
pending.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(count));
|
||||
@@ -697,7 +720,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var es in curSpan)
|
||||
{
|
||||
if (!metas.TryGetComponent(es.Uid, out var meta) || _toCreate.ContainsKey(es.Uid))
|
||||
var uid = _entityManager.GetEntity(es.NetEntity);
|
||||
|
||||
if (!metas.TryGetComponent(uid, out var meta) || _toCreate.ContainsKey(es.NetEntity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -714,7 +739,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
_toApply.Add(es.Uid, (isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
_toApply.Add(uid, (isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
meta.LastStateApplied = curState.ToSequence;
|
||||
}
|
||||
|
||||
@@ -728,22 +753,31 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var es in nextState.EntityStates.Span)
|
||||
{
|
||||
var uid = es.Uid;
|
||||
|
||||
if (!metas.TryGetComponent(uid, out var meta))
|
||||
if (!_entityManager.TryGetEntity(es.NetEntity, out var uid))
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(metas.HasComponent(uid));
|
||||
|
||||
// Does the next state actually have any future information about this entity that could be used for interpolation?
|
||||
if (es.EntityLastModified != nextState.ToSequence)
|
||||
continue;
|
||||
|
||||
if (_toApply.TryGetValue(uid, out var state))
|
||||
_toApply[uid] = (state.EnteringPvs, state.LastApplied, state.curState, es);
|
||||
if (_toApply.TryGetValue(uid.Value, out var state))
|
||||
_toApply[uid.Value] = (state.EnteringPvs, state.LastApplied, state.curState, es);
|
||||
else
|
||||
_toApply[uid] = (false, GameTick.Zero, null, es);
|
||||
_toApply[uid.Value] = (false, GameTick.Zero, null, es);
|
||||
}
|
||||
}
|
||||
|
||||
// Check pending states and see if we need to force any entities to re-run component states.
|
||||
foreach (var uid in _pendingReapplyNetStates.Keys)
|
||||
{
|
||||
if (_toApply.ContainsKey(uid))
|
||||
continue;
|
||||
|
||||
_toApply[uid] = (false, GameTick.Zero, null, null);
|
||||
}
|
||||
|
||||
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
|
||||
|
||||
// Apply entity states.
|
||||
@@ -766,6 +800,7 @@ namespace Robust.Client.GameStates
|
||||
if (!_toApply.TryGetValue(xform.ParentUid, out var parent) || !parent.EnteringPvs)
|
||||
queuedBroadphaseUpdates.Add((entity, xform));
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(_toApply.Count));
|
||||
}
|
||||
|
||||
@@ -829,37 +864,37 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// Construct hashset for set.Contains() checks.
|
||||
var entityStates = state.EntityStates.Span;
|
||||
var stateEnts = new HashSet<EntityUid>(entityStates.Length);
|
||||
var stateEnts = new HashSet<NetEntity>();
|
||||
foreach (var entState in entityStates)
|
||||
{
|
||||
stateEnts.Add(entState.Uid);
|
||||
stateEnts.Add(entState.NetEntity);
|
||||
}
|
||||
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
var currentEnts = _entities.GetEntities();
|
||||
var toDelete = new List<EntityUid>(Math.Max(64, _entities.EntityCount - stateEnts.Count));
|
||||
foreach (var ent in currentEnts)
|
||||
|
||||
// Client side entities won't need the transform, but that should always be a tiny minority of entities
|
||||
var metaQuery = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent>();
|
||||
|
||||
while (metaQuery.MoveNext(out var ent, out var metadata, out var xform))
|
||||
{
|
||||
if (ent.IsClientSide())
|
||||
var netEnt = metadata.NetEntity;
|
||||
if (metadata.NetEntity.IsClientSide())
|
||||
{
|
||||
if (deleteClientEntities)
|
||||
toDelete.Add(ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stateEnts.Contains(ent) && metas.TryGetComponent(ent, out var meta))
|
||||
if (stateEnts.Contains(netEnt))
|
||||
{
|
||||
if (resetAllEntities || meta.LastStateApplied > state.ToSequence)
|
||||
meta.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
if (resetAllEntities || metadata.LastStateApplied > state.ToSequence)
|
||||
metadata.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!xforms.TryGetComponent(ent, out var xform))
|
||||
continue;
|
||||
|
||||
// This entity is going to get deleted, but maybe some if its children won't be, so lets detach them to
|
||||
// null. First we will detach the parent in order to reduce the number of broadphase/lookup updates.
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
@@ -872,7 +907,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
if (deleteClientChildren
|
||||
&& !deleteClientEntities // don't add duplicates
|
||||
&& child.Value.IsClientSide())
|
||||
&& _entities.IsClientSide(child.Value))
|
||||
{
|
||||
toDelete.Add(child.Value);
|
||||
}
|
||||
@@ -887,7 +922,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
private void ProcessDeletions(
|
||||
ReadOnlySpan<EntityUid> delSpan,
|
||||
ReadOnlySpan<NetEntity> delSpan,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
SharedTransformSystem xformSys)
|
||||
@@ -904,13 +939,19 @@ namespace Robust.Client.GameStates
|
||||
|
||||
using var _ = _prof.Group("Deletion");
|
||||
|
||||
foreach (var id in delSpan)
|
||||
foreach (var netEntity in delSpan)
|
||||
{
|
||||
// Don't worry about this for later.
|
||||
_entityManager.PendingNetEntityStates.Remove(netEntity);
|
||||
|
||||
if (!_entityManager.TryGetEntity(netEntity, out var id))
|
||||
continue;
|
||||
|
||||
if (!xforms.TryGetComponent(id, out var xform))
|
||||
continue; // Already deleted? or never sent to us?
|
||||
|
||||
// First, a single recursive map change
|
||||
xformSys.DetachParentToNull(id, xform);
|
||||
xformSys.DetachParentToNull(id.Value, xform);
|
||||
|
||||
// Then detach all children.
|
||||
var childEnumerator = xform.ChildEnumerator;
|
||||
@@ -920,12 +961,12 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
// Finally, delete the entity.
|
||||
_entities.DeleteEntity(id);
|
||||
_entities.DeleteEntity(id.Value);
|
||||
}
|
||||
_prof.WriteValue("Count", ProfData.Int32(delSpan.Length));
|
||||
}
|
||||
|
||||
public void DetachImmediate(List<EntityUid> entities)
|
||||
public void DetachImmediate(List<NetEntity> entities)
|
||||
{
|
||||
var metas = _entities.GetEntityQuery<MetaDataComponent>();
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
@@ -935,7 +976,7 @@ namespace Robust.Client.GameStates
|
||||
Detach(GameTick.MaxValue, null, entities, metas, xforms, xformSys, containerSys, lookupSys);
|
||||
}
|
||||
|
||||
private List<EntityUid> ProcessPvsDeparture(
|
||||
private List<NetEntity> ProcessPvsDeparture(
|
||||
GameTick toTick,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
@@ -944,7 +985,7 @@ namespace Robust.Client.GameStates
|
||||
EntityLookupSystem lookupSys)
|
||||
{
|
||||
var toDetach = _processor.GetEntitiesToDetach(toTick, _pvsDetachBudget);
|
||||
var detached = new List<EntityUid>();
|
||||
var detached = new List<NetEntity>();
|
||||
|
||||
if (toDetach.Count == 0)
|
||||
return detached;
|
||||
@@ -966,16 +1007,18 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private void Detach(GameTick maxTick,
|
||||
GameTick? lastStateApplied,
|
||||
List<EntityUid> entities,
|
||||
List<NetEntity> entities,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
SharedTransformSystem xformSys,
|
||||
ContainerSystem containerSys,
|
||||
EntityLookupSystem lookupSys,
|
||||
List<EntityUid>? detached = null)
|
||||
List<NetEntity>? detached = null)
|
||||
{
|
||||
foreach (var ent in entities)
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
var ent = _entityManager.GetEntity(netEntity);
|
||||
|
||||
if (!metas.TryGetComponent(ent, out var meta))
|
||||
continue;
|
||||
|
||||
@@ -1000,7 +1043,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
|
||||
// In those situations, we need to add the entity back to the list of expected entities after detaching.
|
||||
IContainer? container = null;
|
||||
BaseContainer? container = null;
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
|
||||
@@ -1014,35 +1057,38 @@ namespace Robust.Client.GameStates
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
|
||||
if (container != null)
|
||||
containerSys.AddExpectedEntity(ent, container);
|
||||
containerSys.AddExpectedEntity(netEntity, container);
|
||||
}
|
||||
|
||||
detached?.Add(ent);
|
||||
detached?.Add(netEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeAndStart(Dictionary<EntityUid, EntityState> toCreate)
|
||||
private void InitializeAndStart(Dictionary<NetEntity, EntityState> toCreate)
|
||||
{
|
||||
var metaQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
HashSet<EntityUid> brokenEnts = new HashSet<EntityUid>();
|
||||
var brokenEnts = new List<EntityUid>();
|
||||
#endif
|
||||
using (_prof.Group("Initialize Entity"))
|
||||
{
|
||||
foreach (var entity in toCreate.Keys)
|
||||
foreach (var netEntity in toCreate.Keys)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
#endif
|
||||
_entities.InitializeEntity(entity);
|
||||
_entities.InitializeEntity(entity, metaQuery.GetComponent(entity));
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_sawmill.Error($"Server entity threw in Init: ent={_entityManager.ToPrettyString(entity)}");
|
||||
_sawmill.Error($"Server entity threw in Init: ent={_entities.ToPrettyString(entity)}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
|
||||
brokenEnts.Add(entity);
|
||||
toCreate.Remove(entity);
|
||||
toCreate.Remove(netEntity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1050,8 +1096,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
using (_prof.Group("Start Entity"))
|
||||
{
|
||||
foreach (var entity in toCreate.Keys)
|
||||
foreach (var netEntity in toCreate.Keys)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(netEntity);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
try
|
||||
{
|
||||
@@ -1064,7 +1111,7 @@ namespace Robust.Client.GameStates
|
||||
_sawmill.Error($"Server entity threw in Start: ent={_entityManager.ToPrettyString(entity)}");
|
||||
_runtimeLog.LogException(e, $"{nameof(ClientGameStateManager)}.{nameof(InitializeAndStart)}");
|
||||
brokenEnts.Add(entity);
|
||||
toCreate.Remove(entity);
|
||||
toCreate.Remove(netEntity);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1081,8 +1128,9 @@ namespace Robust.Client.GameStates
|
||||
private void HandleEntityState(EntityUid uid, IEventBus bus, EntityState? curState,
|
||||
EntityState? nextState, GameTick lastApplied, GameTick toTick, bool enteringPvs)
|
||||
{
|
||||
var size = (curState?.ComponentChanges.Span.Length ?? 0) + (nextState?.ComponentChanges.Span.Length ?? 0);
|
||||
var compStateWork = new Dictionary<ushort, (IComponent Component, ComponentState? curState, ComponentState? nextState)>(size);
|
||||
_compStateWork.Clear();
|
||||
var meta = _entityManager.GetComponent<MetaDataComponent>(uid);
|
||||
var netEntity = meta.NetEntity;
|
||||
|
||||
// First remove any deleted components
|
||||
if (curState?.NetComponents != null)
|
||||
@@ -1107,7 +1155,7 @@ namespace Robust.Client.GameStates
|
||||
//
|
||||
// as to why we need to reset: because in the process of detaching to null-space, we will have dirtied
|
||||
// the entity. most notably, all entities will have been ejected from their containers.
|
||||
foreach (var (id, state) in _processor.GetLastServerStates(uid))
|
||||
foreach (var (id, state) in _processor.GetLastServerStates(netEntity))
|
||||
{
|
||||
if (!_entityManager.TryGetComponent(uid, id, out var comp))
|
||||
{
|
||||
@@ -1117,7 +1165,7 @@ namespace Robust.Client.GameStates
|
||||
_entityManager.AddComponent(uid, newComp, true);
|
||||
}
|
||||
|
||||
compStateWork[id] = (comp, state, null);
|
||||
_compStateWork[id] = (comp, state, null);
|
||||
}
|
||||
}
|
||||
else if (curState != null)
|
||||
@@ -1134,7 +1182,7 @@ namespace Robust.Client.GameStates
|
||||
else if (compChange.LastModifiedTick <= lastApplied && lastApplied != GameTick.Zero)
|
||||
continue;
|
||||
|
||||
compStateWork[compChange.NetID] = (comp, compChange.State, null);
|
||||
_compStateWork[compChange.NetID] = (comp, compChange.State, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1152,14 +1200,38 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
if (compStateWork.TryGetValue(compState.NetID, out var state))
|
||||
compStateWork[compState.NetID] = (comp, state.curState, compState.State);
|
||||
if (_compStateWork.TryGetValue(compState.NetID, out var state))
|
||||
_compStateWork[compState.NetID] = (comp, state.curState, compState.State);
|
||||
else
|
||||
compStateWork[compState.NetID] = (comp, null, compState.State);
|
||||
_compStateWork[compState.NetID] = (comp, null, compState.State);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (comp, cur, next) in compStateWork.Values)
|
||||
// If we have a NetEntity we reference come in then apply their state.
|
||||
if (_pendingReapplyNetStates.TryGetValue(uid, out var reapplyTypes))
|
||||
{
|
||||
var lastState = _processor.GetLastServerStates(netEntity);
|
||||
|
||||
foreach (var type in reapplyTypes)
|
||||
{
|
||||
var compRef = _compFactory.GetRegistration(type);
|
||||
var netId = compRef.NetID;
|
||||
|
||||
if (netId == null)
|
||||
continue;
|
||||
|
||||
if (_compStateWork.ContainsKey(netId.Value) ||
|
||||
!_entityManager.TryGetComponent(uid, type, out var comp) ||
|
||||
!lastState.TryGetValue(netId.Value, out var lastCompState))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_compStateWork[netId.Value] = (comp, lastCompState, null);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (comp, cur, next) in _compStateWork.Values)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1180,6 +1252,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
#region Debug Commands
|
||||
|
||||
private bool TryParseUid(IConsoleShell shell, string[] args, out EntityUid uid, [NotNullWhen(true)] out MetaDataComponent? meta)
|
||||
{
|
||||
if (args.Length != 1)
|
||||
@@ -1238,7 +1311,7 @@ namespace Robust.Client.GameStates
|
||||
var xform = _entities.GetComponent<TransformComponent>(uid);
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
IContainer? container = null;
|
||||
BaseContainer? container = null;
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
_entities.TryGetComponent(xform.ParentUid, out MetaDataComponent? containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0)
|
||||
@@ -1249,7 +1322,7 @@ namespace Robust.Client.GameStates
|
||||
_entities.EntitySysManager.GetEntitySystem<TransformSystem>().DetachParentToNull(uid, xform);
|
||||
|
||||
if (container != null)
|
||||
containerSys.AddExpectedEntity(uid, container);
|
||||
containerSys.AddExpectedEntity(_entities.GetNetEntity(uid), container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1266,18 +1339,21 @@ namespace Robust.Client.GameStates
|
||||
|
||||
// If this is not a client-side entity, it also needs to be removed from the full-server state dictionary to
|
||||
// avoid errors. This has to be done recursively for all children.
|
||||
void _recursiveRemoveState(TransformComponent xform, EntityQuery<TransformComponent> query)
|
||||
void _recursiveRemoveState(NetEntity netEntity, TransformComponent xform, EntityQuery<MetaDataComponent> metaQuery, EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
_processor._lastStateFullRep.Remove(xform.Owner);
|
||||
_processor._lastStateFullRep.Remove(netEntity);
|
||||
foreach (var child in xform.ChildEntities)
|
||||
{
|
||||
if (query.TryGetComponent(child, out var childXform))
|
||||
_recursiveRemoveState(childXform, query);
|
||||
if (xformQuery.TryGetComponent(child, out var childXform) &&
|
||||
metaQuery.TryGetComponent(child, out var childMeta))
|
||||
{
|
||||
_recursiveRemoveState(childMeta.NetEntity, childXform, metaQuery, xformQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!uid.IsClientSide() && _entities.TryGetComponent(uid, out TransformComponent? xform))
|
||||
_recursiveRemoveState(xform, _entities.GetEntityQuery<TransformComponent>());
|
||||
if (!_entities.IsClientSide(uid) && _entities.TryGetComponent(uid, out TransformComponent? xform))
|
||||
_recursiveRemoveState(meta.NetEntity, xform, _entities.GetEntityQuery<MetaDataComponent>(), _entities.GetEntityQuery<TransformComponent>());
|
||||
|
||||
// Set ApplyingState to true to avoid logging errors about predicting the deletion of networked entities.
|
||||
using (_timing.StartStateApplicationArea())
|
||||
@@ -1311,7 +1387,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
meta.Flags &= ~MetaDataFlags.Detached;
|
||||
|
||||
if (!_processor.TryGetLastServerStates(uid, out var lastState))
|
||||
if (!_processor.TryGetLastServerStates(meta.NetEntity, out var lastState))
|
||||
return;
|
||||
|
||||
foreach (var (id, state) in lastState)
|
||||
@@ -1352,9 +1428,9 @@ namespace Robust.Client.GameStates
|
||||
public sealed class GameStateAppliedArgs : EventArgs
|
||||
{
|
||||
public GameState AppliedState { get; }
|
||||
public readonly List<EntityUid> Detached;
|
||||
public readonly List<NetEntity> Detached;
|
||||
|
||||
public GameStateAppliedArgs(GameState appliedState, List<EntityUid> detached)
|
||||
public GameStateAppliedArgs(GameState appliedState, List<NetEntity> detached)
|
||||
{
|
||||
AppliedState = appliedState;
|
||||
Detached = detached;
|
||||
@@ -1363,12 +1439,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public sealed class MissingMetadataException : Exception
|
||||
{
|
||||
public readonly EntityUid Uid;
|
||||
public readonly NetEntity NetEntity;
|
||||
|
||||
public MissingMetadataException(EntityUid uid)
|
||||
: base($"Server state is missing the metadata component for a new entity: {uid}.")
|
||||
public MissingMetadataException(NetEntity netEntity)
|
||||
: base($"Server state is missing the metadata component for a new entity: {netEntity}.")
|
||||
{
|
||||
Uid = uid;
|
||||
NetEntity = netEntity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly List<GameState> _stateBuffer = new();
|
||||
|
||||
private readonly Dictionary<GameTick, List<EntityUid>> _pvsDetachMessages = new();
|
||||
private readonly Dictionary<GameTick, List<NetEntity>> _pvsDetachMessages = new();
|
||||
|
||||
private ISawmill _logger = default!;
|
||||
private ISawmill _stateLogger = default!;
|
||||
@@ -44,7 +44,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// This dictionary stores the full most recently received server state of any entity. This is used whenever predicted entities get reset.
|
||||
/// </summary>
|
||||
internal readonly Dictionary<EntityUid, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
internal readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _lastStateFullRep
|
||||
= new();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -178,10 +178,10 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var entityState in state.EntityStates.Span)
|
||||
{
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.Uid, out var compData))
|
||||
if (!_lastStateFullRep.TryGetValue(entityState.NetEntity, out var compData))
|
||||
{
|
||||
compData = new Dictionary<ushort, ComponentState>();
|
||||
_lastStateFullRep.Add(entityState.Uid, compData);
|
||||
_lastStateFullRep.Add(entityState.NetEntity, compData);
|
||||
}
|
||||
|
||||
foreach (var change in entityState.ComponentChanges.Span)
|
||||
@@ -263,7 +263,7 @@ namespace Robust.Client.GameStates
|
||||
return false;
|
||||
}
|
||||
|
||||
internal void AddLeavePvsMessage(List<EntityUid> entities, GameTick tick)
|
||||
internal void AddLeavePvsMessage(List<NetEntity> entities, GameTick tick)
|
||||
{
|
||||
// Late message may still need to be processed,
|
||||
DebugTools.Assert(entities.Count > 0);
|
||||
@@ -272,9 +272,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
public void ClearDetachQueue() => _pvsDetachMessages.Clear();
|
||||
|
||||
public List<(GameTick Tick, List<EntityUid> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
|
||||
public List<(GameTick Tick, List<NetEntity> Entities)> GetEntitiesToDetach(GameTick toTick, int budget)
|
||||
{
|
||||
var result = new List<(GameTick Tick, List<EntityUid> Entities)>();
|
||||
var result = new List<(GameTick Tick, List<NetEntity> Entities)>();
|
||||
foreach (var (tick, entities) in _pvsDetachMessages)
|
||||
{
|
||||
if (tick > toTick)
|
||||
@@ -353,11 +353,11 @@ namespace Robust.Client.GameStates
|
||||
LastFullStateRequested = _timing.LastRealTick;
|
||||
}
|
||||
|
||||
public void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> implicitData)
|
||||
public void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> implicitData)
|
||||
{
|
||||
foreach (var (uid, implicitEntState) in implicitData)
|
||||
foreach (var (netEntity, implicitEntState) in implicitData)
|
||||
{
|
||||
var fullRep = _lastStateFullRep[uid];
|
||||
var fullRep = _lastStateFullRep[netEntity];
|
||||
|
||||
foreach (var (netId, implicitCompState) in implicitEntState)
|
||||
{
|
||||
@@ -374,7 +374,7 @@ namespace Robust.Client.GameStates
|
||||
// state from the entity prototype.
|
||||
if (implicitCompState is not IComponentDeltaState implicitDelta || !implicitDelta.FullState)
|
||||
{
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {uid}");
|
||||
_logger.Error($"Server sent delta state and client failed to construct an implicit full state for entity {netEntity}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -385,17 +385,17 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity)
|
||||
public Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity netEntity)
|
||||
{
|
||||
return _lastStateFullRep[entity];
|
||||
return _lastStateFullRep[netEntity];
|
||||
}
|
||||
|
||||
public Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
public Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep()
|
||||
{
|
||||
return _lastStateFullRep;
|
||||
}
|
||||
|
||||
public bool TryGetLastServerStates(EntityUid entity,
|
||||
public bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary)
|
||||
{
|
||||
return _lastStateFullRep.TryGetValue(entity, out dictionary);
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Applies a given set of game states.
|
||||
/// </summary>
|
||||
IEnumerable<EntityUid> ApplyGameState(GameState curState, GameState? nextState);
|
||||
IEnumerable<NetEntity> ApplyGameState(GameState curState, GameState? nextState);
|
||||
|
||||
/// <summary>
|
||||
/// Resets any entities that have changed while predicting future ticks.
|
||||
@@ -86,12 +86,12 @@ namespace Robust.Client.GameStates
|
||||
/// An input command has been dispatched.
|
||||
/// </summary>
|
||||
/// <param name="message">Message being dispatched.</param>
|
||||
void InputCommandDispatched(FullInputCmdMessage message);
|
||||
void InputCommandDispatched(ClientFullInputCmdMessage clientMsg, FullInputCmdMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Requests a full state from the server. This should override even implicit entity data.
|
||||
/// </summary>
|
||||
void RequestFullState(EntityUid? missingEntity = null);
|
||||
void RequestFullState(NetEntity? missingEntity = null);
|
||||
|
||||
uint SystemMessageDispatched<T>(T message) where T : EntityEventArgs;
|
||||
|
||||
@@ -105,7 +105,7 @@ namespace Robust.Client.GameStates
|
||||
/// <summary>
|
||||
/// Returns the full collection of cached game states that are used to reset predicted entities.
|
||||
/// </summary>
|
||||
Dictionary<EntityUid, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
Dictionary<NetEntity, Dictionary<ushort, ComponentState>> GetFullRep();
|
||||
|
||||
/// <summary>
|
||||
/// This will perform some setup in order to reset the game to an earlier state. To fully reset the state
|
||||
@@ -144,12 +144,12 @@ namespace Robust.Client.GameStates
|
||||
/// Queue a collection of entities that are to be detached to null-space & marked as PVS-detached.
|
||||
/// This store and modify the list given to it.
|
||||
/// </summary>
|
||||
void QueuePvsDetach(List<EntityUid> entities, GameTick tick);
|
||||
void QueuePvsDetach(List<NetEntity> entities, GameTick tick);
|
||||
|
||||
/// <summary>
|
||||
/// Immediately detach several entities.
|
||||
/// </summary>
|
||||
void DetachImmediate(List<EntityUid> entities);
|
||||
void DetachImmediate(List<NetEntity> entities);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the PVS detach queue.
|
||||
|
||||
@@ -83,13 +83,13 @@ namespace Robust.Client.GameStates
|
||||
/// The data to merge.
|
||||
/// It's a dictionary of entity ID -> (component net ID -> ComponentState)
|
||||
/// </param>
|
||||
void MergeImplicitData(Dictionary<EntityUid, Dictionary<ushort, ComponentState>> data);
|
||||
void MergeImplicitData(Dictionary<NetEntity, Dictionary<ushort, ComponentState>> data);
|
||||
|
||||
/// <summary>
|
||||
/// Get the last state data from the server for an entity.
|
||||
/// </summary>
|
||||
/// <returns>Dictionary (net ID -> ComponentState)</returns>
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(EntityUid entity);
|
||||
Dictionary<ushort, ComponentState> GetLastServerStates(NetEntity entity);
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the number of applicable states in the game state buffer from a given tick.
|
||||
@@ -98,7 +98,7 @@ namespace Robust.Client.GameStates
|
||||
/// <param name="fromTick">The tick to calculate from.</param>
|
||||
int CalculateBufferSize(GameTick fromTick);
|
||||
|
||||
bool TryGetLastServerStates(EntityUid entity,
|
||||
bool TryGetLastServerStates(NetEntity entity,
|
||||
[NotNullWhen(true)] out Dictionary<ushort, ComponentState>? dictionary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private readonly Font _font;
|
||||
private readonly int _lineHeight;
|
||||
private readonly Dictionary<EntityUid, NetEntData> _netEnts = new();
|
||||
private readonly Dictionary<NetEntity, NetEntData> _netEnts = new();
|
||||
|
||||
public NetEntityOverlay()
|
||||
{
|
||||
@@ -77,12 +77,12 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var entityState in gameState.EntityStates.Span)
|
||||
{
|
||||
if (!_netEnts.TryGetValue(entityState.Uid, out var netEnt))
|
||||
if (!_netEnts.TryGetValue(entityState.NetEntity, out var netEnt))
|
||||
{
|
||||
if (_netEnts.Count >= _maxEnts)
|
||||
continue;
|
||||
|
||||
_netEnts[entityState.Uid] = netEnt = new();
|
||||
_netEnts[entityState.NetEntity] = netEnt = new();
|
||||
}
|
||||
|
||||
if (!netEnt.InPVS && netEnt.LastUpdate < gameState.ToSequence)
|
||||
@@ -119,11 +119,13 @@ namespace Robust.Client.GameStates
|
||||
var screenHandle = args.ScreenHandle;
|
||||
|
||||
int i = 0;
|
||||
foreach (var (uid, netEnt) in _netEnts)
|
||||
foreach (var (nent, netEnt) in _netEnts)
|
||||
{
|
||||
var uid = _entityManager.GetEntity(nent);
|
||||
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
{
|
||||
_netEnts.Remove(uid);
|
||||
_netEnts.Remove(nent);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Robust.Client.GameStates
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
private const int HistorySize = 60 * 5; // number of ticks to keep in history.
|
||||
private const int TargetPayloadBps = 56000 / 8; // Target Payload size in Bytes per second. A mind-numbing fifty-six thousand bits per second, who would ever need more?
|
||||
@@ -73,7 +74,7 @@ namespace Robust.Client.GameStates
|
||||
_history.Add((toSeq, sz, lag, buffer));
|
||||
|
||||
// not watching an ent
|
||||
if(!WatchEntId.IsValid() || WatchEntId.IsClientSide())
|
||||
if(!WatchEntId.IsValid() || _entManager.IsClientSide(WatchEntId))
|
||||
return;
|
||||
|
||||
string? entStateString = null;
|
||||
@@ -86,7 +87,9 @@ namespace Robust.Client.GameStates
|
||||
var sb = new StringBuilder();
|
||||
foreach (var entState in entStates.Span)
|
||||
{
|
||||
if (entState.Uid != WatchEntId)
|
||||
var uid = _entManager.GetEntity(entState.NetEntity);
|
||||
|
||||
if (uid != WatchEntId)
|
||||
continue;
|
||||
|
||||
if (!entState.ComponentChanges.HasContents)
|
||||
@@ -115,7 +118,9 @@ namespace Robust.Client.GameStates
|
||||
|
||||
foreach (var ent in args.Detached)
|
||||
{
|
||||
if (ent != WatchEntId)
|
||||
var uid = _entManager.GetEntity(ent);
|
||||
|
||||
if (uid != WatchEntId)
|
||||
continue;
|
||||
|
||||
conShell.WriteLine($"watchEnt: Left PVS at tick {args.AppliedState.ToSequence}, eid={WatchEntId}" + "\n");
|
||||
@@ -126,7 +131,9 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var entDelete in entDeletes.Span)
|
||||
{
|
||||
if (entDelete == WatchEntId)
|
||||
var uid = _entManager.GetEntity(entDelete);
|
||||
|
||||
if (uid == WatchEntId)
|
||||
entDelString = "\n Deleted";
|
||||
}
|
||||
}
|
||||
@@ -294,30 +301,33 @@ namespace Robust.Client.GameStates
|
||||
|
||||
private sealed class NetWatchEntCommand : LocalizedCommands
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public override string Command => "net_watchent";
|
||||
|
||||
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntityUid eValue;
|
||||
EntityUid? entity;
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
eValue = IoCManager.Resolve<IPlayerManager>().LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
entity = _playerManager.LocalPlayer?.ControlledEntity ?? EntityUid.Invalid;
|
||||
}
|
||||
else if (!EntityUid.TryParse(args[0], out eValue))
|
||||
else if (!NetEntity.TryParse(args[0], out var netEntity) || !_entManager.TryGetEntity(netEntity, out entity))
|
||||
{
|
||||
shell.WriteError("Invalid argument: Needs to be 0 or an entityId.");
|
||||
return;
|
||||
}
|
||||
|
||||
var overlayMan = IoCManager.Resolve<IOverlayManager>();
|
||||
|
||||
if (!overlayMan.TryGetOverlay(out NetGraphOverlay? overlay))
|
||||
if (!_overlayManager.TryGetOverlay(out NetGraphOverlay? overlay))
|
||||
{
|
||||
overlay = new();
|
||||
overlayMan.AddOverlay(overlay);
|
||||
overlay = new NetGraphOverlay();
|
||||
_overlayManager.AddOverlay(overlay);
|
||||
}
|
||||
|
||||
overlay.WatchEntId = eValue;
|
||||
overlay.WatchEntId = entity.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Robust.Client.Physics
|
||||
{
|
||||
if (args.Current is not JointComponentState jointState) return;
|
||||
|
||||
component.Relay = jointState.Relay;
|
||||
component.Relay = EnsureEntity<JointComponent>(jointState.Relay, uid);
|
||||
|
||||
// Initial state gets applied before the entity (& entity's transform) have been initialized.
|
||||
// So just let joint init code handle that.
|
||||
@@ -29,7 +29,7 @@ namespace Robust.Client.Physics
|
||||
component.Joints.Clear();
|
||||
foreach (var (id, state) in jointState.Joints)
|
||||
{
|
||||
component.Joints[id] = state.GetJoint();
|
||||
component.Joints[id] = state.GetJoint(EntityManager, uid);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -62,8 +62,8 @@ namespace Robust.Client.Physics
|
||||
continue;
|
||||
}
|
||||
|
||||
var other = state.UidA == uid ? state.UidB : state.UidA;
|
||||
|
||||
var uidA = GetEntity(state.UidA);
|
||||
var other = uidA == uid ? GetEntity(state.UidB) : uidA;
|
||||
|
||||
// Add new joint (if possible).
|
||||
// Need to wait for BOTH joint components to come in first before we can add it. Yay dependencies!
|
||||
@@ -82,11 +82,11 @@ namespace Robust.Client.Physics
|
||||
// TODO: component state handling ordering.
|
||||
if (Transform(uid).MapID == MapId.Nullspace)
|
||||
{
|
||||
AddedJoints.Add(state.GetJoint());
|
||||
AddedJoints.Add(state.GetJoint(EntityManager, uid));
|
||||
continue;
|
||||
}
|
||||
|
||||
AddJoint(state.GetJoint());
|
||||
AddJoint(state.GetJoint(EntityManager, uid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Network.Messages;
|
||||
using Robust.Shared.Players;
|
||||
@@ -24,6 +25,7 @@ namespace Robust.Client.Player
|
||||
[Dependency] private readonly IClientNetManager _network = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly ILogManager _logMan = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Active sessions of connected clients to the server.
|
||||
@@ -65,6 +67,8 @@ namespace Robust.Client.Player
|
||||
}
|
||||
}
|
||||
private LocalPlayer? _localPlayer;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
public event Action<LocalPlayerChangedEventArgs>? LocalPlayerChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -82,6 +86,7 @@ namespace Robust.Client.Player
|
||||
{
|
||||
_client.RunLevelChanged += OnRunLevelChanged;
|
||||
|
||||
_sawmill = _logMan.GetSawmill("player");
|
||||
_network.RegisterNetMessage<MsgPlayerListReq>();
|
||||
_network.RegisterNetMessage<MsgPlayerList>(HandlePlayerList);
|
||||
}
|
||||
@@ -122,7 +127,13 @@ namespace Robust.Client.Player
|
||||
|
||||
if (myState != null)
|
||||
{
|
||||
UpdateAttachedEntity(myState.ControlledEntity);
|
||||
var uid = _entManager.GetEntity(myState.ControlledEntity);
|
||||
if (myState.ControlledEntity is {Valid: true} && !_entManager.EntityExists(uid))
|
||||
{
|
||||
_sawmill.Error($"Received player state for local player with an unknown net entity!");
|
||||
}
|
||||
|
||||
UpdateAttachedEntity(uid);
|
||||
UpdateSessionStatus(myState.Status);
|
||||
}
|
||||
|
||||
@@ -181,11 +192,13 @@ namespace Robust.Client.Player
|
||||
if (_sessions.TryGetValue(state.UserId, out var session))
|
||||
{
|
||||
var local = (PlayerSession) session;
|
||||
var controlled = _entManager.GetEntity(state.ControlledEntity);
|
||||
|
||||
// Exists, update data.
|
||||
if (local.Name == state.Name
|
||||
&& local.Status == state.Status
|
||||
&& local.Ping == state.Ping
|
||||
&& local.AttachedEntity == state.ControlledEntity)
|
||||
&& local.AttachedEntity == controlled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -194,7 +207,7 @@ namespace Robust.Client.Player
|
||||
local.Name = state.Name;
|
||||
local.Status = state.Status;
|
||||
local.Ping = state.Ping;
|
||||
local.AttachedEntity = state.ControlledEntity;
|
||||
local.AttachedEntity = controlled;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -206,7 +219,7 @@ namespace Robust.Client.Player
|
||||
Name = state.Name,
|
||||
Status = state.Status,
|
||||
Ping = state.Ping,
|
||||
AttachedEntity = state.ControlledEntity,
|
||||
AttachedEntity = _entManager.GetEntity(state.ControlledEntity),
|
||||
};
|
||||
_sessions.Add(state.UserId, newSession);
|
||||
if (state.UserId == LocalPlayer!.UserId)
|
||||
|
||||
@@ -83,8 +83,8 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
HashSet<ResPath> uploadedFiles = new();
|
||||
var detached = new HashSet<EntityUid>();
|
||||
var detachQueue = new Dictionary<GameTick, List<EntityUid>>();
|
||||
var detached = new HashSet<NetEntity>();
|
||||
var detachQueue = new Dictionary<GameTick, List<NetEntity>>();
|
||||
|
||||
if (initMessages != null)
|
||||
UpdateMessages(initMessages, uploadedFiles, prototypes, cvars, detachQueue, ref timeBase, true);
|
||||
@@ -92,11 +92,11 @@ public sealed partial class ReplayLoadManager
|
||||
ProcessQueue(GameTick.MaxValue, detachQueue, detached);
|
||||
|
||||
var entSpan = state0.EntityStates.Value;
|
||||
Dictionary<EntityUid, EntityState> entStates = new(entSpan.Count);
|
||||
Dictionary<NetEntity, EntityState> entStates = new(entSpan.Count);
|
||||
foreach (var entState in entSpan)
|
||||
{
|
||||
var modifiedState = AddImplicitData(entState);
|
||||
entStates.Add(entState.Uid, modifiedState);
|
||||
entStates.Add(entState.NetEntity, modifiedState);
|
||||
}
|
||||
|
||||
await callback(0, states.Count, LoadingState.ProcessingFiles, true);
|
||||
@@ -112,11 +112,11 @@ public sealed partial class ReplayLoadManager
|
||||
default,
|
||||
entStates.Values.ToArray(),
|
||||
playerStates.Values.ToArray(),
|
||||
Array.Empty<EntityUid>());
|
||||
Array.Empty<NetEntity>());
|
||||
checkPoints.Add(new CheckpointState(state0, timeBase, cvars, 0, detached));
|
||||
|
||||
DebugTools.Assert(state0.EntityDeletions.Value.Count == 0);
|
||||
var empty = Array.Empty<EntityUid>();
|
||||
var empty = Array.Empty<NetEntity>();
|
||||
|
||||
TimeSpan GetTime(GameTick tick)
|
||||
{
|
||||
@@ -176,8 +176,8 @@ public sealed partial class ReplayLoadManager
|
||||
|
||||
private void ProcessQueue(
|
||||
GameTick curTick,
|
||||
Dictionary<GameTick, List<EntityUid>> detachQueue,
|
||||
HashSet<EntityUid> detached)
|
||||
Dictionary<GameTick, List<NetEntity>> detachQueue,
|
||||
HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var (tick, ents) in detachQueue)
|
||||
{
|
||||
@@ -192,7 +192,7 @@ public sealed partial class ReplayLoadManager
|
||||
HashSet<ResPath> uploadedFiles,
|
||||
Dictionary<Type, HashSet<string>> prototypes,
|
||||
Dictionary<string, object> cvars,
|
||||
Dictionary<GameTick, List<EntityUid>> detachQueue,
|
||||
Dictionary<GameTick, List<NetEntity>> detachQueue,
|
||||
ref (TimeSpan, GameTick) timeBase,
|
||||
bool ignoreDuplicates = false)
|
||||
{
|
||||
@@ -301,8 +301,8 @@ public sealed partial class ReplayLoadManager
|
||||
_locMan.ReloadLocalizations();
|
||||
}
|
||||
|
||||
private void UpdateDeletions(NetListAsArray<EntityUid> entityDeletions,
|
||||
Dictionary<EntityUid, EntityState> entStates, HashSet<EntityUid> detached)
|
||||
private void UpdateDeletions(NetListAsArray<NetEntity> entityDeletions,
|
||||
Dictionary<NetEntity, EntityState> entStates, HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var ent in entityDeletions.Span)
|
||||
{
|
||||
@@ -311,16 +311,16 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<EntityUid, EntityState> entStates,
|
||||
ref int spawnedTracker, ref int stateTracker, HashSet<EntityUid> detached)
|
||||
private void UpdateEntityStates(ReadOnlySpan<EntityState> span, Dictionary<NetEntity, EntityState> entStates,
|
||||
ref int spawnedTracker, ref int stateTracker, HashSet<NetEntity> detached)
|
||||
{
|
||||
foreach (var entState in span)
|
||||
{
|
||||
detached.Remove(entState.Uid);
|
||||
if (!entStates.TryGetValue(entState.Uid, out var oldEntState))
|
||||
detached.Remove(entState.NetEntity);
|
||||
if (!entStates.TryGetValue(entState.NetEntity, out var oldEntState))
|
||||
{
|
||||
var modifiedState = AddImplicitData(entState);
|
||||
entStates[entState.Uid] = modifiedState;
|
||||
entStates[entState.NetEntity] = modifiedState;
|
||||
spawnedTracker++;
|
||||
|
||||
#if DEBUG
|
||||
@@ -333,11 +333,11 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
stateTracker++;
|
||||
DebugTools.Assert(oldEntState.Uid == entState.Uid);
|
||||
entStates[entState.Uid] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
|
||||
DebugTools.Assert(oldEntState.NetEntity == entState.NetEntity);
|
||||
entStates[entState.NetEntity] = MergeStates(entState, oldEntState.ComponentChanges.Value, oldEntState.NetComponents);
|
||||
|
||||
#if DEBUG
|
||||
foreach (var state in entStates[entState.Uid].ComponentChanges.Span)
|
||||
foreach (var state in entStates[entState.NetEntity].ComponentChanges.Span)
|
||||
{
|
||||
DebugTools.Assert(state.State is not IComponentDeltaState delta || delta.FullState);
|
||||
}
|
||||
@@ -388,7 +388,7 @@ public sealed partial class ReplayLoadManager
|
||||
}
|
||||
|
||||
DebugTools.Assert(newState.NetComponents == null || newState.NetComponents.Count == combined.Count);
|
||||
return new EntityState(newState.Uid, combined, newState.EntityLastModified, newState.NetComponents ?? oldNetComps);
|
||||
return new EntityState(newState.NetEntity, combined, newState.EntityLastModified, newState.NetComponents ?? oldNetComps);
|
||||
}
|
||||
|
||||
private void UpdatePlayerStates(ReadOnlySpan<PlayerState> span, Dictionary<NetUserId, PlayerState> playerStates)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +68,18 @@ public sealed partial class ReplayLoadManager
|
||||
var metaState = (MetaDataComponentState?)ent.ComponentChanges.Value?
|
||||
.FirstOrDefault(c => c.NetID == _metaId).State;
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(ent.Uid);
|
||||
throw new MissingMetadataException(ent.NetEntity);
|
||||
|
||||
_entMan.CreateEntityUninitialized(metaState.PrototypeId, ent.Uid);
|
||||
entities.Add(ent.Uid);
|
||||
var uid = _entMan.CreateEntityUninitialized(metaState.PrototypeId);
|
||||
entities.Add(uid);
|
||||
var metaComp = _entMan.GetComponent<MetaDataComponent>(uid);
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
_entMan.ClearNetEntity(metaComp.NetEntity);
|
||||
|
||||
_entMan.SetNetEntity(uid, ent.NetEntity, metaComp);
|
||||
|
||||
if (i++ % 50 == 0)
|
||||
{
|
||||
|
||||
@@ -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!;
|
||||
|
||||
@@ -82,19 +82,28 @@ internal sealed partial class ReplayPlaybackManager
|
||||
var metas = _entMan.GetEntityQuery<MetaDataComponent>();
|
||||
foreach (var es in checkpoint.DetachedStates)
|
||||
{
|
||||
if (metas.TryGetComponent(es.Uid, out var meta) && !meta.EntityDeleted)
|
||||
var uid = _entMan.GetEntity(es.NetEntity);
|
||||
if (metas.TryGetComponent(uid, out var meta) && !meta.EntityDeleted)
|
||||
continue;
|
||||
;
|
||||
|
||||
var metaState = (MetaDataComponentState?)es.ComponentChanges.Value?
|
||||
.FirstOrDefault(c => c.NetID == _metaId).State;
|
||||
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(es.Uid);
|
||||
throw new MissingMetadataException(es.NetEntity);
|
||||
|
||||
_entMan.CreateEntityUninitialized(metaState.PrototypeId, es.Uid);
|
||||
meta = metas.GetComponent(es.Uid);
|
||||
_entMan.InitializeEntity(es.Uid, meta);
|
||||
_entMan.StartEntity(es.Uid);
|
||||
_entMan.CreateEntityUninitialized(metaState.PrototypeId, uid);
|
||||
meta = metas.GetComponent(uid);
|
||||
|
||||
// Client creates a client-side net entity for the newly created entity.
|
||||
// We need to clear this mapping before assigning the real net id.
|
||||
// TODO NetEntity Jank: prevent the client from creating this in the first place.
|
||||
_entMan.ClearNetEntity(meta.NetEntity);
|
||||
|
||||
_entMan.SetNetEntity(uid, es.NetEntity, meta);
|
||||
|
||||
_entMan.InitializeEntity(uid, meta);
|
||||
_entMan.StartEntity(uid);
|
||||
meta.LastStateApplied = checkpoint.Tick;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
}
|
||||
|
||||
_timing.CurTick += 1;
|
||||
_entMan.TickUpdate(args.DeltaSeconds, noPredictions: true);
|
||||
_cEntManager.TickUpdate(args.DeltaSeconds, noPredictions: true);
|
||||
|
||||
if (!Playing || AutoPauseCountdown == null)
|
||||
return;
|
||||
@@ -82,7 +82,7 @@ internal sealed partial class ReplayPlaybackManager
|
||||
// Maybe track our own detach queue and use _gameState.DetachImmediate()?
|
||||
// That way we don't have to clone this. Downside would be that all entities will be immediately
|
||||
// detached. I.e., the detach budget cvar will simply be ignored.
|
||||
var clone = new List<EntityUid>(leavePvs.Entities);
|
||||
var clone = new List<NetEntity>(leavePvs.Entities);
|
||||
|
||||
_gameState.QueuePvsDetach(clone, leavePvs.Tick);
|
||||
continue;
|
||||
|
||||
@@ -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!;
|
||||
|
||||
@@ -95,12 +95,12 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
{
|
||||
var tick = _timing.LastRealTick;
|
||||
var players = _player.Sessions.Select(GetPlayerState).ToArray();
|
||||
var deletions = Array.Empty<EntityUid>();
|
||||
var deletions = Array.Empty<NetEntity>();
|
||||
|
||||
var fullRep = _state.GetFullRep();
|
||||
var entStates = new EntityState[fullRep.Count];
|
||||
var i = 0;
|
||||
foreach (var (uid, dict) in fullRep)
|
||||
foreach (var (netEntity, dict) in fullRep)
|
||||
{
|
||||
var compData = new ComponentChange[dict.Count];
|
||||
var netComps = new HashSet<ushort>(dict.Keys);
|
||||
@@ -110,7 +110,7 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
compData[j++] = new ComponentChange(id, compState, tick);
|
||||
}
|
||||
|
||||
entStates[i++] = new EntityState(uid, compData, tick, netComps);
|
||||
entStates[i++] = new EntityState(netEntity, compData, tick, netComps);
|
||||
}
|
||||
|
||||
var state = new GameState(
|
||||
@@ -121,16 +121,17 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
players,
|
||||
deletions);
|
||||
|
||||
var detached = new List<EntityUid>();
|
||||
var detached = new List<NetEntity>();
|
||||
var query = _entMan.AllEntityQueryEnumerator<MetaDataComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (uid.IsClientSide())
|
||||
if (_entMan.IsClientSide(uid))
|
||||
continue;
|
||||
|
||||
DebugTools.Assert(fullRep.ContainsKey(uid));
|
||||
var nent = comp.NetEntity;
|
||||
DebugTools.Assert(fullRep.ContainsKey(nent));
|
||||
if ((comp.Flags & MetaDataFlags.Detached) != 0)
|
||||
detached.Add(uid);
|
||||
detached.Add(nent);
|
||||
}
|
||||
|
||||
var detachMsg = detached.Count > 0 ? new ReplayMessage.LeavePvs(detached, tick) : null;
|
||||
@@ -144,7 +145,7 @@ internal sealed class ReplayRecordingManager : SharedReplayRecordingManager
|
||||
UserId = session.UserId,
|
||||
Status = session.Status,
|
||||
Name = session.Name,
|
||||
ControlledEntity = session.AttachedEntity,
|
||||
ControlledEntity = _entMan.GetNetEntity(session.AttachedEntity),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
private const int TabServerComponents = 3;
|
||||
|
||||
private TabContainer _tabs = default!;
|
||||
private EntityUid _entity = default!;
|
||||
private EntityUid _entity;
|
||||
private NetEntity _netEntity = default!;
|
||||
|
||||
private ViewVariablesAddWindow? _addComponentWindow;
|
||||
private bool _addComponentServer;
|
||||
@@ -71,7 +72,8 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
public override void Initialize(DefaultWindow window, object obj)
|
||||
{
|
||||
_entity = (EntityUid) obj;
|
||||
_netEntity = (NetEntity) obj;
|
||||
_entity = _entityManager.GetEntity(_netEntity);
|
||||
|
||||
var scrollContainer = new ScrollContainer();
|
||||
//scrollContainer.SetAnchorPreset(Control.LayoutPreset.Wide, true);
|
||||
@@ -163,7 +165,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
|
||||
PopulateClientComponents();
|
||||
|
||||
if (!_entity.IsClientSide())
|
||||
if (!_entityManager.IsClientSide(_entity))
|
||||
{
|
||||
_serverVariables = new BoxContainer
|
||||
{
|
||||
@@ -268,12 +270,12 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
ViewVariablesManager.OpenVV(
|
||||
new ViewVariablesComponentSelector(_entity, componentType.FullName));
|
||||
new ViewVariablesComponentSelector(_entityManager.GetNetEntity(_entity), componentType.FullName));
|
||||
};
|
||||
removeButton.OnPressed += _ =>
|
||||
{
|
||||
// We send a command to remove the component.
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_entity} {componentType.ComponentName}");
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"rmcomp {_netEntity} {componentType.ComponentName}");
|
||||
PopulateServerComponents();
|
||||
};
|
||||
button.AddChild(removeButton);
|
||||
@@ -417,7 +419,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
if (_addComponentServer)
|
||||
{
|
||||
// Attempted to add a component to the server entity... We send a command.
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_entity} {eventArgs.Entry}");
|
||||
IoCManager.Resolve<IClientConsoleHost>().RemoteExecuteCommand(null, $"addcomp {_netEntity} {eventArgs.Entry}");
|
||||
PopulateServerComponents();
|
||||
_addComponentWindow?.Populate(await GetValidServerComponentsForAdding());
|
||||
return;
|
||||
@@ -504,7 +506,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
try
|
||||
{
|
||||
_entitySession =
|
||||
await ViewVariablesManager.RequestSession(new ViewVariablesEntitySelector(_entity));
|
||||
await ViewVariablesManager.RequestSession(new ViewVariablesEntitySelector(_netEntity));
|
||||
}
|
||||
catch (SessionDenyException e)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteLine($"{uid} is not a valid entity uid.");
|
||||
shell.WriteLine($"{args[0]} is not a valid entity.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entityManager.EntityExists(uid))
|
||||
if (!_entityManager.TryGetEntity(uidNet, out var uid) || !_entityManager.EntityExists(uid))
|
||||
{
|
||||
shell.WriteLine($"No entity found with id {uid}.");
|
||||
return;
|
||||
@@ -44,17 +44,17 @@ namespace Robust.Server.Console.Commands
|
||||
|
||||
if (_entityManager.HasComponent(uid, registration.Type))
|
||||
{
|
||||
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid).EntityName} already has a {componentName} component.");
|
||||
shell.WriteLine($"Entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName} already has a {componentName} component.");
|
||||
}
|
||||
|
||||
var component = (Component) _componentFactory.GetComponent(registration.Type);
|
||||
|
||||
#pragma warning disable CS0618
|
||||
component.Owner = uid;
|
||||
component.Owner = uid.Value;
|
||||
#pragma warning restore CS0618
|
||||
_entityManager.AddComponent(uid, component);
|
||||
_entityManager.AddComponent(uid.Value, component);
|
||||
|
||||
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid).EntityName}.");
|
||||
shell.WriteLine($"Added {componentName} component to entity {_entityManager.GetComponent<MetaDataComponent>(uid.Value).EntityName}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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}.");
|
||||
|
||||
@@ -24,7 +24,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var netEntity))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {args[0]}");
|
||||
return;
|
||||
@@ -47,6 +47,7 @@ public sealed class ScaleCommand : LocalizedCommands
|
||||
var physics = _entityManager.System<SharedPhysicsSystem>();
|
||||
var appearance = _entityManager.System<AppearanceSystem>();
|
||||
|
||||
var uid = _entityManager.GetEntity(netEntity);
|
||||
_entityManager.EnsureComponent<ScaleVisualsComponent>(uid);
|
||||
var @event = new ScaleEntityEvent();
|
||||
_entityManager.EventBus.RaiseLocalEvent(uid, ref @event);
|
||||
|
||||
@@ -29,10 +29,12 @@ public sealed class SpinCommand : LocalizedCommands
|
||||
}
|
||||
|
||||
// get the target
|
||||
EntityUid target;
|
||||
EntityUid? target;
|
||||
|
||||
if (args.Length == 3)
|
||||
{
|
||||
if (!EntityUid.TryParse(args[2], out target))
|
||||
if (!NetEntity.TryParse(args[2], out var targetNet) ||
|
||||
!_entities.TryGetEntity(targetNet, out target))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {args[1]}");
|
||||
return;
|
||||
@@ -65,6 +67,6 @@ public sealed class SpinCommand : LocalizedCommands
|
||||
|
||||
var physicsSystem = _entities.System<SharedPhysicsSystem>();
|
||||
physicsSystem.SetAngularDamping(physics, drag);
|
||||
physicsSystem.SetAngularVelocity(target, speed, body: physics);
|
||||
physicsSystem.SetAngularVelocity(target.Value, speed, body: physics);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,19 +29,19 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteError($"Unable to parse {args[0]} as a {nameof(EntityUid)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entities.EntityExists(uid))
|
||||
if (!_entities.TryGetEntity(uidNet, out var uid) || !_entities.EntityExists(uid))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {uid}");
|
||||
return;
|
||||
}
|
||||
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().AddViewSubscriber(uid, playerSession);
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().AddViewSubscriber(uid.Value, playerSession);
|
||||
}
|
||||
|
||||
public sealed class RemoveViewSubscriberCommand : LocalizedCommands
|
||||
@@ -66,19 +66,19 @@ namespace Robust.Server.Console.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var uid))
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet))
|
||||
{
|
||||
shell.WriteError($"Unable to parse {args[0]} as a {nameof(EntityUid)}");
|
||||
shell.WriteError($"Unable to parse {args[0]} as a {nameof(NetEntity)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entities.EntityExists(uid))
|
||||
if (!_entities.TryGetEntity(uidNet, out var uid) || !_entities.EntityExists(uid))
|
||||
{
|
||||
shell.WriteError($"Unable to find entity {uid}");
|
||||
return;
|
||||
}
|
||||
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().RemoveViewSubscriber(uid, playerSession);
|
||||
_entities.EntitySysManager.GetEntitySystem<ViewSubscriberSystem>().RemoveViewSubscriber(uid.Value, playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Robust.Server.GameObjects
|
||||
/// </summary>
|
||||
private void OnMessageReceived(BoundUIWrapMessage msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var uid = msg.Entity;
|
||||
var uid = GetEntity(msg.Entity);
|
||||
if (!TryComp(uid, out ServerUserInterfaceComponent? uiComp) || args.SenderSession is not IPlayerSession session)
|
||||
return;
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Robust.Server.GameObjects
|
||||
// get the wrapped message and populate it with the sender & UI key information.
|
||||
var message = msg.Message;
|
||||
message.Session = args.SenderSession;
|
||||
message.Entity = uid;
|
||||
message.Entity = GetNetEntity(uid);
|
||||
message.UiKey = msg.UiKey;
|
||||
|
||||
// Raise as object so the correct type is used.
|
||||
@@ -321,9 +321,9 @@ namespace Robust.Server.GameObjects
|
||||
/// The player session to send this new state to.
|
||||
/// Set to null for sending it to every subscribed player session.
|
||||
/// </param>
|
||||
public static void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
|
||||
public void SetUiState(BoundUserInterface bui, BoundUserInterfaceState state, IPlayerSession? session = null, bool clearOverrides = true)
|
||||
{
|
||||
var msg = new BoundUIWrapMessage(bui.Owner, new UpdateBoundStateMessage(state), bui.UiKey);
|
||||
var msg = new BoundUIWrapMessage(GetNetEntity(bui.Owner), new UpdateBoundStateMessage(state), bui.UiKey);
|
||||
if (session == null)
|
||||
{
|
||||
bui.LastStateMsg = msg;
|
||||
@@ -385,7 +385,7 @@ namespace Robust.Server.GameObjects
|
||||
_openInterfaces.GetOrNew(session).Add(bui);
|
||||
RaiseLocalEvent(bui.Owner, new BoundUIOpenedEvent(bui.UiKey, bui.Owner, session));
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new OpenBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
|
||||
// Fun fact, clients needs to have BUIs open before they can receive the state.....
|
||||
if (bui.LastStateMsg != null)
|
||||
@@ -414,7 +414,7 @@ namespace Robust.Server.GameObjects
|
||||
if (!bui._subscribedSessions.Remove(session))
|
||||
return false;
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), new CloseBoundInterfaceMessage(), bui.UiKey), session.ConnectedClient);
|
||||
CloseShared(bui, session, activeUis);
|
||||
return true;
|
||||
}
|
||||
@@ -492,7 +492,7 @@ namespace Robust.Server.GameObjects
|
||||
/// </summary>
|
||||
public void SendUiMessage(BoundUserInterface bui, BoundUserInterfaceMessage message)
|
||||
{
|
||||
var msg = new BoundUIWrapMessage(bui.Owner, message, bui.UiKey);
|
||||
var msg = new BoundUIWrapMessage(GetNetEntity(bui.Owner), message, bui.UiKey);
|
||||
foreach (var session in bui.SubscribedSessions)
|
||||
{
|
||||
RaiseNetworkEvent(msg, session.ConnectedClient);
|
||||
@@ -518,7 +518,7 @@ namespace Robust.Server.GameObjects
|
||||
if (!bui.SubscribedSessions.Contains(session))
|
||||
return false;
|
||||
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(bui.Owner, message, bui.UiKey), session.ConnectedClient);
|
||||
RaiseNetworkEvent(new BoundUIWrapMessage(GetNetEntity(bui.Owner), message, bui.UiKey), session.ConnectedClient);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ namespace Robust.Server.GameObjects
|
||||
|
||||
private ISawmill _netEntSawmill = default!;
|
||||
|
||||
protected override int NextEntityUid { get; set; } = (int) EntityUid.FirstUid;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_netEntSawmill = LogManager.GetSawmill("net.ent");
|
||||
@@ -54,9 +52,9 @@ namespace Robust.Server.GameObjects
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype, EntityUid uid)
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
|
||||
{
|
||||
return AllocEntity(prototype, out _, uid);
|
||||
return AllocEntity(prototype, out _);
|
||||
}
|
||||
|
||||
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)
|
||||
@@ -79,15 +77,15 @@ namespace Robust.Server.GameObjects
|
||||
StartEntity(entity);
|
||||
}
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, uid, context);
|
||||
return base.CreateEntity(prototypeName, context);
|
||||
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
var entity = base.CreateEntity(prototype, uid, context);
|
||||
var entity = base.CreateEntity(prototype, context);
|
||||
|
||||
// At this point in time, all data configure on the entity *should* be purely from the prototype.
|
||||
// As such, we can reset the modified ticks to Zero,
|
||||
|
||||
@@ -24,6 +24,6 @@ namespace Robust.Server.GameStates
|
||||
|
||||
Action<ICommonSession, GameTick>? ClientAck { get; set; }
|
||||
|
||||
Action<ICommonSession, GameTick, EntityUid?>? ClientRequestFull { get; set; }
|
||||
Action<ICommonSession, GameTick, NetEntity?>? ClientRequestFull { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddGlobalOverride(EntityUid uid, bool removeExistingOverride = true, bool recursive = false)
|
||||
{
|
||||
_pvs.EntityPVSCollection.AddGlobalOverride(uid, removeExistingOverride, recursive);
|
||||
_pvs.EntityPVSCollection.AddGlobalOverride(GetNetEntity(uid), removeExistingOverride, recursive);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +28,7 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
|
||||
{
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, session, removeExistingOverride);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), session, removeExistingOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,6 +39,6 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
if (!Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
_pvs.EntityPVSCollection.UpdateIndex(uid, xform.Coordinates, true);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), xform.Coordinates, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ internal sealed partial class PvsSystem
|
||||
return;
|
||||
|
||||
var ackedTick = sessionData.LastReceivedAck;
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? ackedData;
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? ackedData;
|
||||
|
||||
if (sessionData.Overflow != null && sessionData.Overflow.Value.Tick <= ackedTick)
|
||||
{
|
||||
|
||||
@@ -24,14 +24,13 @@ namespace Robust.Server.GameStates;
|
||||
|
||||
internal sealed partial class PvsSystem : EntitySystem
|
||||
{
|
||||
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IMapManagerInternal _mapManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerNetConfigurationManager _netConfigManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IParallelManager _parallelManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerGameStateManager _serverGameStateManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly IServerNetConfigurationManager _netConfigManager = default!;
|
||||
[Shared.IoC.Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public const float ChunkSize = 8;
|
||||
|
||||
@@ -63,31 +62,28 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
internal readonly Dictionary<ICommonSession, SessionPVSData> PlayerData = new();
|
||||
|
||||
private PVSCollection<EntityUid> _entityPvsCollection = default!;
|
||||
public PVSCollection<EntityUid> EntityPVSCollection => _entityPvsCollection;
|
||||
private PVSCollection<NetEntity> _entityPvsCollection = default!;
|
||||
public PVSCollection<NetEntity> EntityPVSCollection => _entityPvsCollection;
|
||||
|
||||
private readonly List<IPVSCollection> _pvsCollections = new();
|
||||
|
||||
private readonly ObjectPool<Dictionary<EntityUid, PvsEntityVisibility>> _visSetPool
|
||||
= new DefaultObjectPool<Dictionary<EntityUid, PvsEntityVisibility>>(
|
||||
new DictPolicy<EntityUid, PvsEntityVisibility>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<Dictionary<NetEntity, PvsEntityVisibility>> _visSetPool
|
||||
= new DefaultObjectPool<Dictionary<NetEntity, PvsEntityVisibility>>(
|
||||
new DictPolicy<NetEntity, PvsEntityVisibility>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<Stack<EntityUid>> _stackPool
|
||||
= new DefaultObjectPool<Stack<EntityUid>>(
|
||||
new StackPolicy<EntityUid>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<Stack<NetEntity>> _stackPool
|
||||
= new DefaultObjectPool<Stack<NetEntity>>(
|
||||
new StackPolicy<NetEntity>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<HashSet<EntityUid>> _uidSetPool
|
||||
= new DefaultObjectPool<HashSet<EntityUid>>(new SetPolicy<EntityUid>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<Dictionary<EntityUid, MetaDataComponent>> _chunkCachePool =
|
||||
new DefaultObjectPool<Dictionary<EntityUid, MetaDataComponent>>(
|
||||
new DictPolicy<EntityUid, MetaDataComponent>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<Dictionary<NetEntity, MetaDataComponent>> _chunkCachePool =
|
||||
new DefaultObjectPool<Dictionary<NetEntity, MetaDataComponent>>(
|
||||
new DictPolicy<NetEntity, MetaDataComponent>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<HashSet<int>> _playerChunkPool =
|
||||
new DefaultObjectPool<HashSet<int>>(new SetPolicy<int>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<RobustTree<EntityUid>> _treePool =
|
||||
new DefaultObjectPool<RobustTree<EntityUid>>(new TreePolicy<EntityUid>(), MaxVisPoolSize);
|
||||
private readonly ObjectPool<RobustTree<NetEntity>> _treePool =
|
||||
new DefaultObjectPool<RobustTree<NetEntity>>(new TreePolicy<NetEntity>(), MaxVisPoolSize);
|
||||
|
||||
private readonly ObjectPool<Dictionary<MapChunkLocation, int>> _mapChunkPool =
|
||||
new DefaultObjectPool<Dictionary<MapChunkLocation, int>>(
|
||||
@@ -102,19 +98,25 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
private readonly List<(uint, IChunkIndexLocation)> _chunkList = new(64);
|
||||
internal readonly HashSet<ICommonSession> PendingAcks = new();
|
||||
|
||||
private readonly Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary<NetEntity, MetaDataComponent> metadata,
|
||||
RobustTree<NetEntity> tree)?> _previousTrees = new();
|
||||
|
||||
private readonly HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
|
||||
|
||||
private EntityQuery<EyeComponent> _eyeQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_eyeQuery = GetEntityQuery<EyeComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
_entityPvsCollection = RegisterPVSCollection<EntityUid>();
|
||||
_entityPvsCollection = RegisterPVSCollection<NetEntity>();
|
||||
|
||||
SubscribeLocalEvent<MapChangedEvent>(ev =>
|
||||
{
|
||||
@@ -159,7 +161,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
// TODO rate limit this?
|
||||
private void OnClientRequestFull(ICommonSession session, GameTick tick, EntityUid? missingEntity)
|
||||
private void OnClientRequestFull(ICommonSession session, GameTick tick, NetEntity? missingEntity)
|
||||
{
|
||||
if (!PlayerData.TryGetValue(session, out var sessionData))
|
||||
return;
|
||||
@@ -173,7 +175,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if (missingEntity != null)
|
||||
{
|
||||
sb.Append($" Apparently they received an entity without metadata: {ToPrettyString(missingEntity.Value)}.");
|
||||
var entity = GetEntity(missingEntity)!;
|
||||
sb.Append($" Apparently they received an entity without metadata: {ToPrettyString(entity.Value)}.");
|
||||
|
||||
if (sessionData.LastSeenAt.TryGetValue(missingEntity.Value, out var lastSeenTick))
|
||||
sb.Append($" Entity last sent: {lastSeenTick.Value}");
|
||||
@@ -240,17 +243,17 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
#region PVSCollection Event Updates
|
||||
|
||||
private void OnEntityDeleted(EntityUid e)
|
||||
private void OnEntityDeleted(EntityUid e, MetaDataComponent metadata)
|
||||
{
|
||||
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, e);
|
||||
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, metadata.NetEntity);
|
||||
|
||||
var previousTick = _gameTiming.CurTick - 1;
|
||||
|
||||
foreach (var sessionData in PlayerData.Values)
|
||||
{
|
||||
sessionData.LastSeenAt.Remove(e);
|
||||
sessionData.LastSeenAt.Remove(metadata.NetEntity);
|
||||
if (sessionData.SentEntities.TryGetValue(previousTick, out var ents))
|
||||
ents.Remove(e);
|
||||
ents.Remove(metadata.NetEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +281,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
DebugTools.Assert(!_mapManager.IsMap(ev.Sender));
|
||||
|
||||
var coordinates = _transform.GetMoverCoordinates(ev.Sender, ev.Component);
|
||||
UpdateEntityRecursive(ev.Sender, ev.Component, coordinates, false, ev.ParentChanged);
|
||||
UpdateEntityRecursive(ev.Sender, _metaQuery.GetComponent(ev.Sender), ev.Component, coordinates, false, ev.ParentChanged);
|
||||
}
|
||||
|
||||
private void OnTransformStartup(EntityUid uid, TransformComponent component, ref TransformStartupEvent args)
|
||||
@@ -295,10 +298,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
DebugTools.Assert(!_mapManager.IsMap(uid));
|
||||
|
||||
var coordinates = _transform.GetMoverCoordinates(uid, component);
|
||||
UpdateEntityRecursive(uid, component, coordinates, false, false);
|
||||
UpdateEntityRecursive(uid, _metaQuery.GetComponent(uid), component, coordinates, false, false);
|
||||
}
|
||||
|
||||
private void UpdateEntityRecursive(EntityUid uid, TransformComponent xform, EntityCoordinates coordinates, bool mover, bool forceDirty)
|
||||
private void UpdateEntityRecursive(EntityUid uid, MetaDataComponent metadata, TransformComponent xform, EntityCoordinates coordinates, bool mover, bool forceDirty)
|
||||
{
|
||||
if (mover && !xform.LocalPosition.Equals(Vector2.Zero))
|
||||
{
|
||||
@@ -310,9 +313,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
var indices = PVSCollection<EntityUid>.GetChunkIndices(coordinates.Position);
|
||||
if (xform.GridUid != null)
|
||||
_entityPvsCollection.UpdateIndex(uid, xform.GridUid.Value, indices, forceDirty: forceDirty);
|
||||
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.GridUid.Value, indices, forceDirty: forceDirty);
|
||||
else
|
||||
_entityPvsCollection.UpdateIndex(uid, xform.MapID, indices, forceDirty: forceDirty);
|
||||
_entityPvsCollection.UpdateIndex(metadata.NetEntity, xform.MapID, indices, forceDirty: forceDirty);
|
||||
|
||||
var children = xform.ChildEnumerator;
|
||||
|
||||
@@ -322,7 +325,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
// directly.
|
||||
while (children.MoveNext(out var child))
|
||||
{
|
||||
UpdateEntityRecursive(child.Value, _xformQuery.GetComponent(child.Value), coordinates, true, forceDirty);
|
||||
UpdateEntityRecursive(child.Value, _metaQuery.GetComponent(child.Value), _xformQuery.GetComponent(child.Value), coordinates, true, forceDirty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +389,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
pvsCollection.AddGrid(gridId);
|
||||
}
|
||||
|
||||
_entityPvsCollection.AddGlobalOverride(gridId, true, false);
|
||||
_entityPvsCollection.AddGlobalOverride(_metaQuery.GetComponent(gridId).NetEntity, true, false);
|
||||
}
|
||||
|
||||
private void OnMapDestroyed(MapChangedEvent e)
|
||||
@@ -406,7 +409,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if(e.Map == MapId.Nullspace) return;
|
||||
var uid = _mapManager.GetMapEntityId(e.Map);
|
||||
_entityPvsCollection.AddGlobalOverride(uid, true, false);
|
||||
_entityPvsCollection.AddGlobalOverride(_metaQuery.GetComponent(uid).NetEntity, true, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -533,14 +536,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
return (_chunkList, playerChunks, viewerEntities);
|
||||
}
|
||||
|
||||
private Dictionary<(uint visMask, IChunkIndexLocation location), (Dictionary<EntityUid, MetaDataComponent> metadata,
|
||||
RobustTree<EntityUid> tree)?> _previousTrees = new();
|
||||
|
||||
private HashSet<(uint visMask, IChunkIndexLocation location)> _reusedTrees = new();
|
||||
|
||||
public void RegisterNewPreviousChunkTrees(
|
||||
List<(uint, IChunkIndexLocation)> chunks,
|
||||
(Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] trees,
|
||||
(Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] trees,
|
||||
bool[] reuse)
|
||||
{
|
||||
// For any chunks able to re-used we'll chuck them in a dictionary for faster lookup.
|
||||
@@ -584,9 +582,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
public bool TryCalculateChunk(
|
||||
IChunkIndexLocation chunkLocation,
|
||||
uint visMask,
|
||||
EntityQuery<TransformComponent> transform,
|
||||
EntityQuery<MetaDataComponent> metadata,
|
||||
out (Dictionary<EntityUid, MetaDataComponent> mData, RobustTree<EntityUid> tree)? result)
|
||||
out (Dictionary<NetEntity, MetaDataComponent> mData, RobustTree<NetEntity> tree)? result)
|
||||
{
|
||||
if (!_entityPvsCollection.IsDirty(chunkLocation) && _previousTrees.TryGetValue((visMask, chunkLocation), out var previousTree))
|
||||
{
|
||||
@@ -613,11 +609,12 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
var chunkSet = _chunkCachePool.Get();
|
||||
var tree = _treePool.Get();
|
||||
foreach (var uid in chunk)
|
||||
foreach (var netEntity in chunk)
|
||||
{
|
||||
AddToChunkSetRecursively(in uid, visMask, tree, chunkSet, transform, metadata);
|
||||
var uid = GetEntity(netEntity);
|
||||
AddToChunkSetRecursively(in uid, in netEntity, visMask, tree, chunkSet);
|
||||
#if DEBUG
|
||||
var xform = transform.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
if (chunkLocation is MapChunkLocation)
|
||||
DebugTools.Assert(xform.GridUid == null || xform.GridUid == uid);
|
||||
else if (chunkLocation is GridChunkLocation)
|
||||
@@ -649,51 +646,55 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private bool AddToChunkSetRecursively(in EntityUid uid, uint visMask, RobustTree<EntityUid> tree, Dictionary<EntityUid, MetaDataComponent> set, EntityQuery<TransformComponent> transform,
|
||||
EntityQuery<MetaDataComponent> metadata)
|
||||
private bool AddToChunkSetRecursively(in EntityUid uid, in NetEntity netEntity, uint visMask, RobustTree<NetEntity> tree, Dictionary<NetEntity, MetaDataComponent> set)
|
||||
{
|
||||
if (set.ContainsKey(uid))
|
||||
if (set.ContainsKey(netEntity))
|
||||
return true;
|
||||
|
||||
var mComp = metadata.GetComponent(uid);
|
||||
var mComp = _metaQuery.GetComponent(uid);
|
||||
|
||||
// TODO: Don't need to know about parents so no longer need to use bool for this method.
|
||||
// If the eye is missing ANY layer this entity or any of its parents belongs to, it is considered invisible.
|
||||
if ((visMask & mComp.VisibilityMask) != mComp.VisibilityMask)
|
||||
return false;
|
||||
|
||||
var xform = transform.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
|
||||
// is this a map or grid?
|
||||
var isRoot = !xform.ParentUid.IsValid() || uid == xform.GridUid;
|
||||
if (isRoot)
|
||||
{
|
||||
DebugTools.Assert(_mapManager.IsGrid(uid) || _mapManager.IsMap(uid));
|
||||
tree.Set(uid);
|
||||
set.Add(uid, mComp);
|
||||
tree.Set(netEntity);
|
||||
set.Add(netEntity, mComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!_mapManager.IsGrid(uid) && !_mapManager.IsMap(uid));
|
||||
|
||||
var parent = xform.ParentUid;
|
||||
if (!set.ContainsKey(parent) && //was the parent not yet added to toSend?
|
||||
!AddToChunkSetRecursively(in parent, visMask, tree, set, transform, metadata)) //did we just fail to add the parent?
|
||||
var parentNetEntity = _metaQuery.GetComponent(parent).NetEntity;
|
||||
|
||||
// TODO performance
|
||||
// AddToChunkSetRecursively will result in a redundant set.ContainsKey() check.
|
||||
// This can probably be avoided somehow
|
||||
if (!set.ContainsKey(parentNetEntity) && //was the parent not yet added to toSend?
|
||||
!AddToChunkSetRecursively(in parent, in parentNetEntity, visMask, tree, set)) //did we just fail to add the parent?
|
||||
{
|
||||
return false; //we failed? suppose we dont get added either
|
||||
}
|
||||
|
||||
//i want it to crash here if it gets added double bc that shouldnt happen and will add alot of unneeded cycles
|
||||
tree.Set(uid, parent);
|
||||
set.Add(uid, mComp);
|
||||
tree.Set(netEntity, parentNetEntity);
|
||||
set.Add(netEntity, mComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
internal (List<EntityState>? updates, List<EntityUid>? deletions, List<EntityUid>? leftPvs, GameTick fromTick)
|
||||
internal (List<EntityState>? updates, List<NetEntity>? deletions, List<NetEntity>? leftPvs, GameTick fromTick)
|
||||
CalculateEntityStates(IPlayerSession session,
|
||||
GameTick fromTick,
|
||||
GameTick toTick,
|
||||
EntityQuery<MetaDataComponent> mQuery,
|
||||
EntityQuery<TransformComponent> tQuery,
|
||||
(Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] chunks,
|
||||
(Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] chunks,
|
||||
HashSet<int> visibleChunks,
|
||||
EntityUid[] viewers)
|
||||
{
|
||||
@@ -712,7 +713,6 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
throw new Exception("Encountered non-empty object inside of _visSetPool. Was the same object returned to the pool more than once?");
|
||||
|
||||
var deletions = _entityPvsCollection.GetDeletedIndices(fromTick);
|
||||
|
||||
var entStateCount = 0;
|
||||
|
||||
var stack = _stackPool.Get();
|
||||
@@ -726,14 +726,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
// Each root nodes should simply be a map or a grid entity.
|
||||
DebugTools.Assert(cache.Value.tree.RootNodes.Count == 1,
|
||||
$"Root node count is {cache.Value.tree.RootNodes.Count} instead of 1. Session: {session}");
|
||||
var ent = cache.Value.tree.RootNodes.FirstOrDefault();
|
||||
var nent = cache.Value.tree.RootNodes.FirstOrDefault();
|
||||
var ent = GetEntity(nent);
|
||||
DebugTools.Assert(Exists(ent), $"Root node does not exist. Node {ent}. Session: {session}");
|
||||
DebugTools.Assert(HasComp<MapComponent>(ent) || HasComp<MapGridComponent>(ent));
|
||||
#endif
|
||||
|
||||
foreach (var rootNode in cache.Value.tree.RootNodes)
|
||||
{
|
||||
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, cache.Value.metadata, stack, in fromTick,
|
||||
RecursivelyAddTreeNode(in rootNode, cache.Value.tree, lastAcked, lastSent, visibleEnts, lastSeen, stack, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
}
|
||||
@@ -742,8 +743,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var globalEnumerator = _entityPvsCollection.GlobalOverridesEnumerator;
|
||||
while (globalEnumerator.MoveNext())
|
||||
{
|
||||
var uid = globalEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
var netEntity = globalEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
globalEnumerator.Dispose();
|
||||
@@ -752,8 +754,9 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var globalRecursiveEnumerator = _entityPvsCollection.GlobalRecursiveOverridesEnumerator;
|
||||
while (globalRecursiveEnumerator.MoveNext())
|
||||
{
|
||||
var uid = globalRecursiveEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
var netEntity = globalRecursiveEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
globalRecursiveEnumerator.Dispose();
|
||||
@@ -761,15 +764,16 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
|
||||
while (localEnumerator.MoveNext())
|
||||
{
|
||||
var uid = localEnumerator.Current;
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
var netEntity = localEnumerator.Current;
|
||||
var uid = GetEntity(netEntity);
|
||||
RecursivelyAddOverride(in uid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
localEnumerator.Dispose();
|
||||
|
||||
foreach (var viewerEntity in viewers)
|
||||
{
|
||||
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
RecursivelyAddOverride(in viewerEntity, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
@@ -777,27 +781,32 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
RaiseLocalEvent(ref expandEvent);
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in mQuery, in tQuery, in fromTick,
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
|
||||
var entityStates = new List<EntityState>(entStateCount);
|
||||
|
||||
foreach (var (uid, visiblity) in visibleEnts)
|
||||
foreach (var (netEntity, visiblity) in visibleEnts)
|
||||
{
|
||||
var uid = GetEntity(netEntity);
|
||||
|
||||
#if DEBUG
|
||||
// if an entity is visible, its parents should always be visible.
|
||||
DebugTools.Assert((tQuery.GetComponent(uid).ParentUid is not { Valid: true } parent) || visibleEnts.ContainsKey(parent),
|
||||
DebugTools.Assert((_xformQuery.GetComponent(uid).ParentUid is not { Valid: true } parent) ||
|
||||
visibleEnts.ContainsKey(_metaQuery.GetComponent(parent).NetEntity),
|
||||
$"Attempted to send an entity without sending it's parents. Entity: {ToPrettyString(uid)}.");
|
||||
#endif
|
||||
|
||||
if (sessionData.RequestedFull)
|
||||
{
|
||||
entityStates.Add(GetFullEntityState(session, uid, mQuery.GetComponent(uid)));
|
||||
entityStates.Add(GetFullEntityState(session, uid, _metaQuery.GetComponent(uid)));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -805,8 +814,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
var entered = visiblity == PvsEntityVisibility.Entered;
|
||||
var entFromTick = entered ? lastSeen.GetValueOrDefault(uid) : fromTick;
|
||||
var state = GetEntityState(session, uid, entFromTick, mQuery.GetComponent(uid));
|
||||
var entFromTick = entered ? lastSeen.GetValueOrDefault(netEntity) : fromTick;
|
||||
var state = GetEntityState(session, uid, entFromTick, _metaQuery.GetComponent(uid));
|
||||
|
||||
if (entered || !state.Empty)
|
||||
entityStates.Add(state);
|
||||
@@ -850,31 +859,30 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
/// Figure out what entities are no longer visible to the client. These entities are sent reliably to the client
|
||||
/// in a separate net message.
|
||||
/// </summary>
|
||||
private List<EntityUid>? ProcessLeavePVS(
|
||||
Dictionary<EntityUid, PvsEntityVisibility> visibleEnts,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent)
|
||||
private List<NetEntity>? ProcessLeavePVS(
|
||||
Dictionary<NetEntity, PvsEntityVisibility> visibleEnts,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent)
|
||||
{
|
||||
if (lastSent == null)
|
||||
return null;
|
||||
|
||||
var leftView = new List<EntityUid>();
|
||||
foreach (var uid in lastSent.Keys)
|
||||
var leftView = new List<NetEntity>();
|
||||
foreach (var netEntity in lastSent.Keys)
|
||||
{
|
||||
if (!visibleEnts.ContainsKey(uid))
|
||||
leftView.Add(uid);
|
||||
if (!visibleEnts.ContainsKey(netEntity))
|
||||
leftView.Add(netEntity);
|
||||
}
|
||||
|
||||
return leftView.Count > 0 ? leftView : null;
|
||||
}
|
||||
|
||||
private void RecursivelyAddTreeNode(in EntityUid nodeIndex,
|
||||
RobustTree<EntityUid> tree,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
Dictionary<EntityUid, MetaDataComponent> metaDataCache,
|
||||
Stack<EntityUid> stack,
|
||||
private void RecursivelyAddTreeNode(in NetEntity nodeIndex,
|
||||
RobustTree<NetEntity> tree,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
Stack<NetEntity> stack,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -899,7 +907,8 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (!shouldAdd)
|
||||
continue;
|
||||
|
||||
AddToSendSet(in currentNodeIndex, metaDataCache[currentNodeIndex], toSend, fromTick, in entered, ref entStateCount);
|
||||
var entity = GetEntity(currentNodeIndex);
|
||||
AddToSendSet(in currentNodeIndex, _metaQuery.GetComponent(entity), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
var node = tree[currentNodeIndex];
|
||||
@@ -914,12 +923,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
public bool RecursivelyAddOverride(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
in EntityQuery<MetaDataComponent> metaQuery,
|
||||
in EntityQuery<TransformComponent> transQuery,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -933,25 +940,31 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (!uid.IsValid())
|
||||
return false;
|
||||
|
||||
var xform = transQuery.GetComponent(uid);
|
||||
var xform = _xformQuery.GetComponent(uid);
|
||||
var parent = xform.ParentUid;
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in metaQuery, in transQuery, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget))
|
||||
if (parent.IsValid() && !RecursivelyAddOverride(in parent, lastAcked, lastSent, toSend, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget,
|
||||
in enteredEntityBudget))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var metadata = _metaQuery.GetComponent(uid);
|
||||
var netEntity = GetNetEntity(uid, metadata);
|
||||
|
||||
//did we already get added?
|
||||
// Note that we check this AFTER adding parents. This is because while this entity may already have been added
|
||||
// to the toSend set, it doesn't guarantee that its parents have been. E.g., if a player ghost just teleported
|
||||
// to follow a far away entity, the player's own entity is still being sent, but we need to ensure that we also
|
||||
// send the new parents, which may otherwise be delayed because of the PVS budget..
|
||||
if (!toSend.ContainsKey(uid))
|
||||
if (!toSend.ContainsKey(netEntity))
|
||||
{
|
||||
// TODO PERFORMANCE.
|
||||
// ProcessEntry() unnecessarily checks lastSent.ContainsKey() and maybe lastSeen.Contains(). Given that at this
|
||||
// point the budgets are just ignored, this should just bypass those checks. But then again 99% of the time this
|
||||
// is just the player's own entity + maybe a singularity. So currently not all that performance intensive.
|
||||
var (entered, _) = ProcessEntry(in uid, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in uid, metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
var (entered, _) = ProcessEntry(in netEntity, lastAcked, lastSent, lastSeen, ref newEntityCount, ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
AddToSendSet(in netEntity, _metaQuery.GetComponent(uid), toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
if (addChildren)
|
||||
@@ -964,10 +977,10 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
|
||||
private void RecursivelyAddChildren(TransformComponent xform,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, PvsEntityVisibility> toSend,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, PvsEntityVisibility> toSend,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
in GameTick fromTick,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
@@ -980,12 +993,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (!_xformQuery.TryGetComponent(child, out var childXform))
|
||||
continue;
|
||||
|
||||
if (!toSend.ContainsKey(child))
|
||||
var metadata = _metaQuery.GetComponent(child);
|
||||
var childNetEntity = GetNetEntity(child, metadata);
|
||||
|
||||
if (!toSend.ContainsKey(childNetEntity))
|
||||
{
|
||||
var (entered, _) = ProcessEntry(in child, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
var (entered, _) = ProcessEntry(in childNetEntity, lastAcked, lastSent, lastSeen, ref newEntityCount,
|
||||
ref enteredEntityCount, newEntityBudget, enteredEntityBudget);
|
||||
|
||||
AddToSendSet(in child, _metaQuery.GetComponent(child), toSend, fromTick, in entered, ref entStateCount);
|
||||
AddToSendSet(in childNetEntity, metadata, toSend, fromTick, in entered, ref entStateCount);
|
||||
}
|
||||
|
||||
RecursivelyAddChildren(childXform, lastAcked, lastSent, toSend, lastSeen, fromTick, ref newEntityCount,
|
||||
@@ -993,19 +1009,20 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(in EntityUid uid,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<EntityUid, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<EntityUid, GameTick> lastSeen,
|
||||
private (bool Entered, bool ShouldAdd) ProcessEntry(
|
||||
in NetEntity netEntity,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastAcked,
|
||||
Dictionary<NetEntity, PvsEntityVisibility>? lastSent,
|
||||
Dictionary<NetEntity, GameTick> lastSeen,
|
||||
ref int newEntityCount,
|
||||
ref int enteredEntityCount,
|
||||
in int newEntityBudget,
|
||||
in int enteredEntityBudget)
|
||||
{
|
||||
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(uid);
|
||||
var enteredSinceLastSent = lastSent == null || !lastSent.ContainsKey(netEntity);
|
||||
|
||||
var entered = enteredSinceLastSent || // OR, entered since last ack:
|
||||
lastAcked == null || !lastAcked.ContainsKey(uid);
|
||||
lastAcked == null || !lastAcked.ContainsKey(netEntity);
|
||||
|
||||
// If the entity is entering, but we already sent this entering entity in the last message, we won't add it to
|
||||
// the budget. Chances are the packet will arrive in a nice and orderly fashion, and the client will stick to
|
||||
@@ -1020,25 +1037,25 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
return (entered, false);
|
||||
|
||||
enteredEntityCount++;
|
||||
if (!lastSeen.ContainsKey(uid))
|
||||
if (!lastSeen.ContainsKey(netEntity))
|
||||
newEntityCount++;
|
||||
}
|
||||
|
||||
return (entered, true);
|
||||
}
|
||||
|
||||
private void AddToSendSet(in EntityUid uid, MetaDataComponent metaDataComponent, Dictionary<EntityUid, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
|
||||
private void AddToSendSet(in NetEntity netEntity, MetaDataComponent metaDataComponent, Dictionary<NetEntity, PvsEntityVisibility> toSend, GameTick fromTick, in bool entered, ref int entStateCount)
|
||||
{
|
||||
if (metaDataComponent.EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
var rep = new EntityStringRepresentation(uid, metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
|
||||
var rep = new EntityStringRepresentation(GetEntity(netEntity), metaDataComponent.EntityDeleted, metaDataComponent.EntityName, metaDataComponent.EntityPrototype?.ID);
|
||||
Log.Error($"Attempted to add a deleted entity to PVS send set: '{rep}'. Trace:\n{Environment.StackTrace}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entered)
|
||||
{
|
||||
toSend.Add(uid, PvsEntityVisibility.Entered);
|
||||
toSend.Add(netEntity, PvsEntityVisibility.Entered);
|
||||
entStateCount++;
|
||||
return;
|
||||
}
|
||||
@@ -1046,22 +1063,22 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
if (metaDataComponent.EntityLastModifiedTick <= fromTick)
|
||||
{
|
||||
//entity has been sent before and hasnt been updated since
|
||||
toSend.Add(uid, PvsEntityVisibility.StayedUnchanged);
|
||||
toSend.Add(netEntity, PvsEntityVisibility.StayedUnchanged);
|
||||
return;
|
||||
}
|
||||
|
||||
//add us
|
||||
toSend.Add(uid, PvsEntityVisibility.StayedChanged);
|
||||
toSend.Add(netEntity, PvsEntityVisibility.StayedChanged);
|
||||
entStateCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entity states that have been modified after and including the provided tick.
|
||||
/// </summary>
|
||||
public (List<EntityState>?, List<EntityUid>?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
|
||||
public (List<EntityState>?, List<NetEntity>?, GameTick fromTick) GetAllEntityStates(ICommonSession? player, GameTick fromTick, GameTick toTick)
|
||||
{
|
||||
List<EntityState>? stateEntities;
|
||||
var toSend = _uidSetPool.Get();
|
||||
var toSend = new HashSet<EntityUid>();
|
||||
DebugTools.Assert(toSend.Count == 0);
|
||||
bool enumerateAll = false;
|
||||
|
||||
@@ -1096,7 +1113,7 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
if (state.Empty)
|
||||
{
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
|
||||
Log.Error($@"{nameof(GetEntityState)} returned an empty state while enumerating entities.
|
||||
Tick: {fromTick}--{_gameTiming.CurTick}
|
||||
Entity: {ToPrettyString(uid)}
|
||||
Last modified: {md.EntityLastModifiedTick}
|
||||
@@ -1163,7 +1180,6 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
}
|
||||
|
||||
_uidSetPool.Return(toSend);
|
||||
var deletions = _entityPvsCollection.GetDeletedIndices(fromTick);
|
||||
|
||||
if (stateEntities.Count == 0)
|
||||
@@ -1221,7 +1237,8 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
|
||||
DebugTools.Assert(meta.EntityLastModifiedTick >= meta.LastComponentRemoved);
|
||||
var entState = new EntityState(entityUid, changed, meta.EntityLastModifiedTick, netComps);
|
||||
DebugTools.Assert(GetEntity(meta.NetEntity) == entityUid);
|
||||
var entState = new EntityState(meta.NetEntity, changed, meta.EntityLastModifiedTick, netComps);
|
||||
|
||||
return entState;
|
||||
}
|
||||
@@ -1253,7 +1270,7 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
netComps.Add(netId);
|
||||
}
|
||||
|
||||
var entState = new EntityState(entityUid, changed, meta.EntityLastModifiedTick, netComps);
|
||||
var entState = new EntityState(meta.NetEntity, changed, meta.EntityLastModifiedTick, netComps);
|
||||
|
||||
return entState;
|
||||
}
|
||||
@@ -1263,14 +1280,13 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
if (session.Status != SessionStatus.InGame)
|
||||
return Array.Empty<EntityUid>();
|
||||
|
||||
var viewers = _uidSetPool.Get();
|
||||
var viewers = new HashSet<EntityUid>();
|
||||
|
||||
if (session.AttachedEntity != null)
|
||||
{
|
||||
// Fast path
|
||||
if (session is IPlayerSession { ViewSubscriptionCount: 0 })
|
||||
{
|
||||
_uidSetPool.Return(viewers);
|
||||
return new[] { session.AttachedEntity.Value };
|
||||
}
|
||||
|
||||
@@ -1288,7 +1304,6 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
|
||||
var viewerArray = viewers.ToArray();
|
||||
|
||||
_uidSetPool.Return(viewers);
|
||||
return viewerArray;
|
||||
}
|
||||
|
||||
@@ -1336,23 +1351,23 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
/// <summary>
|
||||
/// All <see cref="EntityUid"/>s that this session saw during the last <see cref="DirtyBufferSize"/> ticks.
|
||||
/// </summary>
|
||||
public readonly OverflowDictionary<GameTick, Dictionary<EntityUid, PvsEntityVisibility>> SentEntities = new(DirtyBufferSize);
|
||||
public readonly OverflowDictionary<GameTick, Dictionary<NetEntity, PvsEntityVisibility>> SentEntities = new(DirtyBufferSize);
|
||||
|
||||
/// <summary>
|
||||
/// The most recently acked entities
|
||||
/// </summary>
|
||||
public (GameTick Tick, Dictionary<EntityUid, PvsEntityVisibility> Data)? LastAcked;
|
||||
public (GameTick Tick, Dictionary<NetEntity, PvsEntityVisibility> Data)? LastAcked;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the last tick at which a given entity was acked by a player. Used to avoid re-sending the whole entity
|
||||
/// state when an item re-enters PVS.
|
||||
/// </summary>
|
||||
public readonly Dictionary<EntityUid, GameTick> LastSeenAt = new();
|
||||
public readonly Dictionary<NetEntity, GameTick> LastSeenAt = new();
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SentEntities"/> overflow in case a player's last ack is more than <see cref="DirtyBufferSize"/> ticks behind the current tick.
|
||||
/// </summary>
|
||||
public (GameTick Tick, Dictionary<EntityUid, PvsEntityVisibility> SentEnts)? Overflow;
|
||||
public (GameTick Tick, Dictionary<NetEntity, PvsEntityVisibility> SentEnts)? Overflow;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the client has explicitly requested a full state. Unlike the first state, we will send them
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Robust.Server.GameStates
|
||||
public ushort TransformNetId { get; set; }
|
||||
|
||||
public Action<ICommonSession, GameTick>? ClientAck { get; set; }
|
||||
public Action<ICommonSession, GameTick, EntityUid?>? ClientRequestFull { get; set; }
|
||||
public Action<ICommonSession, GameTick, NetEntity?>? ClientRequestFull { get; set; }
|
||||
|
||||
public void PostInject()
|
||||
{
|
||||
@@ -134,7 +134,7 @@ namespace Robust.Server.GameStates
|
||||
if (!_playerManager.TryGetSessionById(msg.MsgChannel.UserId, out var session))
|
||||
return;
|
||||
|
||||
EntityUid? ent = msg.MissingEntity.IsValid() ? msg.MissingEntity : null;
|
||||
NetEntity? ent = msg.MissingEntity.IsValid() ? msg.MissingEntity : null;
|
||||
ClientRequestFull?.Invoke(session, msg.Tick, ent);
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ namespace Robust.Server.GameStates
|
||||
{
|
||||
public HashSet<int>[] PlayerChunks;
|
||||
public EntityUid[][] ViewerEntities;
|
||||
public (Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[] ChunkCache;
|
||||
public (Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[] ChunkCache;
|
||||
}
|
||||
|
||||
private PvsData? GetPVSData(IPlayerSession[] players)
|
||||
@@ -255,13 +255,11 @@ namespace Robust.Server.GameStates
|
||||
var chunksCount = chunks.Count;
|
||||
var chunkBatches = (int)MathF.Ceiling((float)chunksCount / ChunkBatchSize);
|
||||
var chunkCache =
|
||||
new (Dictionary<EntityUid, MetaDataComponent> metadata, RobustTree<EntityUid> tree)?[chunksCount];
|
||||
new (Dictionary<NetEntity, MetaDataComponent> metadata, RobustTree<NetEntity> tree)?[chunksCount];
|
||||
|
||||
// Update the reused trees sequentially to avoid having to lock the dictionary per chunk.
|
||||
var reuse = ArrayPool<bool>.Shared.Rent(chunksCount);
|
||||
|
||||
var transformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
var metadataQuery = _entityManager.GetEntityQuery<MetaDataComponent>();
|
||||
Parallel.For(0, chunkBatches,
|
||||
new ParallelOptions { MaxDegreeOfParallelism = _parallelMgr.ParallelProcessCount },
|
||||
i =>
|
||||
@@ -272,8 +270,7 @@ namespace Robust.Server.GameStates
|
||||
for (var j = start; j < end; ++j)
|
||||
{
|
||||
var (visMask, chunkIndexLocation) = chunks[j];
|
||||
reuse[j] = _pvs.TryCalculateChunk(chunkIndexLocation, visMask, transformQuery, metadataQuery,
|
||||
out var chunk);
|
||||
reuse[j] = _pvs.TryCalculateChunk(chunkIndexLocation, visMask, out var chunk);
|
||||
chunkCache[j] = chunk;
|
||||
|
||||
#if DEBUG
|
||||
@@ -283,7 +280,8 @@ namespace Robust.Server.GameStates
|
||||
// Each root nodes should simply be a map or a grid entity.
|
||||
DebugTools.Assert(chunk.Value.tree.RootNodes.Count == 1,
|
||||
$"Root node count is {chunk.Value.tree.RootNodes.Count} instead of 1.");
|
||||
var ent = chunk.Value.tree.RootNodes.FirstOrDefault();
|
||||
var nent = chunk.Value.tree.RootNodes.FirstOrDefault();
|
||||
var ent = _entityManager.GetEntity(nent);
|
||||
DebugTools.Assert(_entityManager.EntityExists(ent), $"Root node does not exist. Node {ent}.");
|
||||
DebugTools.Assert(_entityManager.HasComponent<MapComponent>(ent)
|
||||
|| _entityManager.HasComponent<MapGridComponent>(ent));
|
||||
@@ -313,9 +311,9 @@ namespace Robust.Server.GameStates
|
||||
var channel = session.ConnectedClient;
|
||||
var sessionData = _pvs.PlayerData[session];
|
||||
var lastAck = sessionData.LastReceivedAck;
|
||||
List<EntityUid>? leftPvs = null;
|
||||
List<NetEntity>? leftPvs = null;
|
||||
List<EntityState>? entStates;
|
||||
List<EntityUid>? deletions;
|
||||
List<NetEntity>? deletions;
|
||||
GameTick fromTick;
|
||||
|
||||
DebugTools.Assert(_pvs.CullingEnabled == (pvsData != null));
|
||||
@@ -325,8 +323,6 @@ namespace Robust.Server.GameStates
|
||||
session,
|
||||
lastAck,
|
||||
_gameTiming.CurTick,
|
||||
mQuery,
|
||||
tQuery,
|
||||
pvsData.Value.ChunkCache,
|
||||
pvsData.Value.PlayerChunks[i],
|
||||
pvsData.Value.ViewerEntities[i]);
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Robust.Server.Physics
|
||||
|
||||
var msg = new ChunkSplitDebugMessage
|
||||
{
|
||||
Grid = uid,
|
||||
Grid = GetNetEntity(uid),
|
||||
};
|
||||
|
||||
foreach (var (index, group) in _nodes[uid])
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,8 +98,10 @@ namespace Robust.Server.Placement
|
||||
return;
|
||||
|
||||
//TODO: Distance check, so you can't place things off of screen.
|
||||
// I don't think that's this manager's biggest problem
|
||||
|
||||
var coordinates = msg.EntityCoordinates;
|
||||
var netCoordinates = msg.NetCoordinates;
|
||||
var coordinates = _entityManager.GetCoordinates(netCoordinates);
|
||||
|
||||
if (!coordinates.IsValid(_entityManager))
|
||||
{
|
||||
@@ -211,17 +213,19 @@ namespace Robust.Server.Placement
|
||||
private void HandleEntRemoveReq(MsgPlacement msg)
|
||||
{
|
||||
//TODO: Some form of admin check
|
||||
if (!_entityManager.EntityExists(msg.EntityUid))
|
||||
var entity = _entityManager.GetEntity(msg.EntityUid);
|
||||
|
||||
if (!_entityManager.EntityExists(entity))
|
||||
return;
|
||||
|
||||
var placementEraseEvent = new PlacementEntityEvent(msg.EntityUid, _entityManager.GetComponent<TransformComponent>(msg.EntityUid).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
|
||||
var placementEraseEvent = new PlacementEntityEvent(entity, _entityManager.GetComponent<TransformComponent>(entity).Coordinates, PlacementEventAction.Erase, msg.MsgChannel.UserId);
|
||||
_entityManager.EventBus.RaiseEvent(EventSource.Local, placementEraseEvent);
|
||||
_entityManager.DeleteEntity(msg.EntityUid);
|
||||
_entityManager.DeleteEntity(entity);
|
||||
}
|
||||
|
||||
private void HandleRectRemoveReq(MsgPlacement msg)
|
||||
{
|
||||
EntityCoordinates start = msg.EntityCoordinates;
|
||||
EntityCoordinates start = _entityManager.GetCoordinates(msg.NetCoordinates);
|
||||
Vector2 rectSize = msg.RectSize;
|
||||
foreach (EntityUid entity in EntitySystem.Get<EntityLookupSystem>().GetEntitiesIntersecting(start.GetMapId(_entityManager),
|
||||
new Box2(start.Position, start.Position + rectSize)))
|
||||
|
||||
@@ -210,7 +210,7 @@ namespace Robust.Server.Player
|
||||
{
|
||||
PlayerState.Status = Status;
|
||||
PlayerState.Name = Name;
|
||||
PlayerState.ControlledEntity = AttachedEntity;
|
||||
PlayerState.ControlledEntity = IoCManager.Resolve<IEntityManager>().GetNetEntity(AttachedEntity);
|
||||
|
||||
_playerManager.Dirty();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -14,7 +14,8 @@ namespace Robust.Server.ViewVariables.Traits
|
||||
|
||||
public ViewVariablesTraitEntity(IViewVariablesSession session) : base(session)
|
||||
{
|
||||
_entity = (EntityUid) Session.Object;
|
||||
var netEntity = (NetEntity) Session.Object;
|
||||
_entity = IoCManager.Resolve<IEntityManager>().GetEntity(netEntity);
|
||||
}
|
||||
|
||||
public override ViewVariablesBlob? DataRequest(ViewVariablesRequest viewVariablesRequest)
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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}:");
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ public sealed class SpawnCommand : LocalizedCommands
|
||||
}
|
||||
else if (args.Length == 2)
|
||||
{
|
||||
var uid = EntityUid.Parse(args[1]);
|
||||
var entityCoordinates = _entityManager.GetComponent<TransformComponent>(uid).Coordinates;
|
||||
var uidNet = NetEntity.Parse(args[1]);
|
||||
var entityCoordinates = _entityManager.GetComponent<TransformComponent>(_entityManager.GetEntity(uidNet)).Coordinates;
|
||||
var createdEntity = _entityManager.SpawnEntity(args[0], entityCoordinates);
|
||||
placementEv = new PlacementEntityEvent(createdEntity, entityCoordinates, PlacementEventAction.Create, shell.Player?.UserId);
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public sealed class TeleportToCommand : LocalizedCommands
|
||||
[NotNullWhen(true)] out EntityUid? victimUid,
|
||||
[NotNullWhen(true)] out TransformComponent? transform)
|
||||
{
|
||||
if (EntityUid.TryParse(str, out var uid) && _entities.TryGetComponent(uid, out transform))
|
||||
if (NetEntity.TryParse(str, out var uidNet) && _entities.TryGetEntity(uidNet, out var uid) && _entities.TryGetComponent(uid, out transform))
|
||||
{
|
||||
victimUid = uid;
|
||||
return true;
|
||||
@@ -160,10 +160,10 @@ public sealed class TeleportToCommand : LocalizedCommands
|
||||
hint = Loc.GetString(hint);
|
||||
|
||||
var opts = CompletionResult.FromHintOptions(users, hint);
|
||||
if (last != string.Empty && !EntityUid.TryParse(last, out _))
|
||||
if (last != string.Empty && !NetEntity.TryParse(last, out _))
|
||||
return opts;
|
||||
|
||||
return CompletionResult.FromHintOptions(opts.Options.Concat(CompletionHelper.EntityUids(last, _entities)), hint);
|
||||
return CompletionResult.FromHintOptions(opts.Options.Concat(CompletionHelper.NetEntities(last, _entities)), hint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,11 +201,11 @@ sealed class TpGridCommand : LocalizedCommands
|
||||
return;
|
||||
}
|
||||
|
||||
var gridId = EntityUid.Parse(args[0]);
|
||||
var gridIdNet = NetEntity.Parse(args[0]);
|
||||
var xPos = float.Parse(args[1], CultureInfo.InvariantCulture);
|
||||
var yPos = float.Parse(args[2], CultureInfo.InvariantCulture);
|
||||
|
||||
if (!_ent.EntityExists(gridId))
|
||||
if (!_ent.TryGetEntity(gridIdNet, out var gridId) || !_ent.EntityExists(gridId))
|
||||
{
|
||||
shell.WriteError($"Entity does not exist: {args[0]}");
|
||||
return;
|
||||
@@ -217,7 +217,7 @@ sealed class TpGridCommand : LocalizedCommands
|
||||
return;
|
||||
}
|
||||
|
||||
var gridXform = _ent.GetComponent<TransformComponent>(gridId);
|
||||
var gridXform = _ent.GetComponent<TransformComponent>(gridId.Value);
|
||||
var mapId = args.Length == 4 ? new MapId(int.Parse(args[3])) : gridXform.MapID;
|
||||
|
||||
gridXform.Coordinates = new EntityCoordinates(_map.GetMapEntityId(mapId), new Vector2(xPos, yPos));
|
||||
|
||||
@@ -155,18 +155,21 @@ public static class CompletionHelper
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<CompletionOption> EntityUids(string text, IEntityManager? entManager = null)
|
||||
public static IEnumerable<CompletionOption> NetEntities(string text, IEntityManager? entManager = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entManager);
|
||||
|
||||
foreach (var ent in entManager.GetEntities())
|
||||
{
|
||||
var entString = ent.ToString();
|
||||
|
||||
if (!entString.StartsWith(text))
|
||||
if (!entManager.TryGetNetEntity(ent, out var netEntity))
|
||||
continue;
|
||||
|
||||
yield return new CompletionOption(entString);
|
||||
var netString = netEntity.Value.ToString();
|
||||
|
||||
if (!netString.StartsWith(text))
|
||||
continue;
|
||||
|
||||
yield return new CompletionOption(netString);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,14 +177,19 @@ public static class CompletionHelper
|
||||
{
|
||||
IoCManager.Resolve(ref entManager);
|
||||
|
||||
var query = entManager.AllEntityQueryEnumerator<T>();
|
||||
var query = entManager.AllEntityQueryEnumerator<T, MetaDataComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out _))
|
||||
while (query.MoveNext(out var uid, out _, out var metadata))
|
||||
{
|
||||
if (!uid.ToString().StartsWith(text))
|
||||
if (!entManager.TryGetNetEntity(uid, out var netEntity, metadata: metadata))
|
||||
continue;
|
||||
|
||||
yield return new CompletionOption(uid.ToString());
|
||||
var netString = netEntity.Value.ToString();
|
||||
|
||||
if (!netString.StartsWith(text))
|
||||
continue;
|
||||
|
||||
yield return new CompletionOption(netString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,57 +12,85 @@ using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// Base container class that all container inherit from.
|
||||
/// </summary>
|
||||
public abstract partial class BaseContainer : IContainer
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class BaseContainer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Readonly collection of all the entities contained within this specific container
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public abstract IReadOnlyList<EntityUid> ContainedEntities { get; }
|
||||
|
||||
[ViewVariables]
|
||||
public abstract List<EntityUid> ExpectedEntities { get; }
|
||||
/// <summary>
|
||||
/// Number of contained entities.
|
||||
/// </summary>
|
||||
public abstract int Count { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract string ContainerType { get; }
|
||||
[ViewVariables, NonSerialized]
|
||||
public List<NetEntity> ExpectedEntities = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public bool Deleted { get; private set; }
|
||||
/// <summary>
|
||||
/// The ID of this container.
|
||||
/// </summary>
|
||||
[ViewVariables, NonSerialized, Access(typeof(SharedContainerSystem), typeof(ContainerManagerComponent))]
|
||||
public string ID = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public string ID { get; internal set; } = default!; // Make sure you set me in init
|
||||
[NonSerialized]
|
||||
internal ContainerManagerComponent Manager = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContainerManagerComponent Manager { get; internal set; } = default!; // Make sure you set me in init
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Prevents light from escaping the container, from ex. a flashlight.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("occludes")]
|
||||
public bool OccludesLight { get; set; } = true;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// The entity that owns this container.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid Owner => Manager.Owner;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Should the contents of this container be shown? False for closed containers like lockers, true for
|
||||
/// things like glass display cases.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("showEnts")]
|
||||
public bool ShowContents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// DO NOT CALL THIS METHOD DIRECTLY!
|
||||
/// You want <see cref="IContainerManager.MakeContainer{T}(string)" /> instead.
|
||||
/// </summary>
|
||||
protected BaseContainer() { }
|
||||
internal void Init(string id, EntityUid owner, ContainerManagerComponent component)
|
||||
{
|
||||
DebugTools.AssertNull(ID);
|
||||
ID = id;
|
||||
Manager = component;
|
||||
|
||||
/// <inheritdoc />
|
||||
// TODO fix container init.
|
||||
// Eventually, we want an owner field, but currently it needs to use component.Owner
|
||||
// Owner = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the entity into this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the insertion is successful, the inserted entity will end up parented to the
|
||||
/// container entity, and the inserted entity's local position will be set to the zero vector.
|
||||
/// </remarks>
|
||||
/// <param name="toinsert">The entity to insert.</param>
|
||||
/// <param name="entMan"></param>
|
||||
/// <returns>False if the entity could not be inserted.</returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if this container is a child of the entity,
|
||||
/// which would cause infinite loops.
|
||||
/// </exception>
|
||||
public bool Insert(
|
||||
EntityUid toinsert,
|
||||
IEntityManager? entMan = null,
|
||||
@@ -72,17 +100,12 @@ namespace Robust.Shared.Containers
|
||||
PhysicsComponent? physics = null,
|
||||
bool force = false)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(transform == null || transform.Owner == toinsert);
|
||||
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
|
||||
DebugTools.Assert(ownerTransform == null || ownerTransform.Owner == Owner);
|
||||
DebugTools.Assert(physics == null || physics.Owner == toinsert);
|
||||
DebugTools.Assert(!ExpectedEntities.Contains(toinsert));
|
||||
IoCManager.Resolve(ref entMan);
|
||||
|
||||
//Verify we can insert into this container
|
||||
if (!force && !CanInsert(toinsert, entMan))
|
||||
return false;
|
||||
DebugTools.Assert(!ExpectedEntities.Contains(entMan.GetNetEntity(toinsert)));
|
||||
|
||||
var physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
|
||||
var transformQuery = entMan.GetEntityQuery<TransformComponent>();
|
||||
@@ -91,6 +114,11 @@ namespace Robust.Shared.Containers
|
||||
// ECS containers when
|
||||
var physicsSys = entMan.EntitySysManager.GetEntitySystem<SharedPhysicsSystem>();
|
||||
var jointSys = entMan.EntitySysManager.GetEntitySystem<SharedJointSystem>();
|
||||
var containerSys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
|
||||
|
||||
//Verify we can insert into this container
|
||||
if (!force && !containerSys.CanInsert(toinsert, this))
|
||||
return false;
|
||||
|
||||
// Please somebody ecs containers
|
||||
var lookupSys = entMan.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
||||
@@ -214,45 +242,22 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
/// <summary>
|
||||
/// Whether the given entity can be inserted into this container.
|
||||
/// </summary>
|
||||
/// <param name="assumeEmpty">Whether to assume that the container is currently empty.</param>
|
||||
protected internal virtual bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan) => true;
|
||||
|
||||
// cannot insert into itself.
|
||||
if (Owner == toinsert)
|
||||
return false;
|
||||
|
||||
IoCManager.Resolve(ref entMan);
|
||||
|
||||
// no, you can't put maps or grids into containers
|
||||
if (entMan.HasComponent<MapComponent>(toinsert) || entMan.HasComponent<MapGridComponent>(toinsert))
|
||||
return false;
|
||||
|
||||
var xformSystem = entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
var xformQuery = entMan.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Crucial, prevent circular insertion.
|
||||
if (xformSystem.ContainsEntity(xformQuery.GetComponent(toinsert), Owner, xformQuery))
|
||||
return false;
|
||||
|
||||
//Improvement: Traverse the entire tree to make sure we are not creating a loop.
|
||||
|
||||
//raise events
|
||||
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(this, toinsert);
|
||||
entMan.EventBus.RaiseLocalEvent(Owner, insertAttemptEvent, true);
|
||||
if (insertAttemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(this, toinsert);
|
||||
entMan.EventBus.RaiseLocalEvent(toinsert, gettingInsertedAttemptEvent, true);
|
||||
if (gettingInsertedAttemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Attempts to remove the entity from this container.
|
||||
/// </summary>
|
||||
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
|
||||
/// destination is not null</param>
|
||||
/// <param name="force">If true, this will not perform can-remove checks.</param>
|
||||
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
|
||||
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
|
||||
/// container, the grid, or the map.</param>
|
||||
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
|
||||
public bool Remove(
|
||||
EntityUid toRemove,
|
||||
IEntityManager? entMan = null,
|
||||
@@ -264,7 +269,6 @@ namespace Robust.Shared.Containers
|
||||
Angle? localRotation = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(!Deleted);
|
||||
DebugTools.AssertNotNull(Manager);
|
||||
DebugTools.Assert(entMan.EntityExists(toRemove));
|
||||
DebugTools.Assert(xform == null || xform.Owner == toRemove);
|
||||
@@ -273,7 +277,8 @@ namespace Robust.Shared.Containers
|
||||
xform ??= entMan.GetComponent<TransformComponent>(toRemove);
|
||||
meta ??= entMan.GetComponent<MetaDataComponent>(toRemove);
|
||||
|
||||
if (!force && !CanRemove(toRemove, entMan))
|
||||
var sys = entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
|
||||
if (!force && !sys.CanRemove(toRemove, this))
|
||||
return false;
|
||||
|
||||
if (force && !Contains(toRemove))
|
||||
@@ -305,7 +310,7 @@ namespace Robust.Shared.Containers
|
||||
else if (reparent)
|
||||
{
|
||||
// Container ECS when.
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedContainerSystem>().AttachParentToContainerOrGrid(xform);
|
||||
sys.AttachParentToContainerOrGrid(xform);
|
||||
if (localRotation != null)
|
||||
entMan.EntitySysManager.GetEntitySystem<SharedTransformSystem>().SetLocalRotation(xform, localRotation.Value);
|
||||
}
|
||||
@@ -336,40 +341,22 @@ namespace Robust.Shared.Containers
|
||||
public void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null)
|
||||
=> Remove(toRemove, entMan, meta: meta, reparent: false, force: true);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool CanRemove(EntityUid toRemove, IEntityManager? entMan = null)
|
||||
{
|
||||
DebugTools.Assert(!Deleted);
|
||||
|
||||
if (!Contains(toRemove))
|
||||
return false;
|
||||
|
||||
IoCManager.Resolve(ref entMan);
|
||||
|
||||
//raise events
|
||||
var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(this, toRemove);
|
||||
entMan.EventBus.RaiseLocalEvent(Owner, removeAttemptEvent, true);
|
||||
if (removeAttemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(this, toRemove);
|
||||
entMan.EventBus.RaiseLocalEvent(toRemove, gettingRemovedAttemptEvent, true);
|
||||
if (gettingRemovedAttemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Checks if the entity is contained in this container.
|
||||
/// This is not recursive, so containers of children are not checked.
|
||||
/// </summary>
|
||||
/// <param name="contained">The entity to check.</param>
|
||||
/// <returns>True if the entity is immediately contained in this container, false otherwise.</returns>
|
||||
public abstract bool Contains(EntityUid contained);
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Clears the container and marks it as deleted.
|
||||
/// </summary>
|
||||
public void Shutdown(IEntityManager? entMan = null, INetManager? netMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan, ref netMan);
|
||||
InternalShutdown(entMan, netMan.IsClient);
|
||||
Manager.Containers.Remove(ID);
|
||||
Deleted = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
@@ -15,27 +17,21 @@ namespace Robust.Shared.Containers
|
||||
/// For example, inventory containers should be modified only through an inventory component.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[SerializedType(ClassName)]
|
||||
[SerializedType(nameof(Container))]
|
||||
public sealed partial class Container : BaseContainer
|
||||
{
|
||||
private const string ClassName = "Container";
|
||||
|
||||
/// <summary>
|
||||
/// The generic container class uses a list of entities
|
||||
/// </summary>
|
||||
[DataField("ents")]
|
||||
[NonSerialized]
|
||||
private List<EntityUid> _containerList = new();
|
||||
|
||||
private readonly List<EntityUid> _expectedEntities = new();
|
||||
public override int Count => _containerList.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IReadOnlyList<EntityUid> ContainedEntities => _containerList;
|
||||
|
||||
public override List<EntityUid> ExpectedEntities => _expectedEntities;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ContainerType => ClassName;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
|
||||
{
|
||||
@@ -56,6 +52,9 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
|
||||
#if DEBUG
|
||||
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
|
||||
return true;
|
||||
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var flags = entMan.GetComponent<MetaDataComponent>(contained).Flags;
|
||||
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, $"Entity has bad container flags. Ent: {entMan.ToPrettyString(contained)}. Container: {ID}, Owner: {entMan.ToPrettyString(Owner)}");
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Robust.Shared.Containers
|
||||
/// <param name="container">The container that this entity is inside of.</param>
|
||||
/// <returns>If a container was found.</returns>
|
||||
[Obsolete("Use ContainerSystem.TryGetContainingContainer() instead")]
|
||||
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out IContainer? container, IEntityManager? entMan = null)
|
||||
public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out BaseContainer? container, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
DebugTools.Assert(entMan.EntityExists(entity));
|
||||
@@ -100,7 +100,7 @@ namespace Robust.Shared.Containers
|
||||
/// <see cref="SharedContainerSystem.EmptyContainer"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.EmptyContainer() instead")]
|
||||
public static void EmptyContainer(this IContainer container, bool force = false, EntityCoordinates? moveTo = null,
|
||||
public static void EmptyContainer(this BaseContainer container, bool force = false, EntityCoordinates? moveTo = null,
|
||||
bool attachToGridOrMap = false, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
@@ -112,7 +112,7 @@ namespace Robust.Shared.Containers
|
||||
/// <see cref="SharedContainerSystem.CleanContainer"/>
|
||||
/// </summary>
|
||||
[Obsolete("Use SharedContainerSystem.CleanContainer() instead")]
|
||||
public static void CleanContainer(this IContainer container, IEntityManager? entMan = null)
|
||||
public static void CleanContainer(this BaseContainer container, IEntityManager? entMan = null)
|
||||
{
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedContainerSystem>()
|
||||
.CleanContainer(container);
|
||||
@@ -147,10 +147,10 @@ namespace Robust.Shared.Containers
|
||||
/// <param name="containerId"></param>
|
||||
/// <returns>The new container.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown if there already is a container with the specified ID.</exception>
|
||||
/// <seealso cref="IContainerManager.MakeContainer{T}(string)" />
|
||||
/// <seealso cref="BaseContainerManager.MakeContainer{T}(string)" />
|
||||
[Obsolete("Use ContainerSystem.MakeContainer() instead")]
|
||||
public static T CreateContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
var containermanager = entMan.EnsureComponent<ContainerManagerComponent>(entity);
|
||||
@@ -159,7 +159,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
|
||||
public static T EnsureContainer<T>(this EntityUid entity, string containerId, IEntityManager? entMan = null)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
return EnsureContainer<T>(entity, containerId, out _, entMan);
|
||||
@@ -167,7 +167,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
[Obsolete("Use ContainerSystem.EnsureContainer() instead")]
|
||||
public static T EnsureContainer<T>(this EntityUid entity, string containerId, out bool alreadyExisted, IEntityManager? entMan = null)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
IoCManager.Resolve(ref entMan);
|
||||
var containerManager = entMan.EnsureComponent<ContainerManagerComponent>(entity);
|
||||
|
||||
@@ -25,18 +25,16 @@ namespace Robust.Shared.Containers
|
||||
[Dependency] private readonly INetManager _netMan = default!;
|
||||
|
||||
[DataField("containers")]
|
||||
public Dictionary<string, IContainer> Containers = new();
|
||||
public Dictionary<string, BaseContainer> Containers = new();
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
// TODO remove ISerializationHooks I guess the IDs can be set by a custom serializer for the dictionary? But
|
||||
// the component??? Maybe other systems need to stop assuming that containers have been initialized during
|
||||
// their own init.
|
||||
// TODO custom type serializer
|
||||
// TODO set owner uid on init.
|
||||
foreach (var (id, container) in Containers)
|
||||
{
|
||||
var baseContainer = (BaseContainer) container;
|
||||
baseContainer.Manager = this;
|
||||
baseContainer.ID = id;
|
||||
container.Manager = this;
|
||||
container.ID = id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,13 +53,20 @@ namespace Robust.Shared.Containers
|
||||
|
||||
/// <inheritdoc />
|
||||
public T MakeContainer<T>(string id)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
return (T) MakeContainer(id, typeof(T));
|
||||
if (HasContainer(id))
|
||||
throw new ArgumentException($"Container with specified ID already exists: '{id}'");
|
||||
|
||||
var container = _dynFactory.CreateInstanceUnchecked<T>(typeof(T), inject: false);
|
||||
container.Init(id, Owner, this);
|
||||
Containers[id] = container;
|
||||
_entMan.Dirty(this);
|
||||
return container;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IContainer GetContainer(string id)
|
||||
public BaseContainer GetContainer(string id)
|
||||
{
|
||||
return Containers[id];
|
||||
}
|
||||
@@ -73,7 +78,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetContainer(string id, [NotNullWhen(true)] out IContainer? container)
|
||||
public bool TryGetContainer(string id, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
var ret = Containers.TryGetValue(id, out var cont);
|
||||
container = cont!;
|
||||
@@ -81,11 +86,11 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out IContainer? container)
|
||||
public bool TryGetContainer(EntityUid entity, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
foreach (var contain in Containers.Values)
|
||||
{
|
||||
if (!contain.Deleted && contain.Contains(entity))
|
||||
if (contain.Contains(entity))
|
||||
{
|
||||
container = contain;
|
||||
return true;
|
||||
@@ -101,7 +106,7 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
foreach (var container in Containers.Values)
|
||||
{
|
||||
if (!container.Deleted && container.Contains(entity)) return true;
|
||||
if (container.Contains(entity)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -125,19 +130,6 @@ namespace Robust.Shared.Containers
|
||||
return true; // If we don't contain the entity, it will always be removed
|
||||
}
|
||||
|
||||
private IContainer MakeContainer(string id, Type type)
|
||||
{
|
||||
if (HasContainer(id)) throw new ArgumentException($"Container with specified ID already exists: '{id}'");
|
||||
|
||||
var container = _dynFactory.CreateInstanceUnchecked<BaseContainer>(type);
|
||||
container.ID = id;
|
||||
container.Manager = this;
|
||||
|
||||
Containers[id] = container;
|
||||
_entMan.Dirty(this);
|
||||
return container;
|
||||
}
|
||||
|
||||
public AllContainersEnumerable GetAllContainers()
|
||||
{
|
||||
return new(this);
|
||||
@@ -156,25 +148,22 @@ namespace Robust.Shared.Containers
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct ContainerData
|
||||
{
|
||||
public readonly string ContainerType;
|
||||
public readonly string Id;
|
||||
public readonly string ContainerType; // TODO remove this. We dont have to send a whole string.
|
||||
public readonly bool ShowContents;
|
||||
public readonly bool OccludesLight;
|
||||
public readonly EntityUid[] ContainedEntities;
|
||||
public readonly NetEntity[] ContainedEntities;
|
||||
|
||||
public ContainerData(string containerType, string id, bool showContents, bool occludesLight, EntityUid[] containedEntities)
|
||||
public ContainerData(string containerType, bool showContents, bool occludesLight, NetEntity[] containedEntities)
|
||||
{
|
||||
ContainerType = containerType;
|
||||
Id = id;
|
||||
ShowContents = showContents;
|
||||
OccludesLight = occludesLight;
|
||||
ContainedEntities = containedEntities;
|
||||
}
|
||||
|
||||
public void Deconstruct(out string type, out string id, out bool showEnts, out bool occludesLight, out EntityUid[] ents)
|
||||
public void Deconstruct(out string type, out bool showEnts, out bool occludesLight, out NetEntity[] ents)
|
||||
{
|
||||
type = ContainerType;
|
||||
id = Id;
|
||||
showEnts = ShowContents;
|
||||
occludesLight = OccludesLight;
|
||||
ents = ContainedEntities;
|
||||
@@ -182,24 +171,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
private partial struct ContainerPrototypeData
|
||||
{
|
||||
[DataField("entities")] public List<EntityUid> Entities = new ();
|
||||
|
||||
[DataField("type")] public string? Type = null;
|
||||
|
||||
// explicit parameterless constructor is required.
|
||||
public ContainerPrototypeData() { }
|
||||
|
||||
public ContainerPrototypeData(List<EntityUid> entities, string type)
|
||||
{
|
||||
Entities = entities;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct AllContainersEnumerable : IEnumerable<IContainer>
|
||||
public readonly struct AllContainersEnumerable : IEnumerable<BaseContainer>
|
||||
{
|
||||
private readonly ContainerManagerComponent? _manager;
|
||||
|
||||
@@ -213,7 +185,7 @@ namespace Robust.Shared.Containers
|
||||
return new(_manager);
|
||||
}
|
||||
|
||||
IEnumerator<IContainer> IEnumerable<IContainer>.GetEnumerator()
|
||||
IEnumerator<BaseContainer> IEnumerable<BaseContainer>.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
@@ -224,9 +196,9 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
}
|
||||
|
||||
public struct AllContainersEnumerator : IEnumerator<IContainer>
|
||||
public struct AllContainersEnumerator : IEnumerator<BaseContainer>
|
||||
{
|
||||
private Dictionary<string, IContainer>.ValueCollection.Enumerator _enumerator;
|
||||
private Dictionary<string, BaseContainer>.ValueCollection.Enumerator _enumerator;
|
||||
|
||||
public AllContainersEnumerator(ContainerManagerComponent? manager)
|
||||
{
|
||||
@@ -238,11 +210,8 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
while (_enumerator.MoveNext())
|
||||
{
|
||||
if (!_enumerator.Current.Deleted)
|
||||
{
|
||||
Current = _enumerator.Current;
|
||||
return true;
|
||||
}
|
||||
Current = _enumerator.Current;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -250,11 +219,11 @@ namespace Robust.Shared.Containers
|
||||
|
||||
void IEnumerator.Reset()
|
||||
{
|
||||
((IEnumerator<IContainer>) _enumerator).Reset();
|
||||
((IEnumerator<BaseContainer>) _enumerator).Reset();
|
||||
}
|
||||
|
||||
[AllowNull]
|
||||
public IContainer Current { get; private set; }
|
||||
public BaseContainer Current { get; private set; }
|
||||
|
||||
object IEnumerator.Current => Current;
|
||||
|
||||
|
||||
@@ -6,24 +6,27 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[SerializedType(ClassName)]
|
||||
[SerializedType(nameof(ContainerSlot))]
|
||||
public sealed partial class ContainerSlot : BaseContainer
|
||||
{
|
||||
private const string ClassName = "ContainerSlot";
|
||||
public override int Count => ContainedEntity == null ? 0 : 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IReadOnlyList<EntityUid> ContainedEntities
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ContainedEntity == null)
|
||||
if (_containedEntity == null)
|
||||
return Array.Empty<EntityUid>();
|
||||
|
||||
_containedEntityArray ??= new[] { _containedEntity.Value };
|
||||
DebugTools.Assert(_containedEntityArray[0] == _containedEntity);
|
||||
return _containedEntityArray;
|
||||
}
|
||||
}
|
||||
@@ -36,39 +39,19 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
_containedEntity = value;
|
||||
if (value != null)
|
||||
_containedEntityArray[0] = value!.Value;
|
||||
{
|
||||
_containedEntityArray ??= new EntityUid[1];
|
||||
_containedEntityArray[0] = value.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override List<EntityUid> ExpectedEntities => _expectedEntities;
|
||||
|
||||
[NonSerialized]
|
||||
private EntityUid? _containedEntity;
|
||||
private readonly List<EntityUid> _expectedEntities = new();
|
||||
|
||||
// Used by ContainedEntities to avoid allocating.
|
||||
private readonly EntityUid[] _containedEntityArray = new EntityUid[1];
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ContainerType => ClassName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null)
|
||||
{
|
||||
return (ContainedEntity == null) && CanInsertIfEmpty(toinsert, entMan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be inserted into this container, assuming that the container slot is empty.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Useful if you need to know whether an item could be inserted into a slot, without having to actually eject
|
||||
/// the currently contained entity first.
|
||||
/// </remarks>
|
||||
/// <param name="toinsert">The entity to attempt to insert.</param>
|
||||
/// <returns>True if the entity could be inserted into an empty slot, false otherwise.</returns>
|
||||
public bool CanInsertIfEmpty(EntityUid toinsert, IEntityManager? entMan = null)
|
||||
{
|
||||
return base.CanInsert(toinsert, entMan);
|
||||
}
|
||||
[NonSerialized]
|
||||
private EntityUid[]? _containedEntityArray;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Contains(EntityUid contained)
|
||||
@@ -77,6 +60,9 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
|
||||
#if DEBUG
|
||||
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
|
||||
return true;
|
||||
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var flags = entMan.GetComponent<MetaDataComponent>(contained).Flags;
|
||||
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0, $"Entity has bad container flags. Ent: {entMan.ToPrettyString(contained)}. Container: {ID}, Owner: {entMan.ToPrettyString(Owner)}");
|
||||
@@ -84,6 +70,9 @@ namespace Robust.Shared.Containers
|
||||
return true;
|
||||
}
|
||||
|
||||
protected internal override bool CanInsert(EntityUid toInsert, bool assumeEmpty, IEntityManager entMan)
|
||||
=> ContainedEntity == null || assumeEmpty;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
|
||||
{
|
||||
@@ -91,7 +80,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
#if DEBUG
|
||||
// TODO make this a proper debug assert when gun code no longer fudges client-side spawn prediction.
|
||||
if (toInsert.IsClientSide() && !Owner.IsClientSide() && Manager.NetSyncEnabled)
|
||||
if (entMan.IsClientSide(toInsert) && !entMan.IsClientSide(Owner) && Manager.NetSyncEnabled)
|
||||
Logger.Warning("Inserting a client-side entity into a networked container slot. This will block the container slot and may cause issues.");
|
||||
#endif
|
||||
ContainedEntity = toInsert;
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace Robust.Shared.Containers;
|
||||
|
||||
public abstract class ContainerAttemptEventBase : CancellableEntityEventArgs
|
||||
{
|
||||
public readonly IContainer Container;
|
||||
public readonly BaseContainer Container;
|
||||
public readonly EntityUid EntityUid;
|
||||
|
||||
public ContainerAttemptEventBase(IContainer container, EntityUid entityUid)
|
||||
public ContainerAttemptEventBase(BaseContainer container, EntityUid entityUid)
|
||||
{
|
||||
Container = container;
|
||||
EntityUid = entityUid;
|
||||
@@ -18,28 +18,44 @@ public abstract class ContainerAttemptEventBase : CancellableEntityEventArgs
|
||||
|
||||
public sealed class ContainerIsInsertingAttemptEvent : ContainerAttemptEventBase
|
||||
{
|
||||
public ContainerIsInsertingAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
|
||||
/// <summary>
|
||||
/// If true, this check should assume that the container is currently empty.
|
||||
/// I.e., could the entity be inserted if the container doesn't contain anything else?
|
||||
/// </summary>
|
||||
public bool AssumeEmpty { get; set; }
|
||||
|
||||
public ContainerIsInsertingAttemptEvent(BaseContainer container, EntityUid entityUid, bool assumeEmpty)
|
||||
: base(container, entityUid)
|
||||
{
|
||||
AssumeEmpty = assumeEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ContainerGettingInsertedAttemptEvent : ContainerAttemptEventBase
|
||||
{
|
||||
public ContainerGettingInsertedAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
|
||||
/// <summary>
|
||||
/// If true, this check should assume that the container is currently empty.
|
||||
/// I.e., could the entity be inserted if the container doesn't contain anything else?
|
||||
/// </summary>
|
||||
public bool AssumeEmpty { get; set; }
|
||||
|
||||
public ContainerGettingInsertedAttemptEvent(BaseContainer container, EntityUid entityUid, bool assumeEmpty)
|
||||
: base(container, entityUid)
|
||||
{
|
||||
AssumeEmpty = assumeEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ContainerIsRemovingAttemptEvent : ContainerAttemptEventBase
|
||||
{
|
||||
public ContainerIsRemovingAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
|
||||
public ContainerIsRemovingAttemptEvent(BaseContainer container, EntityUid entityUid) : base(container, entityUid)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ContainerGettingRemovedAttemptEvent : ContainerAttemptEventBase
|
||||
{
|
||||
public ContainerGettingRemovedAttemptEvent(IContainer container, EntityUid entityUid) : base(container, entityUid)
|
||||
public ContainerGettingRemovedAttemptEvent(BaseContainer container, EntityUid entityUid) : base(container, entityUid)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace Robust.Shared.Containers
|
||||
/// <summary>
|
||||
/// The container being acted upon.
|
||||
/// </summary>
|
||||
public IContainer Container { get; }
|
||||
public BaseContainer Container { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that was removed or inserted from/into the container.
|
||||
/// </summary>
|
||||
public EntityUid Entity { get; }
|
||||
|
||||
protected ContainerModifiedMessage(EntityUid entity, IContainer container)
|
||||
protected ContainerModifiedMessage(EntityUid entity, BaseContainer container)
|
||||
{
|
||||
Entity = entity;
|
||||
Container = container;
|
||||
|
||||
@@ -9,5 +9,5 @@ namespace Robust.Shared.Containers;
|
||||
[PublicAPI]
|
||||
public sealed class EntGotInsertedIntoContainerMessage : ContainerModifiedMessage
|
||||
{
|
||||
public EntGotInsertedIntoContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
|
||||
public EntGotInsertedIntoContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
public readonly EntityUid OldParent;
|
||||
|
||||
public EntInsertedIntoContainerMessage(EntityUid entity, EntityUid oldParent, IContainer container) : base(entity, container)
|
||||
public EntInsertedIntoContainerMessage(EntityUid entity, EntityUid oldParent, BaseContainer container) : base(entity, container)
|
||||
{
|
||||
OldParent = oldParent;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Robust.Shared.Containers
|
||||
[PublicAPI]
|
||||
public sealed class EntRemovedFromContainerMessage : ContainerModifiedMessage
|
||||
{
|
||||
public EntRemovedFromContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
|
||||
public EntRemovedFromContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -18,6 +18,6 @@ namespace Robust.Shared.Containers
|
||||
[PublicAPI]
|
||||
public sealed class EntGotRemovedFromContainerMessage : ContainerModifiedMessage
|
||||
{
|
||||
public EntGotRemovedFromContainerMessage(EntityUid entity, IContainer container) : base(entity, container) { }
|
||||
public EntGotRemovedFromContainerMessage(EntityUid entity, BaseContainer container) : base(entity, container) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Shared.Containers
|
||||
{
|
||||
/// <summary>
|
||||
/// A container is a way to "contain" entities inside other entities, in a logical way.
|
||||
/// This is alike BYOND's <c>contents</c> system, except more advanced.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <p>
|
||||
/// Containers are logical separations of entities contained inside another entity.
|
||||
/// for example, a crate with two separated compartments would have two separate containers.
|
||||
/// If an entity inside compartment A drops something,
|
||||
/// the dropped entity would be placed in compartment A too,
|
||||
/// and compartment B would be completely untouched.
|
||||
/// </p>
|
||||
/// <p>
|
||||
/// Containers are managed by an entity's <see cref="IContainerManager" />,
|
||||
/// and have an ID to be referenced by.
|
||||
/// </p>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IContainerManager" />
|
||||
[PublicAPI]
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public partial interface IContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// Readonly collection of all the entities contained within this specific container
|
||||
/// </summary>
|
||||
IReadOnlyList<EntityUid> ContainedEntities { get; }
|
||||
|
||||
List<EntityUid> ExpectedEntities { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of this container.
|
||||
/// </summary>
|
||||
string ContainerType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the container has been shut down via <see cref="Shutdown" />
|
||||
/// </summary>
|
||||
bool Deleted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ID of this container.
|
||||
/// </summary>
|
||||
string ID { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Prevents light from escaping the container, from ex. a flashlight.
|
||||
/// </summary>
|
||||
bool OccludesLight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity owning this container.
|
||||
/// </summary>
|
||||
EntityUid Owner { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the contents of this container be shown? False for closed containers like lockers, true for
|
||||
/// things like glass display cases.
|
||||
/// </summary>
|
||||
bool ShowContents { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be inserted into this container.
|
||||
/// </summary>
|
||||
/// <param name="toinsert">The entity to attempt to insert.</param>
|
||||
/// <param name="entMan"></param>
|
||||
/// <returns>True if the entity can be inserted, false otherwise.</returns>
|
||||
bool CanInsert(EntityUid toinsert, IEntityManager? entMan = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to insert the entity into this container.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the insertion is successful, the inserted entity will end up parented to the
|
||||
/// container entity, and the inserted entity's local position will be set to the zero vector.
|
||||
/// </remarks>
|
||||
/// <param name="toinsert">The entity to insert.</param>
|
||||
/// <param name="entMan"></param>
|
||||
/// <returns>False if the entity could not be inserted.</returns>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if this container is a child of the entity,
|
||||
/// which would cause infinite loops.
|
||||
/// </exception>
|
||||
bool Insert(EntityUid toinsert,
|
||||
IEntityManager? entMan = null,
|
||||
TransformComponent? transform = null,
|
||||
TransformComponent? ownerTransform = null,
|
||||
MetaDataComponent? meta = null,
|
||||
PhysicsComponent? physics = null,
|
||||
bool force = false);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity can be removed from this container.
|
||||
/// </summary>
|
||||
/// <param name="toremove">The entity to check.</param>
|
||||
/// <param name="entMan"></param>
|
||||
/// <returns>True if the entity can be removed, false otherwise.</returns>
|
||||
bool CanRemove(EntityUid toremove, IEntityManager? entMan = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove the entity from this container.
|
||||
/// </summary>
|
||||
/// <param name="reparent">If false, this operation will not rigger a move or parent change event. Ignored if
|
||||
/// destination is not null</param>
|
||||
/// <param name="force">If true, this will not perform can-remove checks.</param>
|
||||
/// <param name="destination">Where to place the entity after removing. Avoids unnecessary broadphase updates.
|
||||
/// If not specified, and reparent option is true, then the entity will either be inserted into a parent
|
||||
/// container, the grid, or the map.</param>
|
||||
/// <param name="localRotation">Optional final local rotation after removal. Avoids redundant move events.</param>
|
||||
bool Remove(
|
||||
EntityUid toremove,
|
||||
IEntityManager? entMan = null,
|
||||
TransformComponent? xform = null,
|
||||
MetaDataComponent? meta = null,
|
||||
bool reparent = true,
|
||||
bool force = false,
|
||||
EntityCoordinates? destination = null,
|
||||
Angle? localRotation = null);
|
||||
|
||||
[Obsolete("use force option in Remove()")]
|
||||
void ForceRemove(EntityUid toRemove, IEntityManager? entMan = null, MetaDataComponent? meta = null);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the entity is contained in this container.
|
||||
/// This is not recursive, so containers of children are not checked.
|
||||
/// </summary>
|
||||
/// <param name="contained">The entity to check.</param>
|
||||
/// <returns>True if the entity is immediately contained in this container, false otherwise.</returns>
|
||||
bool Contains(EntityUid contained);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the container and marks it as deleted.
|
||||
/// </summary>
|
||||
void Shutdown(IEntityManager? entMan = null, INetManager? netMan = null);
|
||||
}
|
||||
}
|
||||
49
Robust.Shared/Containers/SharedContainerSystem.Insert.cs
Normal file
49
Robust.Shared/Containers/SharedContainerSystem.Insert.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Containers;
|
||||
|
||||
public abstract partial class SharedContainerSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the entity can be inserted into the given container.
|
||||
/// </summary>
|
||||
/// <param name="assumeEmpty">If true, this will check whether the entity could be inserted if the container were
|
||||
/// empty.</param>
|
||||
public bool CanInsert(
|
||||
EntityUid toInsert,
|
||||
BaseContainer container,
|
||||
TransformComponent? toInsertXform = null,
|
||||
bool assumeEmpty = false)
|
||||
{
|
||||
if (container.Owner == toInsert)
|
||||
return false;
|
||||
|
||||
if (!assumeEmpty && container.Contains(toInsert))
|
||||
return false;
|
||||
|
||||
if (!container.CanInsert(toInsert, assumeEmpty, EntityManager))
|
||||
return false;
|
||||
|
||||
if (!TransformQuery.Resolve(toInsert, ref toInsertXform))
|
||||
return false;
|
||||
|
||||
// no, you can't put maps or grids into containers
|
||||
if (_mapQuery.HasComponent(toInsert) || _gridQuery.HasComponent(toInsert))
|
||||
return false;
|
||||
|
||||
// Prevent circular insertion.
|
||||
if (_transform.ContainsEntity(toInsertXform, container.Owner))
|
||||
return false;
|
||||
|
||||
var insertAttemptEvent = new ContainerIsInsertingAttemptEvent(container, toInsert, assumeEmpty);
|
||||
RaiseLocalEvent(container.Owner, insertAttemptEvent, true);
|
||||
if (insertAttemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
var gettingInsertedAttemptEvent = new ContainerGettingInsertedAttemptEvent(container, toInsert, assumeEmpty);
|
||||
RaiseLocalEvent(toInsert, gettingInsertedAttemptEvent, true);
|
||||
|
||||
return !gettingInsertedAttemptEvent.Cancelled;
|
||||
}
|
||||
}
|
||||
26
Robust.Shared/Containers/SharedContainerSystem.Remove.cs
Normal file
26
Robust.Shared/Containers/SharedContainerSystem.Remove.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Containers;
|
||||
|
||||
public abstract partial class SharedContainerSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if the entity can be removed from this container.
|
||||
/// </summary>
|
||||
/// <returns>True if the entity can be removed, false otherwise.</returns>
|
||||
public bool CanRemove(EntityUid toRemove, BaseContainer container)
|
||||
{
|
||||
if (!container.Contains(toRemove))
|
||||
return false;
|
||||
|
||||
//raise events
|
||||
var removeAttemptEvent = new ContainerIsRemovingAttemptEvent(container, toRemove);
|
||||
RaiseLocalEvent(container.Owner, removeAttemptEvent, true);
|
||||
if (removeAttemptEvent.Cancelled)
|
||||
return false;
|
||||
|
||||
var gettingRemovedAttemptEvent = new ContainerGettingRemovedAttemptEvent(container, toRemove);
|
||||
RaiseLocalEvent(toRemove, gettingRemovedAttemptEvent, true);
|
||||
return !gettingRemovedAttemptEvent.Cancelled;
|
||||
}
|
||||
}
|
||||
@@ -13,22 +13,18 @@ public abstract partial class SharedContainerSystem : EntitySystem
|
||||
{
|
||||
private void OnStartupValidation(EntityUid uid, ContainerManagerComponent component, ComponentStartup args)
|
||||
{
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
var jointQuery = GetEntityQuery<JointComponent>();
|
||||
foreach (var cont in component.Containers.Values)
|
||||
{
|
||||
foreach (var ent in cont.ContainedEntities)
|
||||
{
|
||||
if (!metaQuery.TryGetComponent(ent, out var meta))
|
||||
if (!MetaQuery.TryGetComponent(ent, out var meta))
|
||||
{
|
||||
ValidateMissingEntity(uid, cont, ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
var xform = xformQuery.GetComponent(ent);
|
||||
physicsQuery.TryGetComponent(ent, out var physics);
|
||||
var xform = TransformQuery.GetComponent(ent);
|
||||
PhysicsQuery.TryGetComponent(ent, out var physics);
|
||||
|
||||
DebugTools.Assert(xform.ParentUid == uid,
|
||||
$"Entity not parented to its container. Entity: {ToPrettyString(ent)}, parent: {ToPrettyString(uid)}");
|
||||
@@ -46,15 +42,15 @@ public abstract partial class SharedContainerSystem : EntitySystem
|
||||
// entities in containers without having to "re-insert" them.
|
||||
meta.Flags |= MetaDataFlags.InContainer;
|
||||
_lookup.RemoveFromEntityTree(ent, xform);
|
||||
((BaseContainer)cont).RecursivelyUpdatePhysics(ent, xform, physics, _physics, physicsQuery, xformQuery);
|
||||
cont.RecursivelyUpdatePhysics(ent, xform, physics, _physics, PhysicsQuery, TransformQuery);
|
||||
|
||||
// assert children have correct properties
|
||||
ValidateChildren(xform, xformQuery, physicsQuery);
|
||||
ValidateChildren(xform, TransformQuery, PhysicsQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ValidateMissingEntity(EntityUid uid, IContainer cont, EntityUid missing);
|
||||
protected abstract void ValidateMissingEntity(EntityUid uid, BaseContainer cont, EntityUid missing);
|
||||
|
||||
private void ValidateChildren(TransformComponent xform, EntityQuery<TransformComponent> xformQuery, EntityQuery<PhysicsComponent> physicsQuery)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,9 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -16,9 +18,13 @@ namespace Robust.Shared.Containers
|
||||
{
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metas;
|
||||
private EntityQuery<TransformComponent> _xforms;
|
||||
private EntityQuery<MapGridComponent> _gridQuery;
|
||||
private EntityQuery<MapComponent> _mapQuery;
|
||||
protected EntityQuery<MetaDataComponent> MetaQuery;
|
||||
protected EntityQuery<PhysicsComponent> PhysicsQuery;
|
||||
protected EntityQuery<TransformComponent> TransformQuery;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
@@ -29,25 +35,27 @@ namespace Robust.Shared.Containers
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentStartup>(OnStartupValidation);
|
||||
SubscribeLocalEvent<ContainerManagerComponent, ComponentGetState>(OnContainerGetState);
|
||||
|
||||
_metas = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
_xforms = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||
_mapQuery = GetEntityQuery<MapComponent>();
|
||||
MetaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
PhysicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||
TransformQuery = GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
private void OnContainerGetState(EntityUid uid, ContainerManagerComponent component, ref ComponentGetState args)
|
||||
{
|
||||
// naive implementation that just sends the full state of the component
|
||||
Dictionary<string, ContainerManagerComponent.ContainerManagerComponentState.ContainerData> containerSet = new(component.Containers.Count);
|
||||
|
||||
foreach (var container in component.Containers.Values)
|
||||
{
|
||||
var uidArr = new EntityUid[container.ContainedEntities.Count];
|
||||
var uidArr = new NetEntity[container.ContainedEntities.Count];
|
||||
|
||||
for (var index = 0; index < container.ContainedEntities.Count; index++)
|
||||
{
|
||||
uidArr[index] = container.ContainedEntities[index];
|
||||
uidArr[index] = GetNetEntity(container.ContainedEntities[index]);
|
||||
}
|
||||
|
||||
var sContainer = new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.ContainerType, container.ID, container.ShowContents, container.OccludesLight, uidArr);
|
||||
var sContainer = new ContainerManagerComponent.ContainerManagerComponentState.ContainerData(container.GetType().Name, container.ShowContents, container.OccludesLight, uidArr);
|
||||
containerSet.Add(container.ID, sContainer);
|
||||
}
|
||||
|
||||
@@ -59,7 +67,7 @@ namespace Robust.Shared.Containers
|
||||
#region Proxy Methods
|
||||
|
||||
public T MakeContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid); // Happy Vera.
|
||||
@@ -68,7 +76,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
public T EnsureContainer<T>(EntityUid uid, string id, out bool alreadyExisted, ContainerManagerComponent? containerManager = null)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager, false))
|
||||
containerManager = EntityManager.AddComponent<ContainerManagerComponent>(uid);
|
||||
@@ -88,12 +96,12 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
public T EnsureContainer<T>(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
where T : IContainer
|
||||
where T : BaseContainer
|
||||
{
|
||||
return EnsureContainer<T>(uid, id, out _, containerManager);
|
||||
}
|
||||
|
||||
public IContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
public BaseContainer GetContainer(EntityUid uid, string id, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
if (!Resolve(uid, ref containerManager))
|
||||
throw new ArgumentException("Entity does not have a ContainerManagerComponent!", nameof(uid));
|
||||
@@ -109,7 +117,7 @@ namespace Robust.Shared.Containers
|
||||
return containerManager.HasContainer(id);
|
||||
}
|
||||
|
||||
public bool TryGetContainer(EntityUid uid, string id, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null)
|
||||
public bool TryGetContainer(EntityUid uid, string id, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null)
|
||||
{
|
||||
if (Resolve(uid, ref containerManager, false))
|
||||
return containerManager.TryGetContainer(id, out container);
|
||||
@@ -118,7 +126,7 @@ namespace Robust.Shared.Containers
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out IContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
|
||||
public bool TryGetContainingContainer(EntityUid uid, EntityUid containedUid, [NotNullWhen(true)] out BaseContainer? container, ContainerManagerComponent? containerManager = null, bool skipExistCheck = false)
|
||||
{
|
||||
if (Resolve(uid, ref containerManager, false) && (skipExistCheck || EntityManager.EntityExists(containedUid)))
|
||||
return containerManager.TryGetContainer(containedUid, out container);
|
||||
@@ -164,7 +172,7 @@ namespace Robust.Shared.Containers
|
||||
|
||||
#region Container Helpers
|
||||
|
||||
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out IContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
|
||||
public bool TryGetContainingContainer(EntityUid uid, [NotNullWhen(true)] out BaseContainer? container, MetaDataComponent? meta = null, TransformComponent? transform = null)
|
||||
{
|
||||
container = null;
|
||||
|
||||
@@ -227,7 +235,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first instance of a component on the recursive parented containers that hold an entity
|
||||
/// Finds the first instance of a component on the recursive parented containers that hold an entity
|
||||
/// </summary>
|
||||
public bool TryFindComponentOnEntityContainerOrParent<T>(
|
||||
EntityUid uid,
|
||||
@@ -236,13 +244,13 @@ namespace Robust.Shared.Containers
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null) where T : Component
|
||||
{
|
||||
if (!_metas.Resolve(uid, ref meta))
|
||||
if (!MetaQuery.Resolve(uid, ref meta))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
|
||||
return false;
|
||||
|
||||
if (!_xforms.Resolve(uid, ref xform))
|
||||
if (!TransformQuery.Resolve(uid, ref xform))
|
||||
return false;
|
||||
|
||||
if (!xform.ParentUid.Valid)
|
||||
@@ -255,7 +263,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all instances of a component on the recursive parented containers that hold an entity
|
||||
/// Finds all instances of a component on the recursive parented containers that hold an entity
|
||||
/// </summary>
|
||||
public bool TryFindComponentsOnEntityContainerOrParent<T>(
|
||||
EntityUid uid,
|
||||
@@ -264,13 +272,13 @@ namespace Robust.Shared.Containers
|
||||
MetaDataComponent? meta = null,
|
||||
TransformComponent? xform = null) where T : Component
|
||||
{
|
||||
if (!_metas.Resolve(uid, ref meta))
|
||||
if (!MetaQuery.Resolve(uid, ref meta))
|
||||
return foundComponents.Any();
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
|
||||
return foundComponents.Any();
|
||||
|
||||
if (!_xforms.Resolve(uid, ref xform))
|
||||
if (!TransformQuery.Resolve(uid, ref xform))
|
||||
return foundComponents.Any();
|
||||
|
||||
if (!xform.ParentUid.Valid)
|
||||
@@ -335,8 +343,8 @@ namespace Robust.Shared.Containers
|
||||
public bool IsInSameOrTransparentContainer(
|
||||
EntityUid user,
|
||||
EntityUid other,
|
||||
IContainer? userContainer = null,
|
||||
IContainer? otherContainer = null,
|
||||
BaseContainer? userContainer = null,
|
||||
BaseContainer? otherContainer = null,
|
||||
bool userSeeInsideSelf = false)
|
||||
{
|
||||
if (userContainer == null)
|
||||
@@ -371,14 +379,14 @@ namespace Robust.Shared.Containers
|
||||
/// <summary>
|
||||
/// Gets the top-most container in the hierarchy for this entity, if it exists.
|
||||
/// </summary>
|
||||
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out IContainer? container)
|
||||
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform, [NotNullWhen(true)] out BaseContainer? container)
|
||||
{
|
||||
var xformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
||||
return TryGetOuterContainer(uid, xform, out container, xformQuery);
|
||||
}
|
||||
|
||||
public bool TryGetOuterContainer(EntityUid uid, TransformComponent xform,
|
||||
[NotNullWhen(true)] out IContainer? container, EntityQuery<TransformComponent> xformQuery)
|
||||
[NotNullWhen(true)] out BaseContainer? container, EntityQuery<TransformComponent> xformQuery)
|
||||
{
|
||||
container = null;
|
||||
|
||||
@@ -448,7 +456,7 @@ namespace Robust.Shared.Containers
|
||||
/// Attempts to remove all entities in a container. Returns removed entities.
|
||||
/// </summary>
|
||||
public List<EntityUid> EmptyContainer(
|
||||
IContainer container,
|
||||
BaseContainer container,
|
||||
bool force = false,
|
||||
EntityCoordinates? destination = null,
|
||||
bool reparent = true)
|
||||
@@ -470,7 +478,7 @@ namespace Robust.Shared.Containers
|
||||
/// <summary>
|
||||
/// Attempts to remove and delete all entities in a container.
|
||||
/// </summary>
|
||||
public void CleanContainer(IContainer container)
|
||||
public void CleanContainer(BaseContainer container)
|
||||
{
|
||||
foreach (var ent in container.ContainedEntities.ToArray())
|
||||
{
|
||||
@@ -491,7 +499,7 @@ namespace Robust.Shared.Containers
|
||||
transform.AttachToGridOrMap();
|
||||
}
|
||||
|
||||
private bool TryInsertIntoContainer(TransformComponent transform, IContainer container)
|
||||
private bool TryInsertIntoContainer(TransformComponent transform, BaseContainer container)
|
||||
{
|
||||
if (container.Insert(transform.Owner)) return true;
|
||||
|
||||
|
||||
@@ -63,6 +63,13 @@ namespace Robust.Shared.GameObjects
|
||||
[DataField("desc")] internal string? _entityDescription;
|
||||
internal EntityPrototype? _entityPrototype;
|
||||
|
||||
/// <summary>
|
||||
/// Network identifier for this entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[Access(typeof(EntityManager), Other = AccessPermissions.ReadExecute)]
|
||||
public NetEntity NetEntity { get; internal set; } = NetEntity.Invalid;
|
||||
|
||||
/// <summary>
|
||||
/// When this entity was paused, if applicable. Note that this is the actual time, not the duration which gets
|
||||
/// returned by <see cref="MetaDataSystem.GetPauseTime"/>.
|
||||
|
||||
@@ -64,21 +64,29 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract class for local BUI events.
|
||||
/// </summary>
|
||||
public abstract class BaseLocalBoundUserInterfaceEvent : BaseBoundUserInterfaceEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The Entity receiving the message.
|
||||
/// Only set when the message is raised as a directed event.
|
||||
/// </summary>
|
||||
public EntityUid Entity = EntityUid.Invalid;
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public abstract class BoundUserInterfaceMessage : EntityEventArgs
|
||||
/// <summary>
|
||||
/// Abstract class for all BUI events.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public abstract class BaseBoundUserInterfaceEvent : EntityEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The UI of this message.
|
||||
/// Only set when the message is raised as a directed event.
|
||||
/// </summary>
|
||||
public Enum UiKey { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The Entity receiving the message.
|
||||
/// Only set when the message is raised as a directed event.
|
||||
/// </summary>
|
||||
public EntityUid Entity { get; set; } = EntityUid.Invalid;
|
||||
public Enum UiKey = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The session sending or receiving this message.
|
||||
@@ -88,6 +96,19 @@ namespace Robust.Shared.GameObjects
|
||||
public ICommonSession Session = default!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abstract class for networked BUI events.
|
||||
/// </summary>
|
||||
[NetSerializable, Serializable]
|
||||
public abstract class BoundUserInterfaceMessage : BaseBoundUserInterfaceEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The Entity receiving the message.
|
||||
/// Only set when the message is raised as a directed event.
|
||||
/// </summary>
|
||||
public NetEntity Entity { get; set; } = NetEntity.Invalid;
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
internal sealed class UpdateBoundStateMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
@@ -112,11 +133,11 @@ namespace Robust.Shared.GameObjects
|
||||
[Serializable, NetSerializable]
|
||||
internal sealed class BoundUIWrapMessage : EntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Entity;
|
||||
public readonly NetEntity Entity;
|
||||
public readonly BoundUserInterfaceMessage Message;
|
||||
public readonly Enum UiKey;
|
||||
|
||||
public BoundUIWrapMessage(EntityUid entity, BoundUserInterfaceMessage message, Enum uiKey)
|
||||
public BoundUIWrapMessage(NetEntity entity, BoundUserInterfaceMessage message, Enum uiKey)
|
||||
{
|
||||
Message = message;
|
||||
UiKey = uiKey;
|
||||
@@ -129,7 +150,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BoundUIOpenedEvent : BoundUserInterfaceMessage
|
||||
public sealed class BoundUIOpenedEvent : BaseLocalBoundUserInterfaceEvent
|
||||
{
|
||||
public BoundUIOpenedEvent(Enum uiKey, EntityUid uid, ICommonSession session)
|
||||
{
|
||||
@@ -139,7 +160,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class BoundUIClosedEvent : BoundUserInterfaceMessage
|
||||
public sealed class BoundUIClosedEvent : BaseLocalBoundUserInterfaceEvent
|
||||
{
|
||||
public BoundUIClosedEvent(Enum uiKey, EntityUid uid, ICommonSession session)
|
||||
{
|
||||
|
||||
@@ -1065,7 +1065,7 @@ namespace Robust.Shared.GameObjects
|
||||
where TComp1 : Component
|
||||
{
|
||||
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
|
||||
return new EntityQueryEnumerator<TComp1>(trait1, _metaQuery);
|
||||
return new EntityQueryEnumerator<TComp1>(trait1, MetaQuery);
|
||||
}
|
||||
|
||||
public EntityQueryEnumerator<TComp1, TComp2> EntityQueryEnumerator<TComp1, TComp2>()
|
||||
@@ -1074,7 +1074,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
|
||||
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
|
||||
return new EntityQueryEnumerator<TComp1, TComp2>(trait1, trait2, _metaQuery);
|
||||
return new EntityQueryEnumerator<TComp1, TComp2>(trait1, trait2, MetaQuery);
|
||||
}
|
||||
|
||||
public EntityQueryEnumerator<TComp1, TComp2, TComp3> EntityQueryEnumerator<TComp1, TComp2, TComp3>()
|
||||
@@ -1085,7 +1085,7 @@ namespace Robust.Shared.GameObjects
|
||||
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
|
||||
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
|
||||
var trait3 = _entTraitArray[CompIdx.ArrayIndex<TComp3>()];
|
||||
return new EntityQueryEnumerator<TComp1, TComp2, TComp3>(trait1, trait2, trait3, _metaQuery);
|
||||
return new EntityQueryEnumerator<TComp1, TComp2, TComp3>(trait1, trait2, trait3, MetaQuery);
|
||||
}
|
||||
|
||||
public EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4> EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>()
|
||||
@@ -1099,7 +1099,7 @@ namespace Robust.Shared.GameObjects
|
||||
var trait3 = _entTraitArray[CompIdx.ArrayIndex<TComp3>()];
|
||||
var trait4 = _entTraitArray[CompIdx.ArrayIndex<TComp4>()];
|
||||
|
||||
return new EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>(trait1, trait2, trait3, trait4, _metaQuery);
|
||||
return new EntityQueryEnumerator<TComp1, TComp2, TComp3, TComp4>(trait1, trait2, trait3, trait4, MetaQuery);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1121,7 +1121,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
foreach (var t1Comp in comps.Values)
|
||||
{
|
||||
if (t1Comp.Deleted || !_metaQuery.TryGetComponentInternal(t1Comp.Owner, out var metaComp)) continue;
|
||||
if (t1Comp.Deleted || !MetaQuery.TryGetComponentInternal(t1Comp.Owner, out var metaComp)) continue;
|
||||
|
||||
if (metaComp.EntityPaused) continue;
|
||||
|
||||
@@ -1312,7 +1312,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
foreach (var (uid, comp) in comps)
|
||||
{
|
||||
if (comp.Deleted || !_metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) continue;
|
||||
if (comp.Deleted || !MetaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused) continue;
|
||||
|
||||
yield return (uid, comp);
|
||||
}
|
||||
|
||||
585
Robust.Shared/GameObjects/EntityManager.Network.cs
Normal file
585
Robust.Shared/GameObjects/EntityManager.Network.cs
Normal file
@@ -0,0 +1,585 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
// TODO POOLING
|
||||
// Just add overrides that take in an existing collection.
|
||||
|
||||
/// <summary>
|
||||
/// Inverse lookup for net entities.
|
||||
/// Regular lookup uses MetadataComponent.
|
||||
/// </summary>
|
||||
protected readonly Dictionary<NetEntity, EntityUid> NetEntityLookup = new(EntityCapacity);
|
||||
|
||||
/// <summary>
|
||||
/// Clears an old inverse lookup for a particular entityuid.
|
||||
/// Do not call this unless you are sure of what you're doing.
|
||||
/// </summary>
|
||||
internal void ClearNetEntity(NetEntity netEntity)
|
||||
{
|
||||
NetEntityLookup.Remove(netEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the inverse lookup for a particular entityuid.
|
||||
/// Do not call this unless you are sure of what you're doing.
|
||||
/// </summary>
|
||||
internal void SetNetEntity(EntityUid uid, NetEntity netEntity, MetaDataComponent component)
|
||||
{
|
||||
DebugTools.Assert(!NetEntityLookup.ContainsKey(netEntity));
|
||||
NetEntityLookup[netEntity] = uid;
|
||||
component.NetEntity = netEntity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#region NetEntity
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryParseNetEntity(string arg, [NotNullWhen(true)] out EntityUid? entity)
|
||||
{
|
||||
if (!NetEntity.TryParse(arg, out var netEntity) ||
|
||||
!TryGetEntity(netEntity, out entity))
|
||||
{
|
||||
entity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetEntity(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity)
|
||||
{
|
||||
if (NetEntityLookup.TryGetValue(nEntity, out var went))
|
||||
{
|
||||
entity = went;
|
||||
return true;
|
||||
}
|
||||
|
||||
entity = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity)
|
||||
{
|
||||
if (nEntity == null)
|
||||
{
|
||||
entity = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetEntity(nEntity.Value, out entity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetNetEntity(EntityUid uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (uid == EntityUid.Invalid)
|
||||
{
|
||||
netEntity = NetEntity.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO NetEntity figure out why this happens
|
||||
// I wanted this to logMissing but it seems to break a loootttt of dodgy stuff on content.
|
||||
if (MetaQuery.Resolve(uid, ref metadata, false))
|
||||
{
|
||||
netEntity = metadata.NetEntity;
|
||||
return true;
|
||||
}
|
||||
|
||||
netEntity = NetEntity.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetNetEntity(EntityUid? uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (uid == null)
|
||||
{
|
||||
netEntity = NetEntity.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryGetNetEntity(uid.Value, out netEntity, metadata);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity)
|
||||
{
|
||||
// On server we don't want to ensure any reserved entities for later or flag for comp state handling
|
||||
// so this is just GetEntity. Client-side code overrides this method.
|
||||
return GetEntity(nEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid? EnsureEntity<T>(NetEntity? nEntity, EntityUid callerEntity)
|
||||
{
|
||||
if (nEntity == null)
|
||||
return null;
|
||||
|
||||
return EnsureEntity<T>(nEntity.Value, callerEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityUid GetEntity(NetEntity nEntity)
|
||||
{
|
||||
if (nEntity == NetEntity.Invalid)
|
||||
return EntityUid.Invalid;
|
||||
|
||||
return NetEntityLookup.GetValueOrDefault(nEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid? GetEntity(NetEntity? nEntity)
|
||||
{
|
||||
if (nEntity == null)
|
||||
return null;
|
||||
|
||||
return GetEntity(nEntity.Value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetEntity GetNetEntity(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (uid == EntityUid.Invalid)
|
||||
return NetEntity.Invalid;
|
||||
|
||||
if (!MetaQuery.Resolve(uid, ref metadata))
|
||||
return NetEntity.Invalid;
|
||||
|
||||
return metadata.NetEntity;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetEntity? GetNetEntity(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (uid == null)
|
||||
return null;
|
||||
|
||||
return GetNetEntity(uid.Value, metadata);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NetCoordinates
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetCoordinates GetNetCoordinates(EntityCoordinates coordinates, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return new NetCoordinates(GetNetEntity(coordinates.EntityId, metadata), coordinates.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetCoordinates? GetNetCoordinates(EntityCoordinates? coordinates, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (coordinates == null)
|
||||
return null;
|
||||
|
||||
return new NetCoordinates(GetNetEntity(coordinates.Value.EntityId, metadata), coordinates.Value.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates GetCoordinates(NetCoordinates coordinates)
|
||||
{
|
||||
return new EntityCoordinates(GetEntity(coordinates.NetEntity), coordinates.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates? GetCoordinates(NetCoordinates? coordinates)
|
||||
{
|
||||
if (coordinates == null)
|
||||
return null;
|
||||
|
||||
return new EntityCoordinates(GetEntity(coordinates.Value.NetEntity), coordinates.Value.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
// See EnsureEntity
|
||||
return GetCoordinates(netCoordinates);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityCoordinates? EnsureCoordinates<T>(NetCoordinates? netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
if (netCoordinates == null)
|
||||
return null;
|
||||
|
||||
return EnsureCoordinates<T>(netCoordinates.Value, callerEntity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Collection helpers
|
||||
|
||||
/// <inheritdoc />
|
||||
public HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>();
|
||||
entities.EnsureCapacity(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(GetEntity(netEntity));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityUid> GetEntityList(List<NetEntity> netEntities)
|
||||
{
|
||||
var entities = new List<EntityUid>(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(GetEntity(netEntity));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity)
|
||||
{
|
||||
var entities = new HashSet<EntityUid>(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(EnsureEntity<T>(netEntity, callerEntity));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity)
|
||||
{
|
||||
var entities = new List<EntityUid>(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(EnsureEntity<T>(netEntity, callerEntity));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities)
|
||||
{
|
||||
var entities = new List<EntityUid>(netEntities.Count);
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(GetEntity(netEntity));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityUid?> GetEntityList(List<NetEntity?> netEntities)
|
||||
{
|
||||
var entities = new List<EntityUid?>(netEntities.Count);
|
||||
|
||||
foreach (var netEntity in netEntities)
|
||||
{
|
||||
entities.Add(GetEntity(netEntity));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityUid[] GetEntityArray(NetEntity[] netEntities)
|
||||
{
|
||||
var entities = new EntityUid[netEntities.Length];
|
||||
|
||||
for (var i = 0; i < netEntities.Length; i++)
|
||||
{
|
||||
entities[i] = GetEntity(netEntities[i]);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityUid?[] GetEntityArray(NetEntity?[] netEntities)
|
||||
{
|
||||
var entities = new EntityUid?[netEntities.Length];
|
||||
|
||||
for (var i = 0; i < netEntities.Length; i++)
|
||||
{
|
||||
entities[i] = GetEntity(netEntities[i]);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public HashSet<NetEntity> GetNetEntitySet(HashSet<EntityUid> entities)
|
||||
{
|
||||
var newSet = new HashSet<NetEntity>(entities.Count);
|
||||
|
||||
foreach (var ent in entities)
|
||||
{
|
||||
MetaQuery.TryGetComponent(ent, out var metadata);
|
||||
newSet.Add(GetNetEntity(ent, metadata));
|
||||
}
|
||||
|
||||
return newSet;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetEntity> GetNetEntityList(List<EntityUid> entities)
|
||||
{
|
||||
var netEntities = new List<NetEntity>(entities.Count);
|
||||
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
netEntities.Add(GetNetEntity(netEntity));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetEntity> GetNetEntityList(IReadOnlyList<EntityUid> entities)
|
||||
{
|
||||
var netEntities = new List<NetEntity>(entities.Count);
|
||||
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
netEntities.Add(GetNetEntity(netEntity));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetEntity> GetNetEntityList(ICollection<EntityUid> entities)
|
||||
{
|
||||
var netEntities = new List<NetEntity>(entities.Count);
|
||||
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
netEntities.Add(GetNetEntity(netEntity));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetEntity?> GetNetEntityList(List<EntityUid?> entities)
|
||||
{
|
||||
var netEntities = new List<NetEntity?>(entities.Count);
|
||||
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
netEntities.Add(GetNetEntity(netEntity));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetEntity[] GetNetEntityArray(EntityUid[] entities)
|
||||
{
|
||||
var netEntities = new NetEntity[entities.Length];
|
||||
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
netEntities[i] = GetNetEntity(entities[i]);
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetEntity?[] GetNetEntityArray(EntityUid?[] entities)
|
||||
{
|
||||
var netEntities = new NetEntity?[entities.Length];
|
||||
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
netEntities[i] = GetNetEntity(entities[i]);
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public HashSet<EntityCoordinates> GetEntitySet(HashSet<NetCoordinates> netEntities)
|
||||
{
|
||||
var entities = new HashSet<EntityCoordinates>(netEntities.Count);
|
||||
|
||||
foreach (var netCoordinates in netEntities)
|
||||
{
|
||||
entities.Add(GetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityCoordinates> GetEntityList(List<NetCoordinates> netEntities)
|
||||
{
|
||||
var entities = new List<EntityCoordinates>(netEntities.Count);
|
||||
|
||||
foreach (var netCoordinates in netEntities)
|
||||
{
|
||||
entities.Add(GetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityCoordinates> GetEntityList(ICollection<NetCoordinates> netEntities)
|
||||
{
|
||||
var entities = new List<EntityCoordinates>(netEntities.Count);
|
||||
|
||||
foreach (var netCoordinates in netEntities)
|
||||
{
|
||||
entities.Add(GetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<EntityCoordinates?> GetEntityList(List<NetCoordinates?> netEntities)
|
||||
{
|
||||
var entities = new List<EntityCoordinates?>(netEntities.Count);
|
||||
|
||||
foreach (var netCoordinates in netEntities)
|
||||
{
|
||||
entities.Add(GetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates[] GetEntityArray(NetCoordinates[] netEntities)
|
||||
{
|
||||
var entities = new EntityCoordinates[netEntities.Length];
|
||||
|
||||
for (var i = 0; i < netEntities.Length; i++)
|
||||
{
|
||||
entities[i] = GetCoordinates(netEntities[i]);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityCoordinates?[] GetEntityArray(NetCoordinates?[] netEntities)
|
||||
{
|
||||
var entities = new EntityCoordinates?[netEntities.Length];
|
||||
|
||||
for (var i = 0; i < netEntities.Length; i++)
|
||||
{
|
||||
entities[i] = GetCoordinates(netEntities[i]);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public HashSet<NetCoordinates> GetNetCoordinatesSet(HashSet<EntityCoordinates> entities)
|
||||
{
|
||||
var newSet = new HashSet<NetCoordinates>(entities.Count);
|
||||
|
||||
foreach (var coordinates in entities)
|
||||
{
|
||||
newSet.Add(GetNetCoordinates(coordinates));
|
||||
}
|
||||
|
||||
return newSet;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetCoordinates> GetNetCoordinatesList(List<EntityCoordinates> entities)
|
||||
{
|
||||
var netEntities = new List<NetCoordinates>(entities.Count);
|
||||
|
||||
foreach (var netCoordinates in entities)
|
||||
{
|
||||
netEntities.Add(GetNetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetCoordinates> GetNetCoordinatesList(ICollection<EntityCoordinates> entities)
|
||||
{
|
||||
var netEntities = new List<NetCoordinates>(entities.Count);
|
||||
|
||||
foreach (var netCoordinates in entities)
|
||||
{
|
||||
netEntities.Add(GetNetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<NetCoordinates?> GetNetCoordinatesList(List<EntityCoordinates?> entities)
|
||||
{
|
||||
var netEntities = new List<NetCoordinates?>(entities.Count);
|
||||
|
||||
foreach (var netCoordinates in entities)
|
||||
{
|
||||
netEntities.Add(GetNetCoordinates(netCoordinates));
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetCoordinates[] GetNetCoordinatesArray(EntityCoordinates[] entities)
|
||||
{
|
||||
var netEntities = new NetCoordinates[entities.Length];
|
||||
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
netEntities[i] = GetNetCoordinates(entities[i]);
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public NetCoordinates?[] GetNetCoordinatesArray(EntityCoordinates?[] entities)
|
||||
{
|
||||
var netEntities = new NetCoordinates?[entities.Length];
|
||||
|
||||
for (var i = 0; i < entities.Length; i++)
|
||||
{
|
||||
netEntities[i] = GetNetCoordinates(entities[i]);
|
||||
}
|
||||
|
||||
return netEntities;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -13,11 +13,11 @@ public partial class EntityManager
|
||||
// This method will soon be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> SpawnAttachedTo(protoName, coordinates, overrides);
|
||||
|
||||
|
||||
// This method will soon be marked as obsolete.
|
||||
public EntityUid SpawnEntity(string? protoName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
=> Spawn(protoName, coordinates, overrides);
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityUid[] SpawnEntitiesAttachedTo(EntityCoordinates coordinates, params string?[] protoNames)
|
||||
{
|
||||
@@ -100,8 +100,8 @@ public partial class EntityManager
|
||||
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return false;
|
||||
|
||||
if (!_metaQuery.TryGetComponent(target, out var meta))
|
||||
|
||||
if (!MetaQuery.TryGetComponent(target, out var meta))
|
||||
return false;
|
||||
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) == 0)
|
||||
@@ -135,7 +135,7 @@ public partial class EntityManager
|
||||
EntityUid containerUid,
|
||||
string containerId,
|
||||
[NotNullWhen(true)] out EntityUid? uid,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ContainerManagerComponent? containerComp = null,
|
||||
ComponentRegistry? overrides = null)
|
||||
{
|
||||
uid = null;
|
||||
@@ -160,7 +160,7 @@ public partial class EntityManager
|
||||
xform ??= _xformQuery.GetComponent(target);
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return Spawn(protoName);
|
||||
|
||||
|
||||
var uid = Spawn(protoName, overrides);
|
||||
_xforms.PlaceNextToOrDrop(uid, target);
|
||||
return uid;
|
||||
@@ -180,7 +180,7 @@ public partial class EntityManager
|
||||
|| !containerComp.Containers.TryGetValue(containerId, out var container)
|
||||
|| !container.Insert(uid, this))
|
||||
{
|
||||
|
||||
|
||||
xform ??= _xformQuery.GetComponent(containerUid);
|
||||
if (xform.ParentUid.IsValid())
|
||||
_xforms.PlaceNextToOrDrop(uid, containerUid, targetXform: xform);
|
||||
|
||||
@@ -11,7 +11,6 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -38,7 +37,7 @@ namespace Robust.Shared.GameObjects
|
||||
// positions on spawn....
|
||||
private SharedTransformSystem _xforms = default!;
|
||||
|
||||
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||
protected EntityQuery<MetaDataComponent> MetaQuery;
|
||||
private EntityQuery<TransformComponent> _xformQuery;
|
||||
|
||||
#endregion Dependencies
|
||||
@@ -68,14 +67,16 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private EntityEventBus _eventBus = null!;
|
||||
|
||||
protected virtual int NextEntityUid { get; set; } = (int) EntityUid.FirstUid;
|
||||
protected int NextEntityUid = (int) EntityUid.FirstUid;
|
||||
|
||||
protected int NextNetworkId = (int) NetEntity.First;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEventBus EventBus => _eventBus;
|
||||
|
||||
public event Action<EntityUid>? EntityAdded;
|
||||
public event Action<EntityUid>? EntityInitialized;
|
||||
public event Action<EntityUid>? EntityDeleted;
|
||||
public event Action<EntityUid, MetaDataComponent>? EntityDeleted;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is queued for deletion. Not raised if an entity is deleted.
|
||||
@@ -123,7 +124,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public bool IsDefault(EntityUid uid)
|
||||
{
|
||||
if (!_metaQuery.TryGetComponent(uid, out var metadata) || metadata.EntityPrototype == null)
|
||||
if (!MetaQuery.TryGetComponent(uid, out var metadata) || metadata.EntityPrototype == null)
|
||||
return false;
|
||||
|
||||
var prototype = metadata.EntityPrototype;
|
||||
@@ -232,7 +233,7 @@ namespace Robust.Shared.GameObjects
|
||||
_eventBus.CalcOrdering();
|
||||
_mapSystem = System<SharedMapSystem>();
|
||||
_xforms = System<SharedTransformSystem>();
|
||||
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
MetaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||
}
|
||||
|
||||
@@ -304,19 +305,19 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, euid, overrides);
|
||||
return CreateEntity(prototypeName, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, default, overrides);
|
||||
return CreateEntity(prototypeName, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, default, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, overrides);
|
||||
_xforms.SetCoordinates(newEntity, _xformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
@@ -324,7 +325,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, default, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, overrides);
|
||||
var transform = _xformQuery.GetComponent(newEntity);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
@@ -415,14 +416,10 @@ namespace Robust.Shared.GameObjects
|
||||
if (!Started)
|
||||
return;
|
||||
|
||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var xformSys = EntitySysManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
// Networking blindly spams entities at this function, they can already be
|
||||
// deleted from being a child of a previously deleted entity
|
||||
// TODO: Why does networking need to send deletes for child entities?
|
||||
if (!metaQuery.TryGetComponent(e, out var meta) || meta.EntityDeleted)
|
||||
if (!MetaQuery.TryGetComponent(e, out var meta) || meta.EntityDeleted)
|
||||
return;
|
||||
|
||||
if (meta.EntityLifeStage == EntityLifeStage.Terminating)
|
||||
@@ -436,19 +433,17 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// Notify all entities they are being terminated prior to detaching & deleting
|
||||
RecursiveFlagEntityTermination(e, meta, metaQuery, xformQuery);
|
||||
RecursiveFlagEntityTermination(e, meta);
|
||||
|
||||
// Then actually delete them
|
||||
RecursiveDeleteEntity(e, meta, metaQuery, xformQuery, xformSys);
|
||||
RecursiveDeleteEntity(e, meta);
|
||||
}
|
||||
|
||||
private void RecursiveFlagEntityTermination(
|
||||
EntityUid uid,
|
||||
MetaDataComponent metadata,
|
||||
EntityQuery<MetaDataComponent> metaQuery,
|
||||
EntityQuery<TransformComponent> xformQuery)
|
||||
MetaDataComponent metadata)
|
||||
{
|
||||
var transform = xformQuery.GetComponent(uid);
|
||||
var transform = _xformQuery.GetComponent(uid);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Terminating;
|
||||
|
||||
try
|
||||
@@ -463,27 +458,24 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
foreach (var child in transform._children)
|
||||
{
|
||||
if (!metaQuery.TryGetComponent(child, out var childMeta) || childMeta.EntityDeleted)
|
||||
if (!MetaQuery.TryGetComponent(child, out var childMeta) || childMeta.EntityDeleted)
|
||||
{
|
||||
_sawmill.Error($"A deleted entity was still the transform child of another entity. Parent: {ToPrettyString(uid, metadata)}.");
|
||||
transform._children.Remove(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
RecursiveFlagEntityTermination(child, childMeta, metaQuery, xformQuery);
|
||||
RecursiveFlagEntityTermination(child, childMeta);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecursiveDeleteEntity(
|
||||
EntityUid uid,
|
||||
MetaDataComponent metadata,
|
||||
EntityQuery<MetaDataComponent> metaQuery,
|
||||
EntityQuery<TransformComponent> xformQuery,
|
||||
SharedTransformSystem xformSys)
|
||||
MetaDataComponent metadata)
|
||||
{
|
||||
// Note about this method: #if EXCEPTION_TOLERANCE is not used here because we're gonna it in the future...
|
||||
|
||||
var transform = xformQuery.GetComponent(uid);
|
||||
var netEntity = GetNetEntity(uid, metadata);
|
||||
var transform = _xformQuery.GetComponent(uid);
|
||||
|
||||
// Detach the base entity to null before iterating over children
|
||||
// This also ensures that the entity-lookup updates don't have to be re-run for every child (which recurses up the transform hierarchy).
|
||||
@@ -491,7 +483,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
xformSys.DetachParentToNull(uid, transform);
|
||||
_xforms.DetachParentToNull(uid, transform);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -503,7 +495,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
RecursiveDeleteEntity(child, metaQuery.GetComponent(child), metaQuery, xformQuery, xformSys);
|
||||
RecursiveDeleteEntity(child, MetaQuery.GetComponent(child));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@@ -536,7 +528,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
try
|
||||
{
|
||||
EntityDeleted?.Invoke(uid);
|
||||
EntityDeleted?.Invoke(uid, metadata);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -545,6 +537,8 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
_eventBus.OnEntityDeleted(uid);
|
||||
Entities.Remove(uid);
|
||||
// Need to get the ID above before MetadataComponent shutdown but only remove it after everything else is done.
|
||||
NetEntityLookup.Remove(netEntity);
|
||||
}
|
||||
|
||||
public virtual void QueueDeleteEntity(EntityUid uid)
|
||||
@@ -574,7 +568,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (uid == null)
|
||||
return false;
|
||||
|
||||
return _metaQuery.Resolve(uid.Value, ref metadata) && metadata.EntityPaused;
|
||||
return MetaQuery.Resolve(uid.Value, ref metadata) && metadata.EntityPaused;
|
||||
}
|
||||
|
||||
public bool Deleted(EntityUid uid)
|
||||
@@ -608,10 +602,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
private protected EntityUid AllocEntity(
|
||||
EntityPrototype? prototype,
|
||||
out MetaDataComponent metadata,
|
||||
EntityUid uid = default)
|
||||
out MetaDataComponent metadata)
|
||||
{
|
||||
var entity = AllocEntity(out metadata, uid);
|
||||
var entity = AllocEntity(out metadata);
|
||||
metadata._entityPrototype = prototype;
|
||||
Dirty(entity, metadata, metadata);
|
||||
return entity;
|
||||
@@ -620,25 +613,30 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Allocates an entity and stores it but does not load components or do initialization.
|
||||
/// </summary>
|
||||
private EntityUid AllocEntity(out MetaDataComponent metadata, EntityUid uid = default)
|
||||
private EntityUid AllocEntity(out MetaDataComponent metadata)
|
||||
{
|
||||
if (uid == default)
|
||||
{
|
||||
uid = GenerateEntityUid();
|
||||
}
|
||||
var uid = GenerateEntityUid();
|
||||
|
||||
#if DEBUG
|
||||
if (EntityExists(uid))
|
||||
{
|
||||
throw new InvalidOperationException($"UID already taken: {uid}");
|
||||
}
|
||||
#endif
|
||||
|
||||
// we want this called before adding components
|
||||
EntityAdded?.Invoke(uid);
|
||||
_eventBus.OnEntityAdded(uid);
|
||||
var netEntity = GenerateNetEntity();
|
||||
|
||||
metadata = new MetaDataComponent
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
metadata = new MetaDataComponent { Owner = uid };
|
||||
Owner = uid,
|
||||
#pragma warning restore CS0618
|
||||
};
|
||||
|
||||
SetNetEntity(uid, netEntity, metadata);
|
||||
|
||||
Entities.Add(uid);
|
||||
// add the required MetaDataComponent directly.
|
||||
@@ -653,23 +651,23 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out _, uid);
|
||||
return AllocEntity(out _);
|
||||
|
||||
if (!PrototypeManager.TryIndex<EntityPrototype>(prototypeName, out var prototype))
|
||||
throw new EntityCreationException($"Attempted to spawn an entity with an invalid prototype: {prototypeName}");
|
||||
|
||||
return CreateEntity(prototype, uid, context);
|
||||
return CreateEntity(prototype, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, EntityUid uid = default, IEntityLoadContext? context = null)
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out var metadata, uid);
|
||||
var entity = AllocEntity(prototype, out var metadata);
|
||||
try
|
||||
{
|
||||
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
@@ -686,7 +684,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context)
|
||||
{
|
||||
EntityPrototype.LoadEntity(_metaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
EntityPrototype.LoadEntity(MetaQuery.GetComponent(entity).EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
}
|
||||
|
||||
private protected void LoadEntity(EntityUid entity, IEntityLoadContext? context, EntityPrototype? prototype)
|
||||
@@ -698,7 +696,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
var meta = _metaQuery.GetComponent(entity);
|
||||
var meta = MetaQuery.GetComponent(entity);
|
||||
InitializeEntity(entity, meta);
|
||||
StartEntity(entity);
|
||||
|
||||
@@ -747,6 +745,12 @@ namespace Robust.Shared.GameObjects
|
||||
return ToPrettyString(uid, metadata);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
{
|
||||
return ToPrettyString(GetEntity(netEntity));
|
||||
}
|
||||
|
||||
private EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent metadata)
|
||||
{
|
||||
return new EntityStringRepresentation(uid, metadata.EntityDeleted, metadata.EntityName, metadata.EntityPrototype?.ID);
|
||||
@@ -764,12 +768,16 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Factory for generating a new EntityUid for an entity currently being created.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
protected virtual EntityUid GenerateEntityUid()
|
||||
internal EntityUid GenerateEntityUid()
|
||||
{
|
||||
return new(NextEntityUid++);
|
||||
return new EntityUid(NextEntityUid++);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a unique network id and increments <see cref="NextNetworkId"/>
|
||||
/// </summary>
|
||||
protected virtual NetEntity GenerateNetEntity() => new(NextNetworkId++);
|
||||
|
||||
private sealed class EntityDiffContext : ISerializationContext
|
||||
{
|
||||
public SerializationManager.SerializerProvider SerializerProvider { get; }
|
||||
|
||||
@@ -9,7 +9,10 @@ namespace Robust.Shared.GameObjects
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class EntityState
|
||||
{
|
||||
public EntityUid Uid { get; }
|
||||
/// <summary>
|
||||
/// Network identifier for the entity.
|
||||
/// </summary>
|
||||
public NetEntity NetEntity;
|
||||
|
||||
public NetListAsArray<ComponentChange> ComponentChanges { get; }
|
||||
|
||||
@@ -23,9 +26,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public HashSet<ushort>? NetComponents;
|
||||
|
||||
public EntityState(EntityUid uid, NetListAsArray<ComponentChange> changedComponents, GameTick lastModified, HashSet<ushort>? netComps = null)
|
||||
public EntityState(NetEntity netEntity, NetListAsArray<ComponentChange> changedComponents, GameTick lastModified, HashSet<ushort>? netComps = null)
|
||||
{
|
||||
Uid = uid;
|
||||
NetEntity = netEntity;
|
||||
ComponentChanges = changedComponents;
|
||||
EntityLastModified = lastModified;
|
||||
NetComponents = netComps;
|
||||
|
||||
@@ -403,6 +403,13 @@ public partial class EntitySystem
|
||||
return EntityManager.ToPrettyString(uid);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
{
|
||||
return EntityManager.ToPrettyString(netEntity);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Component Get
|
||||
@@ -886,4 +893,344 @@ public partial class EntitySystem
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region NetEntities
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected bool IsClientSide(EntityUid entity)
|
||||
{
|
||||
return EntityManager.IsClientSide(entity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetEntity(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity)
|
||||
{
|
||||
return EntityManager.TryGetEntity(nEntity, out entity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity)
|
||||
{
|
||||
return EntityManager.TryGetEntity(nEntity, out entity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetNetEntity(EntityUid uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.TryGetNetEntity(uid, out netEntity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetNetEntity(EntityUid? uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.TryGetNetEntity(uid, out netEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> of an entity. Returns <see cref="NetEntity.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected NetEntity GetNetEntity(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.GetNetEntity(uid, metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> of an entity. Logs an error if the entity does not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected NetEntity? GetNetEntity(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.GetNetEntity(uid, metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> of an entity or creates a new entity if none exists.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid EnsureEntity<T>(NetEntity netEntity, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureEntity<T>(netEntity, callerEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> of an entity or creates a new one if not null.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid? EnsureEntity<T>(NetEntity? netEntity, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureEntity<T>(netEntity, callerEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetCoordinates"/> of an entity or creates a new entity if none exists.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureCoordinates<T>(netCoordinates, callerEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetCoordinates"/> of an entity or creates a new one if not null.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityCoordinates? EnsureCoordinates<T>(NetCoordinates? netCoordinates, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureCoordinates<T>(netCoordinates, callerEntity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HashSet<EntityUid> EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureEntitySet<T>(netEntities, callerEntity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity)
|
||||
{
|
||||
return EntityManager.EnsureEntityList<T>(netEntities, callerEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid GetEntity(NetEntity netEntity)
|
||||
{
|
||||
return EntityManager.GetEntity(netEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid? GetEntity(NetEntity? netEntity)
|
||||
{
|
||||
return EntityManager.GetEntity(netEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected HashSet<NetEntity> GetNetEntitySet(HashSet<EntityUid> uids)
|
||||
{
|
||||
return EntityManager.GetNetEntitySet(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntitySet(netEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<NetEntity> GetNetEntityList(ICollection<EntityUid> uids)
|
||||
{
|
||||
return EntityManager.GetNetEntityList(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<NetEntity> GetNetEntityList(IReadOnlyList<EntityUid> uids)
|
||||
{
|
||||
return EntityManager.GetNetEntityList(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityList(netEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<NetEntity> GetNetEntityList(List<EntityUid> uids)
|
||||
{
|
||||
return EntityManager.GetNetEntityList(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<EntityUid> GetEntityList(List<NetEntity> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityList(netEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<NetEntity?> GetNetEntityList(List<EntityUid?> uids)
|
||||
{
|
||||
return EntityManager.GetNetEntityList(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected List<EntityUid?> GetEntityList(List<NetEntity?> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityList(netEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected NetEntity[] GetNetEntityArray(EntityUid[] uids)
|
||||
{
|
||||
return EntityManager.GetNetEntityArray(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid[] GetEntityArray(NetEntity[] netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityArray(netEntities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> versions of the supplied entities. Logs an error if the entities do not exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected NetEntity?[] GetNetEntityArray(EntityUid?[] uids)
|
||||
{
|
||||
return EntityManager.GetNetEntityArray(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> versions of the supplied <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityUid?[] GetEntityArray(NetEntity?[] netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityArray(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected NetCoordinates GetNetCoordinates(EntityCoordinates coordinates, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.GetNetCoordinates(coordinates, metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="NetEntity"/> of an entity. Returns <see cref="NetEntity.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected NetCoordinates? GetNetCoordinates(EntityCoordinates? coordinates, MetaDataComponent? metadata = null)
|
||||
{
|
||||
return EntityManager.GetNetCoordinates(coordinates, metadata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityCoordinates GetCoordinates(NetCoordinates netEntity)
|
||||
{
|
||||
return EntityManager.GetCoordinates(netEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="EntityUid"/> of a <see cref="NetEntity"/>. Returns <see cref="EntityUid.Invalid"/> if it doesn't exist.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityCoordinates? GetCoordinates(NetCoordinates? netEntity)
|
||||
{
|
||||
return EntityManager.GetCoordinates(netEntity);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HashSet<EntityCoordinates> GetEntitySet(HashSet<NetCoordinates> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntitySet(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<EntityCoordinates> GetEntityList(List<NetCoordinates> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityList(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<EntityCoordinates> GetEntityList(ICollection<NetCoordinates> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityList(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<EntityCoordinates?> GetEntityList(List<NetCoordinates?> netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityList(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityCoordinates[] GetEntityArray(NetCoordinates[] netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityArray(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EntityCoordinates?[] GetEntityArray(NetCoordinates?[] netEntities)
|
||||
{
|
||||
return EntityManager.GetEntityArray(netEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HashSet<NetCoordinates> GetNetCoordinatesSet(HashSet<EntityCoordinates> entities)
|
||||
{
|
||||
return EntityManager.GetNetCoordinatesSet(entities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<NetCoordinates> GetNetCoordinatesList(List<EntityCoordinates> entities)
|
||||
{
|
||||
return EntityManager.GetNetCoordinatesList(entities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<NetCoordinates> GetNetCoordinatesList(ICollection<EntityCoordinates> entities)
|
||||
{
|
||||
return EntityManager.GetNetCoordinatesList(entities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public List<NetCoordinates?> GetNetCoordinatesList(List<EntityCoordinates?> entities)
|
||||
{
|
||||
return EntityManager.GetNetCoordinatesList(entities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public NetCoordinates[] GetNetCoordinatesArray(EntityCoordinates[] entities)
|
||||
{
|
||||
return EntityManager.GetNetCoordinatesArray(entities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public NetCoordinates?[] GetNetCoordinatesArray(EntityCoordinates?[] entities)
|
||||
{
|
||||
return EntityManager.GetNetCoordinatesArray(entities);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ public sealed class PlayAudioGlobalMessage : AudioMessage
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlayAudioPositionalMessage : AudioMessage
|
||||
{
|
||||
public EntityCoordinates Coordinates { get; set; }
|
||||
public EntityCoordinates FallbackCoordinates { get; set; }
|
||||
public NetCoordinates Coordinates { get; set; }
|
||||
public NetCoordinates FallbackCoordinates { get; set; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PlayAudioEntityMessage : AudioMessage
|
||||
{
|
||||
public EntityUid EntityUid { get; set; }
|
||||
public EntityCoordinates Coordinates { get; set; }
|
||||
public EntityCoordinates FallbackCoordinates { get; set; }
|
||||
public NetEntity NetEntity { get; set; }
|
||||
public NetCoordinates Coordinates { get; set; }
|
||||
public NetCoordinates FallbackCoordinates { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -14,15 +13,10 @@ namespace Robust.Shared.GameObjects
|
||||
/// This type contains a network identification number of an entity.
|
||||
/// This can be used by the EntityManager to access an entity
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable, CopyByRef]
|
||||
[CopyByRef]
|
||||
public readonly struct EntityUid : IEquatable<EntityUid>, IComparable<EntityUid>, ISpanFormattable
|
||||
{
|
||||
/// <summary>
|
||||
/// If this bit is set on a UID, it's client sided.
|
||||
/// Use <see cref="IsClientSide" /> to check this.
|
||||
/// </summary>
|
||||
internal const int ClientUid = 2 << 29;
|
||||
readonly int _uid;
|
||||
public readonly int Id;
|
||||
|
||||
/// <summary>
|
||||
/// An Invalid entity UID you can compare against.
|
||||
@@ -37,9 +31,9 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Creates an instance of this structure, with the given network ID.
|
||||
/// </summary>
|
||||
public EntityUid(int uid)
|
||||
public EntityUid(int id)
|
||||
{
|
||||
_uid = uid;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public bool Valid => IsValid();
|
||||
@@ -49,14 +43,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public static EntityUid Parse(ReadOnlySpan<char> uid)
|
||||
{
|
||||
if (uid.StartsWith("c"))
|
||||
{
|
||||
return new EntityUid(int.Parse(uid[1..]) | ClientUid);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new EntityUid(int.Parse(uid));
|
||||
}
|
||||
return new EntityUid(int.Parse(uid));
|
||||
}
|
||||
|
||||
public static bool TryParse(ReadOnlySpan<char> uid, out EntityUid entityUid)
|
||||
@@ -80,19 +67,13 @@ namespace Robust.Shared.GameObjects
|
||||
[Pure]
|
||||
public bool IsValid()
|
||||
{
|
||||
return _uid > 0;
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public bool IsClientSide()
|
||||
{
|
||||
return (_uid & (2 << 29)) != 0;
|
||||
return Id > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(EntityUid other)
|
||||
{
|
||||
return _uid == other._uid;
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -105,7 +86,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _uid;
|
||||
return Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +94,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public static bool operator ==(EntityUid a, EntityUid b)
|
||||
{
|
||||
return a._uid == b._uid;
|
||||
return a.Id == b.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -130,17 +111,13 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public static explicit operator int(EntityUid self)
|
||||
{
|
||||
return self._uid;
|
||||
return self.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsClientSide())
|
||||
{
|
||||
return $"c{_uid & ~ClientUid}";
|
||||
}
|
||||
return _uid.ToString();
|
||||
return Id.ToString();
|
||||
}
|
||||
|
||||
public string ToString(string? format, IFormatProvider? formatProvider)
|
||||
@@ -154,21 +131,13 @@ namespace Robust.Shared.GameObjects
|
||||
ReadOnlySpan<char> format,
|
||||
IFormatProvider? provider)
|
||||
{
|
||||
if (IsClientSide())
|
||||
{
|
||||
return FormatHelpers.TryFormatInto(
|
||||
destination,
|
||||
out charsWritten,
|
||||
$"c{_uid & ~ClientUid}");
|
||||
}
|
||||
|
||||
return _uid.TryFormat(destination, out charsWritten);
|
||||
return Id.TryFormat(destination, out charsWritten);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(EntityUid other)
|
||||
{
|
||||
return _uid.CompareTo(other._uid);
|
||||
return Id.CompareTo(other.Id);
|
||||
}
|
||||
|
||||
#region ViewVariables
|
||||
|
||||
194
Robust.Shared/GameObjects/IEntityManager.Network.cs
Normal file
194
Robust.Shared/GameObjects/IEntityManager.Network.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial interface IEntityManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to parse a string as a NetEntity and return the relevant EntityUid.
|
||||
/// </summary>
|
||||
public bool TryParseNetEntity(string arg, [NotNullWhen(true)] out EntityUid? entity);
|
||||
|
||||
/// <summary>
|
||||
/// TryGet version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
public bool TryGetEntity(NetEntity nEntity, [NotNullWhen(true)] out EntityUid? entity);
|
||||
|
||||
/// <summary>
|
||||
/// TryGet version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
public bool TryGetEntity(NetEntity? nEntity, [NotNullWhen(true)] out EntityUid? entity);
|
||||
|
||||
/// <summary>
|
||||
/// TryGet version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
public bool TryGetNetEntity(EntityUid uid, [NotNullWhen(true)] out NetEntity? netEntity, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// TryGet version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
public bool TryGetNetEntity(EntityUid? uid, [NotNullWhen(true)] out NetEntity? netEntity,
|
||||
MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the entity only exists on the client.
|
||||
/// </summary>
|
||||
public bool IsClientSide(EntityUid uid, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a corresponding <see cref="EntityUid"/> if it exists, otherwise creates an entity for it.
|
||||
/// </summary>
|
||||
/// <param name="nEntity">The net entity we're trying to resolve.</param>
|
||||
/// <param name="T">The type of the component that may need its state handling run later.</param>
|
||||
/// <param name="callerEntity">The entity trying to resolve the net entity. This may be flagged for later component state handling.</param>
|
||||
public EntityUid EnsureEntity<T>(NetEntity nEntity, EntityUid callerEntity);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a corresponding <see cref="EntityUid"/> if it exists and nEntity is not null.
|
||||
/// </summary>
|
||||
public EntityUid? EnsureEntity<T>(NetEntity? nEntity, EntityUid callerEntity);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding local <see cref="EntityUid"/>.
|
||||
/// </summary>
|
||||
public EntityUid GetEntity(NetEntity nEntity);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding local <see cref="EntityUid"/>.
|
||||
/// </summary>
|
||||
public EntityUid? GetEntity(NetEntity? nEntity);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="NetEntity"/> for the local entity.
|
||||
/// </summary>
|
||||
public NetEntity GetNetEntity(EntityUid uid, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="NetEntity"/> for the local entity.
|
||||
/// </summary>
|
||||
public NetEntity? GetNetEntity(EntityUid? uid, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// HashSet version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
public HashSet<EntityUid> GetEntitySet(HashSet<NetEntity> netEntities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
public List<EntityUid> GetEntityList(List<NetEntity> netEntities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
public List<EntityUid> GetEntityList(ICollection<NetEntity> netEntities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
public List<EntityUid?> GetEntityList(List<NetEntity?> netEntities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
EntityUid[] GetEntityArray(NetEntity[] netEntities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetEntity"/>
|
||||
/// </summary>
|
||||
EntityUid?[] GetEntityArray(NetEntity?[] netEntities);
|
||||
|
||||
/// <summary>
|
||||
/// HashSet version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
public HashSet<NetEntity> GetNetEntitySet(HashSet<EntityUid> entities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
public List<NetEntity> GetNetEntityList(List<EntityUid> entities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
public List<NetEntity> GetNetEntityList(ICollection<EntityUid> entities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
public List<NetEntity?> GetNetEntityList(List<EntityUid?> entities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
NetEntity[] GetNetEntityArray(EntityUid[] entities);
|
||||
|
||||
/// <summary>
|
||||
/// List version of <see cref="GetNetEntity"/>
|
||||
/// </summary>
|
||||
NetEntity?[] GetNetEntityArray(EntityUid?[] entities);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="NetCoordinates"/> for the specified local coordinates.
|
||||
/// </summary>
|
||||
public NetCoordinates GetNetCoordinates(EntityCoordinates coordinates, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="NetCoordinates"/> for the specified local coordinates.
|
||||
/// </summary>
|
||||
public NetCoordinates? GetNetCoordinates(EntityCoordinates? coordinates, MetaDataComponent? metadata = null);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="EntityCoordinates"/> for the specified network coordinates.
|
||||
/// </summary>
|
||||
public EntityCoordinates GetCoordinates(NetCoordinates coordinates);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corresponding <see cref="EntityCoordinates"/> for the specified network coordinates.
|
||||
/// </summary>
|
||||
public EntityCoordinates? GetCoordinates(NetCoordinates? coordinates);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a corresponding <see cref="EntityCoordinates"/> if it exists, otherwise creates an entity for it.
|
||||
/// </summary>
|
||||
/// <param name="netCoordinates">The net coordinates we're trying to resolve.</param>
|
||||
/// <param name="T">The type of the component that may need its state handling run later.</param>
|
||||
/// <param name="callerEntity">The entity trying to resolve the net entity. This may be flagged for later component state handling.</param>
|
||||
public EntityCoordinates EnsureCoordinates<T>(NetCoordinates netCoordinates, EntityUid callerEntity);
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get a corresponding <see cref="EntityCoordinates"/> if it exists and nEntity is not null.
|
||||
/// </summary>
|
||||
public EntityCoordinates? EnsureCoordinates<T>(NetCoordinates? netCoordinates, EntityUid callerEntity);
|
||||
|
||||
public HashSet<EntityCoordinates> GetEntitySet(HashSet<NetCoordinates> netEntities);
|
||||
|
||||
public List<EntityCoordinates> GetEntityList(List<NetCoordinates> netEntities);
|
||||
|
||||
public HashSet<EntityUid> EnsureEntitySet<T>(HashSet<NetEntity> netEntities, EntityUid callerEntity);
|
||||
|
||||
public List<EntityUid> EnsureEntityList<T>(List<NetEntity> netEntities, EntityUid callerEntity);
|
||||
|
||||
public List<EntityCoordinates> GetEntityList(ICollection<NetCoordinates> netEntities);
|
||||
|
||||
public List<EntityCoordinates?> GetEntityList(List<NetCoordinates?> netEntities);
|
||||
|
||||
public EntityCoordinates[] GetEntityArray(NetCoordinates[] netEntities);
|
||||
|
||||
public EntityCoordinates?[] GetEntityArray(NetCoordinates?[] netEntities);
|
||||
|
||||
public HashSet<NetCoordinates> GetNetCoordinatesSet(HashSet<EntityCoordinates> entities);
|
||||
|
||||
public List<NetCoordinates> GetNetCoordinatesList(List<EntityCoordinates> entities);
|
||||
|
||||
public List<NetCoordinates> GetNetCoordinatesList(ICollection<EntityCoordinates> entities);
|
||||
|
||||
public List<NetCoordinates?> GetNetCoordinatesList(List<EntityCoordinates?> entities);
|
||||
|
||||
public NetCoordinates[] GetNetCoordinatesArray(EntityCoordinates[] entities);
|
||||
|
||||
public NetCoordinates?[] GetNetCoordinatesArray(EntityCoordinates?[] entities);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
event Action<EntityUid>? EntityAdded;
|
||||
event Action<EntityUid>? EntityInitialized;
|
||||
event Action<EntityUid>? EntityDeleted;
|
||||
event Action<EntityUid, MetaDataComponent>? EntityDeleted;
|
||||
event Action<EntityUid>? EntityDirtied; // only raised after initialization
|
||||
|
||||
EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null);
|
||||
|
||||
226
Robust.Shared/GameObjects/NetEntity.cs
Normal file
226
Robust.Shared/GameObjects/NetEntity.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
/// <summary>
|
||||
/// Network identifier for entities; used by client and server to refer to the same entity where their local <see cref="EntityUid"/> may differ.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct NetEntity : IEquatable<NetEntity>, IComparable<NetEntity>, ISpanFormattable
|
||||
{
|
||||
public readonly int Id;
|
||||
|
||||
public const int ClientEntity = 2 << 29;
|
||||
|
||||
/*
|
||||
* Differed to EntityUid to be more consistent with Arch.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// An Invalid entity UID you can compare against.
|
||||
/// </summary>
|
||||
public static readonly NetEntity Invalid = new(0);
|
||||
|
||||
/// <summary>
|
||||
/// The first entity UID the entityManager should use when the manager is initialized.
|
||||
/// </summary>
|
||||
public static readonly NetEntity First = new(1);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this structure, with the given network ID.
|
||||
/// </summary>
|
||||
public NetEntity(int id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public bool Valid => IsValid();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a network entity UID by parsing a string number.
|
||||
/// </summary>
|
||||
public static NetEntity Parse(ReadOnlySpan<char> uid)
|
||||
{
|
||||
return new NetEntity(int.Parse(uid));
|
||||
}
|
||||
|
||||
public static bool TryParse(ReadOnlySpan<char> uid, out NetEntity entity)
|
||||
{
|
||||
try
|
||||
{
|
||||
entity = Parse(uid);
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
entity = Invalid;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the ID value is valid. Does not check if it identifies
|
||||
/// a valid Entity.
|
||||
/// </summary>
|
||||
[Pure]
|
||||
public bool IsValid()
|
||||
{
|
||||
return Id > 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(NetEntity other)
|
||||
{
|
||||
return Id == other.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
return obj is EntityUid id && Equals(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for equality by value between two objects.
|
||||
/// </summary>
|
||||
public static bool operator ==(NetEntity a, NetEntity b)
|
||||
{
|
||||
return a.Id == b.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for inequality by value between two objects.
|
||||
/// </summary>
|
||||
public static bool operator !=(NetEntity a, NetEntity b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicit conversion of EntityId to int. This should only be used in special
|
||||
/// cases like serialization. Do NOT use this in content.
|
||||
/// </summary>
|
||||
public static explicit operator int(NetEntity self)
|
||||
{
|
||||
return self.Id;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString();
|
||||
}
|
||||
|
||||
public string ToString(string? format, IFormatProvider? formatProvider)
|
||||
{
|
||||
return ToString();
|
||||
}
|
||||
|
||||
public bool TryFormat(
|
||||
Span<char> destination,
|
||||
out int charsWritten,
|
||||
ReadOnlySpan<char> format,
|
||||
IFormatProvider? provider)
|
||||
{
|
||||
return Id.TryFormat(destination, out charsWritten);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int CompareTo(NetEntity other)
|
||||
{
|
||||
return Id.CompareTo(other.Id);
|
||||
}
|
||||
|
||||
public bool IsClientSide() => (Id & ClientEntity) == ClientEntity;
|
||||
|
||||
#region ViewVariables
|
||||
|
||||
|
||||
[ViewVariables]
|
||||
private string Representation
|
||||
{
|
||||
get
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
return entManager.ToPrettyString(entManager.GetEntity(this));
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private string Name
|
||||
{
|
||||
get => MetaData?.EntityName ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (MetaData is {} metaData)
|
||||
metaData.EntityName = value;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private string Description
|
||||
{
|
||||
get => MetaData?.EntityDescription ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (MetaData is {} metaData)
|
||||
metaData.EntityDescription = value;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private EntityPrototype? Prototype => MetaData?.EntityPrototype;
|
||||
|
||||
[ViewVariables]
|
||||
private GameTick LastModifiedTick => MetaData?.EntityLastModifiedTick ?? GameTick.Zero;
|
||||
|
||||
[ViewVariables]
|
||||
private bool Paused => MetaData?.EntityPaused ?? false;
|
||||
|
||||
[ViewVariables]
|
||||
private EntityLifeStage LifeStage => MetaData?.EntityLifeStage ?? EntityLifeStage.Deleted;
|
||||
|
||||
[ViewVariables]
|
||||
private MetaDataComponent? MetaData
|
||||
{
|
||||
get
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
return entManager.GetComponentOrNull<MetaDataComponent>(entManager.GetEntity(this));
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private TransformComponent? Transform
|
||||
{
|
||||
get
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
return entManager.GetComponentOrNull<TransformComponent>(entManager.GetEntity(this));
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
private EntityUid Uid
|
||||
{
|
||||
get
|
||||
{
|
||||
return IoCManager.Resolve<IEntityManager>().GetEntity(this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -230,7 +230,7 @@ namespace Robust.Shared.GameObjects
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ChunkSplitDebugMessage : EntityEventArgs
|
||||
{
|
||||
public EntityUid Grid;
|
||||
public NetEntity Grid;
|
||||
public Dictionary<Vector2i, List<List<Vector2i>>> Nodes = new ();
|
||||
public List<(Vector2 Start, Vector2 End)> Connections = new();
|
||||
}
|
||||
|
||||
@@ -480,7 +480,7 @@ public abstract partial class SharedTransformSystem
|
||||
if (value.EntityId == uid)
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
if (_netMan.IsServer || uid.IsClientSide())
|
||||
if (_netMan.IsServer || IsClientSide(uid))
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to parent an entity to itself: {ToPrettyString(uid)}");
|
||||
}
|
||||
@@ -490,7 +490,7 @@ public abstract partial class SharedTransformSystem
|
||||
if (!_xformQuery.Resolve(value.EntityId, ref newParent, false))
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
if (_netMan.IsServer || uid.IsClientSide())
|
||||
if (_netMan.IsServer || IsClientSide(uid))
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to parent entity {ToPrettyString(uid)} to non-existent entity {value.EntityId}");
|
||||
}
|
||||
@@ -498,7 +498,7 @@ public abstract partial class SharedTransformSystem
|
||||
if (newParent.LifeStage > ComponentLifeStage.Running || LifeStage(value.EntityId) > EntityLifeStage.MapInitialized)
|
||||
{
|
||||
DetachParentToNull(uid, xform);
|
||||
if (_netMan.IsServer || uid.IsClientSide())
|
||||
if (_netMan.IsServer || IsClientSide(uid))
|
||||
QueueDel(uid);
|
||||
throw new InvalidOperationException($"Attempted to re-parent to a terminating object. Entity: {ToPrettyString(uid)}, new parent: {ToPrettyString(value.EntityId)}");
|
||||
}
|
||||
@@ -625,7 +625,7 @@ public abstract partial class SharedTransformSystem
|
||||
|
||||
public TransformComponent? GetParent(TransformComponent xform)
|
||||
{
|
||||
if (!xform.ParentUid.IsValid())
|
||||
if (!xform.ParentUid.IsValid())
|
||||
return null;
|
||||
return _xformQuery.GetComponent(xform.ParentUid);
|
||||
}
|
||||
@@ -678,10 +678,12 @@ public abstract partial class SharedTransformSystem
|
||||
internal void OnGetState(EntityUid uid, TransformComponent component, ref ComponentGetState args)
|
||||
{
|
||||
DebugTools.Assert(!component.ParentUid.IsValid() || (!Deleted(component.ParentUid) && !EntityManager.IsQueuedForDeletion(component.ParentUid)));
|
||||
var parent = GetNetEntity(component.ParentUid);
|
||||
|
||||
args.State = new TransformComponentState(
|
||||
component.LocalPosition,
|
||||
component.LocalRotation,
|
||||
component.ParentUid,
|
||||
parent,
|
||||
component.NoLocalRotation,
|
||||
component.Anchored);
|
||||
}
|
||||
@@ -690,13 +692,16 @@ public abstract partial class SharedTransformSystem
|
||||
{
|
||||
if (args.Current is TransformComponentState newState)
|
||||
{
|
||||
var newParentId = newState.ParentID;
|
||||
var parent = GetEntity(newState.ParentID);
|
||||
if (!parent.IsValid() && newState.ParentID.IsValid())
|
||||
Log.Error($"Received transform component state with an unknown parent Id. Entity: {ToPrettyString(uid)}. Net parent: {newState.ParentID}");
|
||||
|
||||
var oldAnchored = xform.Anchored;
|
||||
|
||||
// update actual position data, if required
|
||||
if (!xform.LocalPosition.EqualsApprox(newState.LocalPosition)
|
||||
|| !xform.LocalRotation.EqualsApprox(newState.Rotation)
|
||||
|| xform.ParentUid != newParentId)
|
||||
|| xform.ParentUid != parent)
|
||||
{
|
||||
// remove from any old grid lookups
|
||||
if (xform.Anchored && TryComp(xform.ParentUid, out MapGridComponent? grid))
|
||||
@@ -709,7 +714,7 @@ public abstract partial class SharedTransformSystem
|
||||
xform._anchored |= newState.Anchored;
|
||||
|
||||
// Update the action position, rotation, and parent (and hence also map, grid, etc).
|
||||
SetCoordinates(uid, xform, new EntityCoordinates(newParentId, newState.LocalPosition), newState.Rotation, unanchor: false);
|
||||
SetCoordinates(uid, xform, new EntityCoordinates(parent, newState.LocalPosition), newState.Rotation, unanchor: false);
|
||||
|
||||
xform._anchored = newState.Anchored;
|
||||
|
||||
@@ -744,7 +749,7 @@ public abstract partial class SharedTransformSystem
|
||||
xform.PrevRotation = newState.Rotation;
|
||||
xform._noLocalRotation = newState.NoLocalRotation;
|
||||
|
||||
DebugTools.Assert(xform.ParentUid == newState.ParentID, "Transform state failed to set parent");
|
||||
DebugTools.Assert(xform.ParentUid == parent, "Transform state failed to set parent");
|
||||
DebugTools.Assert(xform.Anchored == newState.Anchored, "Transform state failed to set anchored");
|
||||
}
|
||||
|
||||
@@ -752,7 +757,7 @@ public abstract partial class SharedTransformSystem
|
||||
{
|
||||
xform.NextPosition = nextTransform.LocalPosition;
|
||||
xform.NextRotation = nextTransform.Rotation;
|
||||
xform.LerpParent = nextTransform.ParentID;
|
||||
xform.LerpParent = GetEntity(nextTransform.ParentID);
|
||||
ActivateLerp(xform);
|
||||
}
|
||||
else
|
||||
@@ -1375,12 +1380,12 @@ public abstract partial class SharedTransformSystem
|
||||
/// Attempts to place one entity next to another entity. If the target entity is in a container, this will attempt
|
||||
/// to insert that entity into the same container.
|
||||
/// </summary>
|
||||
public void PlaceNextToOrDrop(EntityUid uid, EntityUid target,
|
||||
public void PlaceNextToOrDrop(EntityUid uid, EntityUid target,
|
||||
TransformComponent? xform = null, TransformComponent? targetXform = null)
|
||||
{
|
||||
if (!_xformQuery.Resolve(target, ref targetXform))
|
||||
return;
|
||||
|
||||
|
||||
if (!_xformQuery.Resolve(uid, ref xform))
|
||||
return;
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Current parent entity of this entity.
|
||||
/// </summary>
|
||||
public readonly EntityUid ParentID;
|
||||
public readonly NetEntity ParentID;
|
||||
|
||||
/// <summary>
|
||||
/// Current position offset of the entity.
|
||||
@@ -310,7 +310,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="rotation">Current direction offset of this entity.</param>
|
||||
/// <param name="parentId">Current parent transform of this entity.</param>
|
||||
/// <param name="noLocalRotation"></param>
|
||||
public TransformComponentState(Vector2 localPosition, Angle rotation, EntityUid parentId, bool noLocalRotation, bool anchored)
|
||||
public TransformComponentState(Vector2 localPosition, Angle rotation, NetEntity parentId, bool noLocalRotation, bool anchored)
|
||||
{
|
||||
LocalPosition = localPosition;
|
||||
Rotation = rotation;
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Robust.Shared.GameStates
|
||||
uint lastInput,
|
||||
NetListAsArray<EntityState> entities,
|
||||
NetListAsArray<PlayerState> players,
|
||||
NetListAsArray<EntityUid> deletions)
|
||||
NetListAsArray<NetEntity> deletions)
|
||||
{
|
||||
FromSequence = fromSequence;
|
||||
ToSequence = toSequence;
|
||||
@@ -43,6 +43,6 @@ namespace Robust.Shared.GameStates
|
||||
|
||||
public readonly NetListAsArray<EntityState> EntityStates;
|
||||
public readonly NetListAsArray<PlayerState> PlayerStates;
|
||||
public readonly NetListAsArray<EntityUid> EntityDeletions;
|
||||
public readonly NetListAsArray<NetEntity> EntityDeletions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Robust.Shared.GameStates
|
||||
public SessionStatus Status { get; set; }
|
||||
public short Ping { get; set; }
|
||||
|
||||
public EntityUid? ControlledEntity { get; set; }
|
||||
public NetEntity? ControlledEntity { get; set; }
|
||||
|
||||
public PlayerState Clone()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
@@ -18,7 +19,7 @@ namespace Robust.Shared.Input.Binding
|
||||
{
|
||||
}
|
||||
|
||||
public abstract bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message);
|
||||
public abstract bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message);
|
||||
|
||||
/// <summary>
|
||||
/// Makes a quick input command from enabled and disabled delegates.
|
||||
@@ -56,12 +57,9 @@ namespace Robust.Shared.Input.Binding
|
||||
DisabledDelegate?.Invoke(session);
|
||||
}
|
||||
|
||||
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
|
||||
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
|
||||
{
|
||||
if (!(message is FullInputCmdMessage msg))
|
||||
return false;
|
||||
|
||||
switch (msg.State)
|
||||
switch (message.State)
|
||||
{
|
||||
case BoundKeyState.Up:
|
||||
Disabled(session);
|
||||
@@ -111,14 +109,28 @@ namespace Robust.Shared.Input.Binding
|
||||
FireOutsidePrediction = outsidePrediction;
|
||||
}
|
||||
|
||||
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
|
||||
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
|
||||
{
|
||||
if (!(message is FullInputCmdMessage msg) || (_ignoreUp && msg.State != BoundKeyState.Down))
|
||||
if ((_ignoreUp && message.State != BoundKeyState.Down))
|
||||
return false;
|
||||
|
||||
var handled = _callback?.Invoke(new PointerInputCmdArgs(session, msg.Coordinates,
|
||||
msg.ScreenCoordinates, msg.Uid, msg.State, msg));
|
||||
return handled.HasValue && handled.Value;
|
||||
switch (message)
|
||||
{
|
||||
case ClientFullInputCmdMessage clientInput:
|
||||
{
|
||||
var handled = _callback?.Invoke(new PointerInputCmdArgs(session, clientInput.Coordinates,
|
||||
clientInput.ScreenCoordinates, clientInput.Uid, message.State, message));
|
||||
return handled.HasValue && handled.Value;
|
||||
}
|
||||
case FullInputCmdMessage fullInput:
|
||||
{
|
||||
var handled = _callback?.Invoke(new PointerInputCmdArgs(session, entManager.GetCoordinates(fullInput.Coordinates),
|
||||
fullInput.ScreenCoordinates, entManager.GetEntity(fullInput.Uid), fullInput.State, message));
|
||||
return handled.HasValue && handled.Value;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly struct PointerInputCmdArgs
|
||||
@@ -128,11 +140,11 @@ namespace Robust.Shared.Input.Binding
|
||||
public readonly ScreenCoordinates ScreenCoordinates;
|
||||
public readonly EntityUid EntityUid;
|
||||
public readonly BoundKeyState State;
|
||||
public readonly FullInputCmdMessage OriginalMessage;
|
||||
public readonly IFullInputCmdMessage OriginalMessage;
|
||||
|
||||
public PointerInputCmdArgs(ICommonSession? session, EntityCoordinates coordinates,
|
||||
ScreenCoordinates screenCoordinates, EntityUid entityUid, BoundKeyState state,
|
||||
FullInputCmdMessage originalMessage)
|
||||
IFullInputCmdMessage originalMessage)
|
||||
{
|
||||
Session = session;
|
||||
Coordinates = coordinates;
|
||||
@@ -158,17 +170,28 @@ namespace Robust.Shared.Input.Binding
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
|
||||
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
|
||||
{
|
||||
if (!(message is FullInputCmdMessage msg))
|
||||
return false;
|
||||
|
||||
switch (msg.State)
|
||||
switch (message)
|
||||
{
|
||||
case BoundKeyState.Up:
|
||||
return _disabled?.Invoke(session, msg.Coordinates, msg.Uid) == true;
|
||||
case BoundKeyState.Down:
|
||||
return _enabled?.Invoke(session, msg.Coordinates, msg.Uid) == true;
|
||||
case ClientFullInputCmdMessage clientInput:
|
||||
switch (clientInput.State)
|
||||
{
|
||||
case BoundKeyState.Up:
|
||||
return _disabled?.Invoke(session, clientInput.Coordinates, clientInput.Uid) == true;
|
||||
case BoundKeyState.Down:
|
||||
return _enabled?.Invoke(session, clientInput.Coordinates, clientInput.Uid) == true;
|
||||
}
|
||||
break;
|
||||
case FullInputCmdMessage fullInput:
|
||||
switch (fullInput.State)
|
||||
{
|
||||
case BoundKeyState.Up:
|
||||
return _disabled?.Invoke(session, entManager.GetCoordinates(fullInput.Coordinates), entManager.GetEntity(fullInput.Uid)) == true;
|
||||
case BoundKeyState.Down:
|
||||
return _enabled?.Invoke(session, entManager.GetCoordinates(fullInput.Coordinates), entManager.GetEntity(fullInput.Uid)) == true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Client Sanitization: unknown key state, just ignore
|
||||
@@ -183,7 +206,7 @@ namespace Robust.Shared.Input.Binding
|
||||
public sealed class NullInputCmdHandler : InputCmdHandler
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
|
||||
public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,12 +113,12 @@ namespace Robust.Shared.Input
|
||||
/// <summary>
|
||||
/// Local Coordinates of the pointer when the command was created.
|
||||
/// </summary>
|
||||
public EntityCoordinates Coordinates { get; }
|
||||
public NetCoordinates Coordinates { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity that was under the pointer when the command was created (if any).
|
||||
/// </summary>
|
||||
public EntityUid Uid { get; }
|
||||
public NetEntity Uid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="PointerInputCmdMessage"/>.
|
||||
@@ -126,8 +126,8 @@ namespace Robust.Shared.Input
|
||||
/// <param name="tick">Client tick this was created.</param>
|
||||
/// <param name="inputFunctionId">Function this command is changing.</param>
|
||||
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
|
||||
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, EntityCoordinates coordinates)
|
||||
: this(tick, subTick, inputFunctionId, coordinates, EntityUid.Invalid) { }
|
||||
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, NetCoordinates coordinates)
|
||||
: this(tick, subTick, inputFunctionId, coordinates, NetEntity.Invalid) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="PointerInputCmdMessage"/> with an optional Entity reference.
|
||||
@@ -136,7 +136,7 @@ namespace Robust.Shared.Input
|
||||
/// <param name="inputFunctionId">Function this command is changing.</param>
|
||||
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
|
||||
/// <param name="uid">Entity that was under the pointer when the command was created.</param>
|
||||
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, EntityCoordinates coordinates, EntityUid uid)
|
||||
public PointerInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, NetCoordinates coordinates, NetEntity uid)
|
||||
: base(tick, subTick, inputFunctionId)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
@@ -144,11 +144,51 @@ namespace Robust.Shared.Input
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles inputs clientside. This is used so the client can still interact with client-only entities without relying on
|
||||
/// <see cref="NetEntity"/>.
|
||||
/// </summary>
|
||||
public sealed class ClientFullInputCmdMessage : InputCmdMessage, IFullInputCmdMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// New state of the Input Function.
|
||||
/// </summary>
|
||||
public BoundKeyState State { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Local Coordinates of the pointer when the command was created.
|
||||
/// </summary>
|
||||
public EntityCoordinates Coordinates { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Screen Coordinates of the pointer when the command was created.
|
||||
/// </summary>
|
||||
public ScreenCoordinates ScreenCoordinates { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Entity that was under the pointer when the command was created (if any).
|
||||
/// </summary>
|
||||
public EntityUid Uid { get; init; }
|
||||
|
||||
public ClientFullInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId) : base(tick, subTick, inputFunctionId)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public interface IFullInputCmdMessage
|
||||
{
|
||||
GameTick Tick { get; }
|
||||
BoundKeyState State { get; }
|
||||
KeyFunctionId InputFunctionId { get; }
|
||||
ushort SubTick { get; }
|
||||
uint InputSequence { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An input command that has both state and pointer info.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class FullInputCmdMessage : InputCmdMessage
|
||||
public sealed class FullInputCmdMessage : InputCmdMessage, IFullInputCmdMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// New state of the Input Function.
|
||||
@@ -158,7 +198,7 @@ namespace Robust.Shared.Input
|
||||
/// <summary>
|
||||
/// Local Coordinates of the pointer when the command was created.
|
||||
/// </summary>
|
||||
public EntityCoordinates Coordinates { get; }
|
||||
public NetCoordinates Coordinates { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Screen Coordinates of the pointer when the command was created.
|
||||
@@ -168,7 +208,7 @@ namespace Robust.Shared.Input
|
||||
/// <summary>
|
||||
/// Entity that was under the pointer when the command was created (if any).
|
||||
/// </summary>
|
||||
public EntityUid Uid { get; }
|
||||
public NetEntity Uid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="FullInputCmdMessage"/>.
|
||||
@@ -179,8 +219,8 @@ namespace Robust.Shared.Input
|
||||
/// <param name="state">New state of the Input Function.</param>
|
||||
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
|
||||
/// <param name="screenCoordinates"></param>
|
||||
public FullInputCmdMessage(GameTick tick, ushort subTick, int inputSequence, KeyFunctionId inputFunctionId, BoundKeyState state, EntityCoordinates coordinates, ScreenCoordinates screenCoordinates)
|
||||
: this(tick, subTick, inputFunctionId, state, coordinates, screenCoordinates, EntityUid.Invalid) { }
|
||||
public FullInputCmdMessage(GameTick tick, ushort subTick, int inputSequence, KeyFunctionId inputFunctionId, BoundKeyState state, NetCoordinates coordinates, ScreenCoordinates screenCoordinates)
|
||||
: this(tick, subTick, inputFunctionId, state, coordinates, screenCoordinates, NetEntity.Invalid) { }
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="FullInputCmdMessage"/> with an optional Entity reference.
|
||||
@@ -191,7 +231,7 @@ namespace Robust.Shared.Input
|
||||
/// <param name="coordinates">Local Coordinates of the pointer when the command was created.</param>
|
||||
/// <param name="screenCoordinates"></param>
|
||||
/// <param name="uid">Entity that was under the pointer when the command was created.</param>
|
||||
public FullInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, BoundKeyState state, EntityCoordinates coordinates, ScreenCoordinates screenCoordinates, EntityUid uid)
|
||||
public FullInputCmdMessage(GameTick tick, ushort subTick, KeyFunctionId inputFunctionId, BoundKeyState state, NetCoordinates coordinates, ScreenCoordinates screenCoordinates, NetEntity uid)
|
||||
: base(tick, subTick, inputFunctionId)
|
||||
{
|
||||
State = state;
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Robust.Shared.Map
|
||||
/// A set of coordinates relative to another entity.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct EntityCoordinates : IEquatable<EntityCoordinates>, ISpanFormattable
|
||||
{
|
||||
public static readonly EntityCoordinates Invalid = new(EntityUid.Invalid, Vector2.Zero);
|
||||
|
||||
102
Robust.Shared/Map/NetCoordinates.cs
Normal file
102
Robust.Shared/Map/NetCoordinates.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.Map;
|
||||
|
||||
/// <summary>
|
||||
/// A networked version of <see cref="EntityCoordinates"/>
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[Serializable, NetSerializable]
|
||||
public readonly struct NetCoordinates : IEquatable<NetCoordinates>, ISpanFormattable
|
||||
{
|
||||
public static readonly NetCoordinates Invalid = new(NetEntity.Invalid, Vector2.Zero);
|
||||
|
||||
/// <summary>
|
||||
/// Networked ID of the entity that this position is relative to.
|
||||
/// </summary>
|
||||
public readonly NetEntity NetEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Position in the entity's local space.
|
||||
/// </summary>
|
||||
public readonly Vector2 Position;
|
||||
|
||||
/// <summary>
|
||||
/// Location of the X axis local to the entity.
|
||||
/// </summary>
|
||||
public float X => Position.X;
|
||||
|
||||
/// <summary>
|
||||
/// Location of the Y axis local to the entity.
|
||||
/// </summary>
|
||||
public float Y => Position.Y;
|
||||
|
||||
public NetCoordinates(NetEntity netEntity, Vector2 position)
|
||||
{
|
||||
NetEntity = netEntity;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public NetCoordinates(NetEntity netEntity, float x, float y)
|
||||
{
|
||||
NetEntity = netEntity;
|
||||
Position = new Vector2(x, y);
|
||||
}
|
||||
#region IEquatable
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(NetCoordinates other)
|
||||
{
|
||||
return NetEntity.Equals(other.NetEntity) && Position.Equals(other.Position);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is NetCoordinates other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(NetEntity, Position);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Deconstructs the object into it's fields.
|
||||
/// </summary>
|
||||
/// <param name="entId">ID of the entity that this position is relative to.</param>
|
||||
/// <param name="localPos">Position in the entity's local space.</param>
|
||||
public void Deconstruct(out NetEntity entId, out Vector2 localPos)
|
||||
{
|
||||
entId = NetEntity;
|
||||
localPos = Position;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"NetEntity={NetEntity}, X={Position.X:N2}, Y={Position.Y:N2}";
|
||||
}
|
||||
|
||||
public string ToString(string? format, IFormatProvider? formatProvider) => ToString();
|
||||
|
||||
public bool TryFormat(
|
||||
Span<char> destination,
|
||||
out int charsWritten,
|
||||
ReadOnlySpan<char> format,
|
||||
IFormatProvider? provider)
|
||||
{
|
||||
return FormatHelpers.TryFormatInto(
|
||||
destination,
|
||||
out charsWritten,
|
||||
$"NetEntity={NetEntity}, X={Position.X:N2}, Y={Position.Y:N2}");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user