mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 11:40:52 +01:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1904207358 | ||
|
|
939840ddab |
@@ -1,4 +1,4 @@
|
||||
<Project>
|
||||
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
<!-- This file automatically reset by Tools/version.py -->
|
||||
|
||||
|
||||
@@ -54,73 +54,7 @@ END TEMPLATE-->
|
||||
*None yet*
|
||||
|
||||
|
||||
## 166.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* EntityUid-NetEntity conversion methods now return null when given a null value, rather than returning an invalid id.
|
||||
* ExpandPvsEvent now defaults to using null lists to reduce allocations.
|
||||
* Various component lifestage related methods have been moved from the `Component` class to `EntityManager`.
|
||||
* Session/client specific PVS overrides are now always recursive, which means that all children of the overriden entity will also get sent.
|
||||
|
||||
### New features
|
||||
|
||||
* Added a SortedSet yaml serializer.
|
||||
|
||||
### Other
|
||||
|
||||
* AddComponentUninitialized is now marked as obsolete and will be removed in the future.
|
||||
* DebugTools.AssertOwner() now accepts null components.
|
||||
|
||||
|
||||
## 165.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The arguments of `SplitContainer`s resize-finished event have changed.
|
||||
|
||||
### New features
|
||||
|
||||
* The YAML validator now checks the default values of ProtoId<T> and EntProtoId data fields.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* The minimum draggable area of split containers now blocks mouse inputs.
|
||||
|
||||
|
||||
## 164.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Make automatic component states infer cloneData.
|
||||
* Removed cloneData from AutoNetworkedFieldAttribute. This is now automatically inferred.
|
||||
|
||||
### Internal
|
||||
|
||||
* Reduce Transform GetComponents in RecursiveDeleteEntity.
|
||||
|
||||
|
||||
## 163.0.0
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* Moved TimedDespawn to engine for a component that deletes the attached entity after a timer has elapsed.
|
||||
|
||||
### New features
|
||||
|
||||
* Add ExecuteCommand for integration tests.
|
||||
* Allow adding / removing widgets of cub-controls.
|
||||
* Give maps / grids a default name to help with debugging.
|
||||
* Use ToPrettyString in component resolve errors to help with debugging.
|
||||
|
||||
### Bugfixes
|
||||
|
||||
* Fix console backspace exception.
|
||||
* Fix rendering invalid maps spamming exceptions every frame.
|
||||
|
||||
### Internal
|
||||
|
||||
* Move ClientGameStatemanager local variables to fields to avoid re-allocating every tick.
|
||||
## 162.2.2
|
||||
|
||||
|
||||
## 162.2.1
|
||||
|
||||
@@ -43,9 +43,9 @@ namespace Robust.Client.GameObjects
|
||||
base.FlushEntities();
|
||||
}
|
||||
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName, out MetaDataComponent metadata)
|
||||
EntityUid IClientEntityManagerInternal.CreateEntity(string? prototypeName)
|
||||
{
|
||||
return base.CreateEntity(prototypeName, out metadata);
|
||||
return base.CreateEntity(prototypeName);
|
||||
}
|
||||
|
||||
void IClientEntityManagerInternal.InitializeEntity(EntityUid entity, MetaDataComponent? meta)
|
||||
@@ -93,10 +93,14 @@ namespace Robust.Client.GameObjects
|
||||
base.Dirty(uid, component, meta);
|
||||
}
|
||||
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metaDataComponent = null)
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
public override EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
{
|
||||
if (uid == null)
|
||||
return null;
|
||||
|
||||
if (_playerManager.LocalPlayer?.ControlledEntity == uid)
|
||||
return base.ToPrettyString(uid) with { Session = _playerManager.LocalPlayer.Session };
|
||||
return base.ToPrettyString(uid).Value with { Session = _playerManager.LocalPlayer.Session };
|
||||
|
||||
return base.ToPrettyString(uid);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using TerraFX.Interop.Windows;
|
||||
|
||||
namespace Robust.Client.GameObjects
|
||||
{
|
||||
@@ -116,7 +117,7 @@ namespace Robust.Client.GameObjects
|
||||
|
||||
if (compTrack.ComponentType == null)
|
||||
{
|
||||
_sawmill.Error("Attempted to play a component animation without any component specified.");
|
||||
_sawmill.Error($"Attempted to play a component animation without any component specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -135,14 +136,8 @@ namespace Robust.Client.GameObjects
|
||||
// In principle there is nothing wrong with this, as long as the property of the component being
|
||||
// animated is not part of the networked state and setting it does not dirty the component. Hence only a
|
||||
// warning in debug mode.
|
||||
if (reg.NetID != null && compTrack.Property != null)
|
||||
{
|
||||
if (animatedComp.GetType().GetProperty(compTrack.Property) is { } property &&
|
||||
property.HasCustomAttribute<AutoNetworkedFieldAttribute>())
|
||||
{
|
||||
_sawmill.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(component.Owner)}");
|
||||
}
|
||||
}
|
||||
if (reg.NetID != null)
|
||||
_sawmill.Warning($"Playing a component animation on a networked component {reg.Name} belonging to {ToPrettyString(component.Owner)}");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Robust.Client.GameObjects
|
||||
{
|
||||
// These methods are used by the Game State Manager.
|
||||
|
||||
EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata);
|
||||
EntityUid CreateEntity(string? prototypeName);
|
||||
|
||||
void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null);
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Physics;
|
||||
@@ -53,14 +52,6 @@ namespace Robust.Client.GameStates
|
||||
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 readonly HashSet<NetEntity> _stateEnts = new();
|
||||
private readonly List<EntityUid> _toDelete = new();
|
||||
private readonly List<Component> _toRemove = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<ushort, ComponentState>> _outputData = new();
|
||||
private readonly List<(EntityUid, TransformComponent)> _queuedBroadphaseUpdates = new();
|
||||
|
||||
private readonly ObjectPool<Dictionary<ushort, ComponentState>> _compDataPool =
|
||||
new DefaultObjectPool<Dictionary<ushort, ComponentState>>(new DictPolicy<ushort, ComponentState>(), 256);
|
||||
|
||||
private uint _metaCompNetId;
|
||||
|
||||
@@ -598,13 +589,14 @@ namespace Robust.Client.GameStates
|
||||
/// </remarks>
|
||||
private void MergeImplicitData(IEnumerable<NetEntity> createdEntities)
|
||||
{
|
||||
var outputData = new Dictionary<NetEntity, Dictionary<ushort, ComponentState>>();
|
||||
var bus = _entityManager.EventBus;
|
||||
|
||||
foreach (var netEntity in createdEntities)
|
||||
{
|
||||
var (createdEntity, meta) = _entityManager.GetEntityData(netEntity);
|
||||
var compData = _compDataPool.Get();
|
||||
_outputData.Add(netEntity, compData);
|
||||
var compData = new Dictionary<ushort, ComponentState>();
|
||||
outputData.Add(netEntity, compData);
|
||||
|
||||
foreach (var (netId, component) in meta.NetComponents)
|
||||
{
|
||||
@@ -617,14 +609,7 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
}
|
||||
|
||||
_processor.MergeImplicitData(_outputData);
|
||||
|
||||
foreach (var data in _outputData.Values)
|
||||
{
|
||||
_compDataPool.Return(data);
|
||||
}
|
||||
|
||||
_outputData.Clear();
|
||||
_processor.MergeImplicitData(outputData);
|
||||
}
|
||||
|
||||
private void AckGameState(GameTick sequence)
|
||||
@@ -702,10 +687,12 @@ namespace Robust.Client.GameStates
|
||||
if (metaState == null)
|
||||
throw new MissingMetadataException(es.NetEntity);
|
||||
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId, out var newMeta);
|
||||
var uid = _entities.CreateEntity(metaState.PrototypeId);
|
||||
_toCreate.Add(es.NetEntity, es);
|
||||
var newMeta = metas.GetComponent(uid);
|
||||
_toApply.Add(uid, (es.NetEntity, newMeta, false, GameTick.Zero, es, null));
|
||||
|
||||
|
||||
// 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.
|
||||
@@ -735,7 +722,9 @@ namespace Robust.Client.GameStates
|
||||
if (_toCreate.ContainsKey(es.NetEntity))
|
||||
continue;
|
||||
|
||||
if (!_entityManager.TryGetEntityData(es.NetEntity, out var uid, out var meta))
|
||||
var uid = _entityManager.GetEntity(es.NetEntity);
|
||||
|
||||
if (!metas.TryGetComponent(uid, out var meta))
|
||||
continue;
|
||||
|
||||
bool isEnteringPvs = (meta.Flags & MetaDataFlags.Detached) != 0;
|
||||
@@ -750,7 +739,7 @@ namespace Robust.Client.GameStates
|
||||
continue;
|
||||
}
|
||||
|
||||
_toApply.Add(uid.Value, (es.NetEntity, meta, isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
_toApply.Add(uid, (es.NetEntity, meta, isEnteringPvs, meta.LastStateApplied, es, null));
|
||||
meta.LastStateApplied = curState.ToSequence;
|
||||
}
|
||||
|
||||
@@ -796,7 +785,7 @@ namespace Robust.Client.GameStates
|
||||
state = (meta.NetEntity, meta, false, GameTick.Zero, null, null);
|
||||
}
|
||||
|
||||
_queuedBroadphaseUpdates.Clear();
|
||||
var queuedBroadphaseUpdates = new List<(EntityUid, TransformComponent)>(enteringPvs);
|
||||
|
||||
// Apply entity states.
|
||||
using (_prof.Group("Apply States"))
|
||||
@@ -816,7 +805,7 @@ namespace Robust.Client.GameStates
|
||||
DebugTools.Assert(xform.Broadphase == BroadphaseData.Invalid);
|
||||
xform.Broadphase = null;
|
||||
if (!_toApply.TryGetValue(xform.ParentUid, out var parent) || !parent.EnteringPvs)
|
||||
_queuedBroadphaseUpdates.Add((entity, xform));
|
||||
queuedBroadphaseUpdates.Add((entity, xform));
|
||||
}
|
||||
|
||||
_prof.WriteValue("Count", ProfData.Int32(_toApply.Count));
|
||||
@@ -827,7 +816,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var (uid, xform) in _queuedBroadphaseUpdates)
|
||||
foreach (var (uid, xform) in queuedBroadphaseUpdates)
|
||||
{
|
||||
lookupSys.FindAndAddToEntityTree(uid, true, xform);
|
||||
}
|
||||
@@ -844,7 +833,7 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessDeletions(delSpan, xforms, xformSys);
|
||||
ProcessDeletions(delSpan, xforms, metas, xformSys);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -881,17 +870,17 @@ namespace Robust.Client.GameStates
|
||||
_sawmill.Info($"Resetting all entity states to tick {state.ToSequence}.");
|
||||
|
||||
// Construct hashset for set.Contains() checks.
|
||||
_stateEnts.Clear();
|
||||
var entityStates = state.EntityStates.Span;
|
||||
var stateEnts = new HashSet<NetEntity>();
|
||||
foreach (var entState in entityStates)
|
||||
{
|
||||
_stateEnts.Add(entState.NetEntity);
|
||||
stateEnts.Add(entState.NetEntity);
|
||||
}
|
||||
|
||||
var xforms = _entities.GetEntityQuery<TransformComponent>();
|
||||
var xformSys = _entitySystemManager.GetEntitySystem<SharedTransformSystem>();
|
||||
|
||||
_toDelete.Clear();
|
||||
var toDelete = new List<EntityUid>(Math.Max(64, _entities.EntityCount - stateEnts.Count));
|
||||
|
||||
// Client side entities won't need the transform, but that should always be a tiny minority of entities
|
||||
var metaQuery = _entityManager.AllEntityQueryEnumerator<MetaDataComponent, TransformComponent>();
|
||||
@@ -902,16 +891,14 @@ namespace Robust.Client.GameStates
|
||||
if (metadata.NetEntity.IsClientSide())
|
||||
{
|
||||
if (deleteClientEntities)
|
||||
_toDelete.Add(ent);
|
||||
|
||||
toDelete.Add(ent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_stateEnts.Contains(netEnt))
|
||||
if (stateEnts.Contains(netEnt))
|
||||
{
|
||||
if (resetAllEntities || metadata.LastStateApplied > state.ToSequence)
|
||||
metadata.LastStateApplied = GameTick.Zero; // TODO track last-state-applied for individual components? Is it even worth it?
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -929,14 +916,13 @@ namespace Robust.Client.GameStates
|
||||
&& !deleteClientEntities // don't add duplicates
|
||||
&& _entities.IsClientSide(child.Value))
|
||||
{
|
||||
_toDelete.Add(child.Value);
|
||||
toDelete.Add(child.Value);
|
||||
}
|
||||
}
|
||||
|
||||
_toDelete.Add(ent);
|
||||
toDelete.Add(ent);
|
||||
}
|
||||
|
||||
foreach (var ent in _toDelete)
|
||||
foreach (var ent in toDelete)
|
||||
{
|
||||
_entities.DeleteEntity(ent);
|
||||
}
|
||||
@@ -945,6 +931,7 @@ namespace Robust.Client.GameStates
|
||||
private void ProcessDeletions(
|
||||
ReadOnlySpan<NetEntity> delSpan,
|
||||
EntityQuery<TransformComponent> xforms,
|
||||
EntityQuery<MetaDataComponent> metas,
|
||||
SharedTransformSystem xformSys)
|
||||
{
|
||||
// Processing deletions is non-trivial, because by default deletions will also delete all child entities.
|
||||
@@ -1015,7 +1002,6 @@ namespace Robust.Client.GameStates
|
||||
// things like container insertion and ejection.
|
||||
|
||||
using var _ = _prof.Group("Leave PVS");
|
||||
detached.EnsureCapacity(toDetach.Count);
|
||||
|
||||
foreach (var (tick, ents) in toDetach)
|
||||
{
|
||||
@@ -1038,7 +1024,9 @@ namespace Robust.Client.GameStates
|
||||
{
|
||||
foreach (var netEntity in entities)
|
||||
{
|
||||
if (!_entityManager.TryGetEntityData(netEntity, out var ent, out var meta))
|
||||
var ent = _entityManager.GetEntity(netEntity);
|
||||
|
||||
if (!metas.TryGetComponent(ent, out var meta))
|
||||
continue;
|
||||
|
||||
if (meta.LastStateApplied > maxTick)
|
||||
@@ -1054,10 +1042,10 @@ namespace Robust.Client.GameStates
|
||||
if (lastStateApplied.HasValue)
|
||||
meta.LastStateApplied = lastStateApplied.Value;
|
||||
|
||||
var xform = xforms.GetComponent(ent.Value);
|
||||
var xform = xforms.GetComponent(ent);
|
||||
if (xform.ParentUid.IsValid())
|
||||
{
|
||||
lookupSys.RemoveFromEntityTree(ent.Value, xform);
|
||||
lookupSys.RemoveFromEntityTree(ent, xform);
|
||||
xform.Broadphase = BroadphaseData.Invalid;
|
||||
|
||||
// In some cursed scenarios an entity inside of a container can leave PVS without the container itself leaving PVS.
|
||||
@@ -1066,13 +1054,13 @@ namespace Robust.Client.GameStates
|
||||
if ((meta.Flags & MetaDataFlags.InContainer) != 0 &&
|
||||
metas.TryGetComponent(xform.ParentUid, out var containerMeta) &&
|
||||
(containerMeta.Flags & MetaDataFlags.Detached) == 0 &&
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, ent.Value, out container, null, true))
|
||||
containerSys.TryGetContainingContainer(xform.ParentUid, ent, out container, null, true))
|
||||
{
|
||||
container.Remove(ent.Value, _entities, xform, meta, false, true);
|
||||
container.Remove(ent, _entities, xform, meta, false, true);
|
||||
}
|
||||
|
||||
meta._flags |= MetaDataFlags.Detached;
|
||||
xformSys.DetachParentToNull(ent.Value, xform);
|
||||
xformSys.DetachParentToNull(ent, xform);
|
||||
DebugTools.Assert((meta.Flags & MetaDataFlags.InContainer) == 0);
|
||||
|
||||
if (container != null)
|
||||
@@ -1152,15 +1140,14 @@ namespace Robust.Client.GameStates
|
||||
// First remove any deleted components
|
||||
if (curState?.NetComponents != null)
|
||||
{
|
||||
_toRemove.Clear();
|
||||
|
||||
RemQueue<Component> toRemove = new();
|
||||
foreach (var (id, comp) in meta.NetComponents)
|
||||
{
|
||||
if (comp.NetSyncEnabled && !curState.NetComponents.Contains(id))
|
||||
_toRemove.Add(comp);
|
||||
toRemove.Add(comp);
|
||||
}
|
||||
|
||||
foreach (var comp in _toRemove)
|
||||
foreach (var comp in toRemove)
|
||||
{
|
||||
_entities.RemoveComponent(uid, comp, meta);
|
||||
}
|
||||
@@ -1428,15 +1415,14 @@ namespace Robust.Client.GameStates
|
||||
}
|
||||
|
||||
// ensure we don't have any extra components
|
||||
_toRemove.Clear();
|
||||
|
||||
RemQueue<Component> toRemove = new();
|
||||
foreach (var (id, comp) in meta.NetComponents)
|
||||
{
|
||||
if (comp.NetSyncEnabled && !lastState.ContainsKey(id))
|
||||
_toRemove.Add(comp);
|
||||
toRemove.Add(comp);
|
||||
}
|
||||
|
||||
foreach (var comp in _toRemove)
|
||||
foreach (var comp in toRemove)
|
||||
{
|
||||
_entities.RemoveComponent(uid, comp);
|
||||
}
|
||||
|
||||
@@ -338,12 +338,10 @@ namespace Robust.Client.Graphics.Clyde
|
||||
}
|
||||
|
||||
var mapId = eye.Position.MapId;
|
||||
if (mapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
// If this map has lighting disabled, return
|
||||
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||
if (!_entityManager.TryGetComponent<MapComponent>(mapUid, out var map) || !map.LightingEnabled)
|
||||
if (!_entityManager.GetComponent<MapComponent>(mapUid).LightingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.2" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" Version="0.1.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
|
||||
<PackageReference Include="OpenToolkit.Graphics" Version="4.0.0-pre9.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.SharpFont" Version="1.0.1" PrivateAssets="compile" />
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Spawners;
|
||||
|
||||
namespace Robust.Client.Spawners;
|
||||
|
||||
public sealed class TimedDespawnSystem : SharedTimedDespawnSystem
|
||||
{
|
||||
protected override bool CanDelete(EntityUid uid)
|
||||
{
|
||||
return IsClientSide(uid);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Client.UserInterface.Controls
|
||||
@@ -15,17 +14,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// for each enum value to see how the different options work.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public SplitResizeMode ResizeMode
|
||||
{
|
||||
get => _resizeMode;
|
||||
set
|
||||
{
|
||||
_resizeMode = value;
|
||||
_splitDragArea.Visible = value != SplitResizeMode.NotResizable;
|
||||
}
|
||||
}
|
||||
|
||||
private SplitResizeMode _resizeMode = SplitResizeMode.RespectChildrenMinSize;
|
||||
public SplitResizeMode ResizeMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Width of the split in virtual pixels
|
||||
@@ -41,7 +30,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private float _splitWidth = 10;
|
||||
private float _splitWidth;
|
||||
|
||||
/// <summary>
|
||||
/// This width determines the minimum size of the draggable area around the split. This has no effect if it
|
||||
@@ -49,13 +38,11 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
public float MinDraggableWidth = 10f;
|
||||
|
||||
public float DraggableWidth => MathF.Max(MinDraggableWidth, _splitWidth);
|
||||
|
||||
/// <summary>
|
||||
/// Virtual pixel offset from the edge beyond which the split cannot be moved.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SplitEdgeSeparation { get; set; } = 10;
|
||||
public float SplitEdgeSeparation { get; set; }
|
||||
|
||||
private float _splitStart;
|
||||
|
||||
@@ -71,7 +58,6 @@ namespace Robust.Client.UserInterface.Controls
|
||||
_splitStart = value - _splitWidth / 2;
|
||||
ClampSplitCenter();
|
||||
InvalidateMeasure();
|
||||
OnSplitResized?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,51 +81,13 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
private SplitState _splitState = SplitState.Auto;
|
||||
private SplitOrientation _orientation;
|
||||
private SplitStretchDirection _stretchDirection = SplitStretchDirection.BottomRight;
|
||||
private SplitState _splitState;
|
||||
private bool _dragging;
|
||||
private float _dragOffset;
|
||||
private SplitOrientation _orientation;
|
||||
private SplitStretchDirection _stretchDirection;
|
||||
|
||||
private bool Vertical => Orientation == SplitOrientation.Vertical;
|
||||
|
||||
private readonly SplitDragControl _splitDragArea = new();
|
||||
|
||||
/// <summary>
|
||||
/// The upper/left control in the split container.
|
||||
/// </summary>
|
||||
public Control? First
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChildCount < 3)
|
||||
return null;
|
||||
|
||||
DebugTools.AssertNotEqual(GetChild(0), _splitDragArea);
|
||||
return GetChild(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The lower/right control in the split container.
|
||||
/// </summary>
|
||||
public Control? Second
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ChildCount < 3)
|
||||
return null;
|
||||
|
||||
DebugTools.AssertNotEqual(GetChild(1), _splitDragArea);
|
||||
return GetChild(1);
|
||||
}
|
||||
}
|
||||
|
||||
public (Control First, Control Second)? Splits => ChildCount < 3 ? null : (GetChild(0), GetChild(1));
|
||||
|
||||
public event Action? OnSplitResizeFinished;
|
||||
public event Action? OnSplitResized;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the split position should be set manually or automatically.
|
||||
/// </summary>
|
||||
@@ -183,53 +131,73 @@ namespace Robust.Client.UserInterface.Controls
|
||||
public SplitContainer()
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
AddChild(_splitDragArea);
|
||||
_splitDragArea.Visible = _resizeMode != SplitResizeMode.NotResizable;
|
||||
_splitDragArea.DefaultCursorShape = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
_splitDragArea.OnMouseUp += StopDragging;
|
||||
_splitDragArea.OnMouseDown += StartDragging;
|
||||
_splitDragArea.OnMouseMove += OnMove;
|
||||
_splitState = SplitState.Auto;
|
||||
_stretchDirection = SplitStretchDirection.BottomRight;
|
||||
_dragging = false;
|
||||
ResizeMode = SplitResizeMode.RespectChildrenMinSize;
|
||||
SplitWidth = 10;
|
||||
SplitEdgeSeparation = 10;
|
||||
}
|
||||
|
||||
private void OnMove(GUIMouseMoveEventArgs args)
|
||||
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
{
|
||||
if (ResizeMode == SplitResizeMode.NotResizable)
|
||||
return;
|
||||
base.MouseMove(args);
|
||||
|
||||
if (!_dragging)
|
||||
return;
|
||||
if (ResizeMode == SplitResizeMode.NotResizable) return;
|
||||
|
||||
// Source control might be either the container, or the separator.
|
||||
// So we manually calculate the relative coordinates wrt the container.
|
||||
var relative = args.GlobalPosition - GlobalPosition;
|
||||
SplitCenter = Vertical ? relative.Y - _dragOffset : relative.X + _dragOffset;
|
||||
if (_dragging)
|
||||
{
|
||||
var newOffset = Vertical ? args.RelativePosition.Y : args.RelativePosition.X;
|
||||
|
||||
SplitCenter = newOffset;
|
||||
DefaultCursorShape = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
}
|
||||
else
|
||||
{
|
||||
// on mouseover, check if they are over the split and change the cursor accordingly
|
||||
var cursor = CursorShape.Arrow;
|
||||
if (CanDragAt(args.RelativePosition))
|
||||
{
|
||||
cursor = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
}
|
||||
|
||||
DefaultCursorShape = cursor;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ChildAdded(Control newChild)
|
||||
|
||||
protected internal override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.ChildAdded(newChild);
|
||||
_splitDragArea.SetPositionLast();
|
||||
base.KeyBindDown(args);
|
||||
|
||||
if (ResizeMode == SplitResizeMode.NotResizable) return;
|
||||
|
||||
if (_dragging || args.Function != EngineKeyFunctions.UIClick) return;
|
||||
|
||||
if (CanDragAt(args.RelativePosition))
|
||||
{
|
||||
_dragging = true;
|
||||
_splitState = SplitState.Manual;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartDragging(GUIBoundKeyEventArgs args)
|
||||
protected internal override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
if (ResizeMode == SplitResizeMode.NotResizable || _dragging)
|
||||
return;
|
||||
base.KeyBindUp(args);
|
||||
|
||||
_dragging = true;
|
||||
_dragOffset = DraggableWidth / 2 - (Vertical ? args.RelativePosition.Y : args.RelativePosition.X);
|
||||
_splitState = SplitState.Manual;
|
||||
DefaultCursorShape = Vertical ? CursorShape.VResize : CursorShape.HResize;
|
||||
}
|
||||
|
||||
private void StopDragging(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
if (!_dragging)
|
||||
return;
|
||||
if (args.Function != EngineKeyFunctions.UIClick) return;
|
||||
|
||||
_dragging = false;
|
||||
DefaultCursorShape = CursorShape.Arrow;
|
||||
OnSplitResizeFinished?.Invoke();
|
||||
}
|
||||
|
||||
private bool CanDragAt(Vector2 relativePosition)
|
||||
{
|
||||
var distance = Vertical
|
||||
? Math.Abs(relativePosition.Y - SplitCenter)
|
||||
: Math.Abs(relativePosition.X - SplitCenter);
|
||||
|
||||
return distance <= _splitWidth || distance <= MinDraggableWidth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -250,7 +218,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
desiredSplit += Vertical ? desiredSize.Value.Y - Size.Y : desiredSize.Value.X - Size.X;
|
||||
_splitStart = MathHelper.Clamp(desiredSplit, SplitEdgeSeparation, splitMax);
|
||||
|
||||
if (ResizeMode == SplitResizeMode.RespectChildrenMinSize && ChildCount == 3)
|
||||
if (ResizeMode == SplitResizeMode.RespectChildrenMinSize && ChildCount == 2)
|
||||
{
|
||||
var first = GetChild(0);
|
||||
var second = GetChild(1);
|
||||
@@ -266,7 +234,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
||||
{
|
||||
if (ChildCount < 3)
|
||||
if (ChildCount != 2)
|
||||
{
|
||||
return finalSize;
|
||||
}
|
||||
@@ -319,23 +287,15 @@ namespace Robust.Client.UserInterface.Controls
|
||||
}
|
||||
}
|
||||
|
||||
// location & size of the draggable area may be larger than the split area.
|
||||
var dragCenter = _splitStart + _splitWidth / 2;
|
||||
var dragWidth = DraggableWidth;
|
||||
var dragStart = dragCenter - dragWidth / 2;
|
||||
var dragEnd = dragCenter + dragWidth / 2;
|
||||
|
||||
if (Vertical)
|
||||
{
|
||||
first.Arrange(new UIBox2(0, 0, finalSize.X, _splitStart));
|
||||
second.Arrange(new UIBox2(0, _splitStart + _splitWidth, finalSize.X, finalSize.Y));
|
||||
_splitDragArea.Arrange(new UIBox2(0, dragStart, finalSize.X, dragEnd));
|
||||
}
|
||||
else
|
||||
{
|
||||
first.Arrange(new UIBox2(0, 0, _splitStart, finalSize.Y));
|
||||
second.Arrange(new UIBox2(_splitStart + _splitWidth, 0, finalSize.X, finalSize.Y));
|
||||
_splitDragArea.Arrange(new UIBox2(dragStart, 0, dragEnd, finalSize.Y));
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
@@ -343,7 +303,7 @@ namespace Robust.Client.UserInterface.Controls
|
||||
|
||||
protected override Vector2 MeasureOverride(Vector2 availableSize)
|
||||
{
|
||||
if (ChildCount < 3)
|
||||
if (ChildCount != 2)
|
||||
{
|
||||
return Vector2.Zero;
|
||||
}
|
||||
@@ -460,40 +420,5 @@ namespace Robust.Client.UserInterface.Controls
|
||||
/// </summary>
|
||||
TopLeft,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple control to intercept mous events and redirect them to the parent.
|
||||
/// </summary>
|
||||
private sealed class SplitDragControl : Control
|
||||
{
|
||||
public event Action<GUIBoundKeyEventArgs>? OnMouseDown;
|
||||
public event Action<GUIBoundKeyEventArgs>? OnMouseUp;
|
||||
public event Action<GUIMouseMoveEventArgs>? OnMouseMove;
|
||||
|
||||
public SplitDragControl()
|
||||
{
|
||||
MouseFilter = MouseFilterMode.Stop;
|
||||
}
|
||||
|
||||
protected internal override void MouseMove(GUIMouseMoveEventArgs args)
|
||||
{
|
||||
base.MouseMove(args);
|
||||
OnMouseMove?.Invoke(args);
|
||||
}
|
||||
|
||||
protected internal override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindDown(args);
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
OnMouseDown?.Invoke(args);
|
||||
}
|
||||
|
||||
protected internal override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||
{
|
||||
base.KeyBindUp(args);
|
||||
if (args.Function == EngineKeyFunctions.UIClick)
|
||||
OnMouseUp?.Invoke(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,11 +69,12 @@ public abstract class UIScreen : LayoutContainer
|
||||
|
||||
public void RemoveWidget<T>() where T : UIWidget, new()
|
||||
{
|
||||
if (!_widgets.Remove(typeof(T), out var widget))
|
||||
return;
|
||||
if (_widgets.TryGetValue(typeof(T), out var widget))
|
||||
{
|
||||
RemoveChild(widget);
|
||||
}
|
||||
|
||||
widget.Parent?.RemoveChild(widget);
|
||||
RemoveChildren(widget);
|
||||
_widgets.Remove(typeof(T));
|
||||
}
|
||||
|
||||
internal void OnRemoved()
|
||||
@@ -102,14 +103,6 @@ public abstract class UIScreen : LayoutContainer
|
||||
AddChild(widget);
|
||||
}
|
||||
|
||||
public void AddWidgetDirect(UIWidget widget)
|
||||
{
|
||||
if (!_widgets.TryAdd(widget.GetType(), widget))
|
||||
throw new Exception("Tried to add duplicate widget to screen!");
|
||||
|
||||
RegisterChildren(widget);
|
||||
}
|
||||
|
||||
public T? GetWidget<T>() where T : UIWidget, new()
|
||||
{
|
||||
return (T?) _widgets.GetValueOrDefault(typeof(T));
|
||||
|
||||
@@ -270,7 +270,7 @@ namespace Robust.Client.ViewVariables.Instances
|
||||
button.OnPressed += _ =>
|
||||
{
|
||||
ViewVariablesManager.OpenVV(
|
||||
new ViewVariablesComponentSelector(_netEntity, componentType.FullName));
|
||||
new ViewVariablesComponentSelector(_entityManager.GetNetEntity(_entity), componentType.FullName));
|
||||
};
|
||||
removeButton.OnPressed += _ =>
|
||||
{
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Robust.Server.Console
|
||||
break;
|
||||
|
||||
case ConsoleKey.Backspace:
|
||||
if (currentBuffer.Length > 0 && internalCursor > 0)
|
||||
if (currentBuffer.Length > 0)
|
||||
{
|
||||
currentBuffer = currentBuffer.Remove(internalCursor - 1, 1);
|
||||
internalCursor--;
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace Robust.Server.GameObjects
|
||||
#endif
|
||||
|
||||
private ISawmill _netEntSawmill = default!;
|
||||
private EntityQuery<ActorComponent> _actorQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -54,12 +53,6 @@ namespace Robust.Server.GameObjects
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
_actorQuery = GetEntityQuery<ActorComponent>();
|
||||
}
|
||||
|
||||
EntityUid IServerEntityManagerInternal.AllocEntity(EntityPrototype? prototype)
|
||||
{
|
||||
return AllocEntity(prototype, out _);
|
||||
@@ -85,15 +78,15 @@ namespace Robust.Server.GameObjects
|
||||
StartEntity(entity);
|
||||
}
|
||||
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
private protected override EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return base.CreateEntity(prototypeName, out metadata, 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, out metadata, 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,
|
||||
@@ -116,10 +109,15 @@ namespace Robust.Server.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
public override EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
public override EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
{
|
||||
_actorQuery.TryGetComponent(uid, out ActorComponent? actor);
|
||||
return base.ToPrettyString(uid) with { Session = actor?.PlayerSession };
|
||||
if (uid == null)
|
||||
return null;
|
||||
|
||||
TryGetComponent(uid, out ActorComponent? actor);
|
||||
|
||||
return base.ToPrettyString(uid).Value with { Session = actor?.PlayerSession };
|
||||
}
|
||||
|
||||
#region IEntityNetworkManager impl
|
||||
|
||||
@@ -95,7 +95,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <summary>
|
||||
/// List of <see cref="TIndex"/> that should always get sent to a certain <see cref="ICommonSession"/>.
|
||||
/// </summary>
|
||||
private readonly Dictionary<ICommonSession, HashSet<TIndex>> _sessionOverrides = new();
|
||||
private readonly Dictionary<ICommonSession, HashSet<TIndex>> _localOverrides = new();
|
||||
|
||||
/// <summary>
|
||||
/// Which <see cref="TIndex"/> where last seen/sent to a certain <see cref="ICommonSession"/>.
|
||||
@@ -207,7 +207,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
public bool TryGetChunk(EntityUid gridId, Vector2i chunkIndices, [NotNullWhen(true)] out HashSet<TIndex>? indices) =>
|
||||
_gridChunkContents[gridId].TryGetValue(chunkIndices, out indices);
|
||||
|
||||
public HashSet<TIndex>.Enumerator GetSessionOverrides(ICommonSession session) => _sessionOverrides[session].GetEnumerator();
|
||||
public HashSet<TIndex>.Enumerator GetElementsForSession(ICommonSession session) => _localOverrides[session].GetEnumerator();
|
||||
|
||||
private void AddIndexInternal(TIndex index, IIndexLocation location, HashSet<IChunkIndexLocation> dirtyChunks)
|
||||
{
|
||||
@@ -226,10 +226,10 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
gridLoc.Add(index);
|
||||
dirtyChunks.Add(gridChunkLocation);
|
||||
break;
|
||||
case SessionOverride sessionOverride:
|
||||
if (!_sessionOverrides.TryGetValue(sessionOverride.Session, out var set))
|
||||
return;
|
||||
set.Add(index);
|
||||
case LocalOverride localOverride:
|
||||
// might be gone due to disconnects
|
||||
if(!_localOverrides.ContainsKey(localOverride.Session)) return;
|
||||
_localOverrides[localOverride.Session].Add(index);
|
||||
break;
|
||||
case MapChunkLocation mapChunkLocation:
|
||||
// might be gone due to map-deletions
|
||||
@@ -253,14 +253,16 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
switch (location)
|
||||
{
|
||||
case GlobalOverride global:
|
||||
var set = global.Recursive ? _globalRecursiveOverrides : _globalOverrides;
|
||||
set.Remove(index);
|
||||
if (global.Recursive)
|
||||
_globalRecursiveOverrides.Remove(index);
|
||||
else
|
||||
_globalOverrides.Remove(index);
|
||||
break;
|
||||
case GridChunkLocation gridChunkLocation:
|
||||
_gridChunkContents[gridChunkLocation.GridId][gridChunkLocation.ChunkIndices].Remove(index);
|
||||
break;
|
||||
case SessionOverride sessionOverride:
|
||||
_sessionOverrides.GetValueOrDefault(sessionOverride.Session)?.Remove(index);
|
||||
case LocalOverride localOverride:
|
||||
_localOverrides[localOverride.Session].Remove(index);
|
||||
break;
|
||||
case MapChunkLocation mapChunkLocation:
|
||||
_mapChunkContents[mapChunkLocation.MapId][mapChunkLocation.ChunkIndices].Remove(index);
|
||||
@@ -274,7 +276,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <inheritdoc />
|
||||
public bool AddPlayer(ICommonSession session)
|
||||
{
|
||||
return _sessionOverrides.TryAdd(session, new()) & _lastSeen.TryAdd(session, new());
|
||||
return _localOverrides.TryAdd(session, new()) & _lastSeen.TryAdd(session, new());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -290,7 +292,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
/// <inheritdoc />
|
||||
public bool RemovePlayer(ICommonSession session)
|
||||
{
|
||||
if (_sessionOverrides.Remove(session, out var indices))
|
||||
if (_localOverrides.Remove(session, out var indices))
|
||||
{
|
||||
foreach (var index in indices)
|
||||
{
|
||||
@@ -410,7 +412,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride && oldLocation is SessionOverride)
|
||||
if (!removeFromOverride && oldLocation is LocalOverride)
|
||||
return;
|
||||
|
||||
if (oldLocation is GlobalOverride global &&
|
||||
@@ -424,29 +426,28 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
|
||||
/// <summary>
|
||||
/// Updates an <see cref="TIndex"/> to be sent to a specific <see cref="ICommonSession"/> at all times.
|
||||
/// This will always also send all children of the given entity.
|
||||
/// </summary>
|
||||
/// <param name="index">The <see cref="TIndex"/> to update.</param>
|
||||
/// <param name="session">The <see cref="ICommonSession"/> receiving the object.</param>
|
||||
/// <param name="removeFromOverride">An index at an override position will not be updated unless you set this flag.</param>
|
||||
public void AddSessionOverride(TIndex index, ICommonSession session, bool removeFromOverride)
|
||||
public void UpdateIndex(TIndex index, ICommonSession session, bool removeFromOverride = false)
|
||||
{
|
||||
if (!TryGetLocation(index, out var oldLocation))
|
||||
{
|
||||
RegisterUpdate(index, new SessionOverride(session));
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!removeFromOverride && oldLocation is GlobalOverride)
|
||||
if (!removeFromOverride || oldLocation is GlobalOverride)
|
||||
return;
|
||||
|
||||
if (oldLocation is SessionOverride local &&
|
||||
if (oldLocation is LocalOverride local &&
|
||||
(!removeFromOverride || local.Session == session))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterUpdate(index, new SessionOverride(session));
|
||||
RegisterUpdate(index, new LocalOverride(session));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -459,7 +460,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
{
|
||||
if (!removeFromOverride
|
||||
&& TryGetLocation(index, out var oldLocation)
|
||||
&& oldLocation is GlobalOverride or SessionOverride)
|
||||
&& oldLocation is GlobalOverride or LocalOverride)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -509,7 +510,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or SessionOverride && !removeFromOverride)
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
if (oldLocation is GridChunkLocation oldGrid &&
|
||||
@@ -540,7 +541,7 @@ public sealed class PVSCollection<TIndex> : IPVSCollection where TIndex : ICompa
|
||||
_indexLocations.TryGetValue(index, out var oldLocation);
|
||||
|
||||
//removeFromOverride is false 99% of the time.
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or SessionOverride && !removeFromOverride)
|
||||
if ((bufferedLocation ?? oldLocation) is GlobalOverride or LocalOverride && !removeFromOverride)
|
||||
return;
|
||||
|
||||
// Is this entity just returning to its old location?
|
||||
@@ -640,14 +641,14 @@ public struct GlobalOverride : IIndexLocation
|
||||
}
|
||||
}
|
||||
|
||||
public struct SessionOverride : IIndexLocation
|
||||
public struct LocalOverride : IIndexLocation
|
||||
{
|
||||
public SessionOverride(ICommonSession session)
|
||||
public LocalOverride(ICommonSession session)
|
||||
{
|
||||
Session = session;
|
||||
}
|
||||
|
||||
public readonly ICommonSession Session;
|
||||
public ICommonSession Session { get; init; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -22,14 +22,13 @@ public sealed class PvsOverrideSystem : EntitySystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to ensure that an entity is always sent to a specific client. By default this overrides any global or pre-existing
|
||||
/// client-specific overrides. Unlike global overrides, this is always recursive.
|
||||
/// Used to ensure that an entity is always sent to a specific client. Overrides any global or pre-existing
|
||||
/// client-specific overrides.
|
||||
/// </summary>
|
||||
/// <param name="removeExistingOverride">Whether or not to supersede existing overrides.</param>
|
||||
/// <param name="recursive">If true, this will also recursively send any children of the given index.</param>
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session, bool removeExistingOverride = true)
|
||||
public void AddSessionOverride(EntityUid uid, ICommonSession session,bool removeExistingOverride = true)
|
||||
{
|
||||
_pvs.EntityPVSCollection.AddSessionOverride(GetNetEntity(uid), session, removeExistingOverride);
|
||||
_pvs.EntityPVSCollection.UpdateIndex(GetNetEntity(uid), session, removeExistingOverride);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -756,15 +756,15 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
}
|
||||
globalRecursiveEnumerator.Dispose();
|
||||
|
||||
var sessionOverrides = _entityPvsCollection.GetSessionOverrides(session);
|
||||
while (sessionOverrides.MoveNext())
|
||||
var localEnumerator = _entityPvsCollection.GetElementsForSession(session);
|
||||
while (localEnumerator.MoveNext())
|
||||
{
|
||||
var netEntity = sessionOverrides.Current;
|
||||
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, true);
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
sessionOverrides.Dispose();
|
||||
localEnumerator.Dispose();
|
||||
|
||||
foreach (var viewerEntity in viewers)
|
||||
{
|
||||
@@ -774,22 +774,16 @@ internal sealed partial class PvsSystem : EntitySystem
|
||||
|
||||
var expandEvent = new ExpandPvsEvent(session);
|
||||
RaiseLocalEvent(ref expandEvent);
|
||||
if (expandEvent.Entities != null)
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
foreach (var entityUid in expandEvent.Entities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen, in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget);
|
||||
}
|
||||
|
||||
if (expandEvent.RecursiveEntities != null)
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
foreach (var entityUid in expandEvent.RecursiveEntities)
|
||||
{
|
||||
RecursivelyAddOverride(in entityUid, lastAcked, lastSent, visibleEnts, lastSeen,in fromTick,
|
||||
ref newEntityCount, ref enteredEntityCount, ref entStateCount, in newEntityBudget, in enteredEntityBudget, true);
|
||||
}
|
||||
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);
|
||||
@@ -1396,20 +1390,20 @@ Transform last modified: {Transform(uid).LastModifiedTick}");
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public struct ExpandPvsEvent
|
||||
public readonly struct ExpandPvsEvent
|
||||
{
|
||||
public readonly IPlayerSession Session;
|
||||
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set.
|
||||
/// </summary>
|
||||
public List<EntityUid>? Entities;
|
||||
public readonly List<EntityUid> Entities = new();
|
||||
|
||||
/// <summary>
|
||||
/// List of entities that will get added to this session's PVS set. Unlike <see cref="Entities"/> this will also
|
||||
/// recursively add all children of the given entity.
|
||||
/// </summary>
|
||||
public List<EntityUid>? RecursiveEntities;
|
||||
public readonly List<EntityUid> RecursiveEntities = new();
|
||||
|
||||
public ExpandPvsEvent(IPlayerSession session)
|
||||
{
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Spawners;
|
||||
|
||||
namespace Robust.Server.Spawners;
|
||||
|
||||
public sealed class TimedDespawnSystem : SharedTimedDespawnSystem
|
||||
{
|
||||
protected override bool CanDelete(EntityUid uid)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ namespace Robust.Server.ViewVariables
|
||||
}
|
||||
|
||||
if (value is EntityUid uid)
|
||||
return IoCManager.Resolve<IEntityManager>().GetComponentOrNull<MetaDataComponent>(uid)?.NetEntity ?? NetEntity.Invalid;
|
||||
return IoCManager.Resolve<IEntityManager>().GetNetEntity(uid);
|
||||
|
||||
var valType = value.GetType();
|
||||
if (!valType.IsValueType)
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using static Microsoft.CodeAnalysis.SymbolDisplayFormat;
|
||||
|
||||
namespace Robust.Shared.CompNetworkGenerator
|
||||
{
|
||||
@@ -15,23 +14,15 @@ 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 const string GlobalEntityUidSetName = "global::System.Collections.Generic.HashSet<global::Robust.Shared.GameObjects.EntityUid>";
|
||||
private const string GlobalNetEntityUidSetName = "global::System.Collections.Generic.HashSet<global::Robust.Shared.GameObjects.NetEntity>";
|
||||
|
||||
private const string GlobalEntityUidListName = "global::System.Collections.Generic.List<global::Robust.Shared.GameObjects.EntityUid>";
|
||||
private const string GlobalNetEntityUidListName = "global::System.Collections.Generic.List<global::Robust.Shared.GameObjects.NetEntity>";
|
||||
|
||||
private const string GlobalDictionaryName = "global::System.Collections.Generic.Dictionary<TKey, TValue>";
|
||||
private const string GlobalHashSetName = "global::System.Collections.Generic.HashSet<T>";
|
||||
private const string GlobalListName = "global::System.Collections.Generic.List<T>";
|
||||
|
||||
private static string GenerateSource(in GeneratorExecutionContext context, INamedTypeSymbol classSymbol, CSharpCompilation comp, bool raiseAfterAutoHandle)
|
||||
{
|
||||
var nameSpace = classSymbol.ContainingNamespace.ToDisplayString();
|
||||
@@ -39,7 +30,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
var stateName = $"{componentName}_AutoState";
|
||||
|
||||
var members = classSymbol.GetMembers();
|
||||
var fields = new List<(ITypeSymbol Type, string FieldName)>();
|
||||
var fields = new List<(ITypeSymbol Type, string FieldName, AttributeData Attribute)>();
|
||||
var fieldAttr = comp.GetTypeByMetadataName(MemberAttributeName);
|
||||
|
||||
foreach (var mem in members)
|
||||
@@ -56,7 +47,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
switch (mem)
|
||||
{
|
||||
case IFieldSymbol field:
|
||||
fields.Add((field.Type, field.Name));
|
||||
fields.Add((field.Type, field.Name, attribute));
|
||||
break;
|
||||
case IPropertySymbol prop:
|
||||
{
|
||||
@@ -92,7 +83,7 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
continue;
|
||||
}
|
||||
|
||||
fields.Add((prop.Type, prop.Name));
|
||||
fields.Add((prop.Type, prop.Name, attribute));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -130,9 +121,9 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
// component.Count = state.Count;
|
||||
var handleStateSetters = new StringBuilder();
|
||||
|
||||
foreach (var (type, name) in fields)
|
||||
foreach (var (type, name, attribute) in fields)
|
||||
{
|
||||
var typeDisplayStr = type.ToDisplayString(FullyQualifiedFormat);
|
||||
var typeDisplayStr = type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
|
||||
var nullable = type.NullableAnnotation == NullableAnnotation.Annotated;
|
||||
var nullableAnnotation = nullable ? "?" : string.Empty;
|
||||
|
||||
@@ -159,6 +150,31 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
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($@"
|
||||
if (state.{name} != null)
|
||||
component.{name} = new(state.{name});");
|
||||
}
|
||||
else
|
||||
{
|
||||
getStateInit.Append($@"
|
||||
{name} = component.{name},");
|
||||
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = state.{name};");
|
||||
}
|
||||
|
||||
break;
|
||||
case GlobalEntityUidSetName:
|
||||
stateFields.Append($@"
|
||||
@@ -179,33 +195,6 @@ namespace Robust.Shared.CompNetworkGenerator
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = EnsureEntityList<{componentName}>(state.{name}, uid);");
|
||||
|
||||
break;
|
||||
default:
|
||||
stateFields.Append($@"
|
||||
public {typeDisplayStr} {name} = default!;");
|
||||
|
||||
if (IsCloneType(type))
|
||||
{
|
||||
// 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($@"
|
||||
if (state.{name} == null)
|
||||
component.{name} = null;
|
||||
else
|
||||
component.{name} = new(state.{name});");
|
||||
}
|
||||
else
|
||||
{
|
||||
getStateInit.Append($@"
|
||||
{name} = component.{name},");
|
||||
|
||||
handleStateSetters.Append($@"
|
||||
component.{name} = state.{name};");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -364,20 +353,5 @@ public partial class {componentName}
|
||||
}
|
||||
context.RegisterForSyntaxNotifications(() => new NameReferenceSyntaxReceiver());
|
||||
}
|
||||
|
||||
private static bool IsCloneType(ITypeSymbol type)
|
||||
{
|
||||
if (type is not INamedTypeSymbol named || !named.IsGenericType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var constructed = named.ConstructedFrom.ToDisplayString(FullyQualifiedFormat);
|
||||
return constructed switch
|
||||
{
|
||||
GlobalDictionaryName or GlobalHashSetName or GlobalListName => true,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<LangVersion>9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -32,6 +32,20 @@ public sealed class AutoGenerateComponentStateAttribute : Attribute
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class AutoNetworkedFieldAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the data should be wrapped in a new() when setting in get/handlestate
|
||||
/// e.g. for cloning collections like dictionaries or hashsets which is sometimes necessary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should only be true if the type actually has a constructor that takes in itself.
|
||||
/// </remarks>
|
||||
[UsedImplicitly]
|
||||
public bool CloneData;
|
||||
|
||||
public AutoNetworkedFieldAttribute(bool cloneData=false)
|
||||
{
|
||||
CloneData = cloneData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Robust.Shared.Containers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override void OnRemove()
|
||||
protected override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
|
||||
@@ -2,8 +2,11 @@ using System;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Robust.Shared.GameObjects
|
||||
@@ -33,7 +36,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public ComponentLifeStage LifeStage { get; internal set; } = ComponentLifeStage.PreAdd;
|
||||
public ComponentLifeStage LifeStage { get; private set; } = ComponentLifeStage.PreAdd;
|
||||
|
||||
/// <summary>
|
||||
/// If true, and if this is a networked component, then component data will only be sent to players if their
|
||||
@@ -48,6 +51,93 @@ namespace Robust.Shared.GameObjects
|
||||
/// </summary>
|
||||
public virtual bool SessionSpecific => false;
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
|
||||
/// after raising a <see cref="ComponentAdd"/> event.
|
||||
/// </summary>
|
||||
internal void LifeAddToEntity(IEntityManager entManager, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.PreAdd);
|
||||
|
||||
LifeStage = ComponentLifeStage.Adding;
|
||||
CreationTick = entManager.CurrentTick;
|
||||
// networked components are assumed to be dirty when added to entities. See also: ClearTicks()
|
||||
LastModifiedTick = entManager.CurrentTick;
|
||||
entManager.EventBus.RaiseComponentEvent(this, type, CompAddInstance);
|
||||
LifeStage = ComponentLifeStage.Added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
|
||||
/// calling <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
internal void LifeInitialize(IEntityManager entManager, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Added);
|
||||
|
||||
LifeStage = ComponentLifeStage.Initializing;
|
||||
entManager.EventBus.RaiseComponentEvent(this, type, CompInitInstance);
|
||||
LifeStage = ComponentLifeStage.Initialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Initialized" /> to
|
||||
/// <see cref="ComponentLifeStage.Running" />, calling <see cref="Startup" />.
|
||||
/// </summary>
|
||||
internal void LifeStartup(IEntityManager entManager)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Initialized);
|
||||
|
||||
LifeStage = ComponentLifeStage.Starting;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompStartupInstance);
|
||||
LifeStage = ComponentLifeStage.Running;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Running" /> to <see cref="ComponentLifeStage.Stopped" />,
|
||||
/// calling <see cref="Shutdown" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components are allowed to remove themselves in their own Startup function.
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(IEntityManager entManager)
|
||||
{
|
||||
DebugTools.Assert(LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
if (LifeStage <= ComponentLifeStage.Initialized)
|
||||
{
|
||||
// Component was never started, no shutdown logic necessary. Simply mark it as stopped.
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
LifeStage = ComponentLifeStage.Stopping;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompShutdownInstance);
|
||||
LifeStage = ComponentLifeStage.Stopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Stopped" /> to <see cref="ComponentLifeStage.Deleted" />,
|
||||
/// calling <see cref="OnRemove" />.
|
||||
/// </summary>
|
||||
internal void LifeRemoveFromEntity(IEntityManager entManager)
|
||||
{
|
||||
// can be called at any time after PreAdd, including inside other life stage events.
|
||||
DebugTools.Assert(LifeStage != ComponentLifeStage.PreAdd);
|
||||
|
||||
LifeStage = ComponentLifeStage.Removing;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompRemoveInstance);
|
||||
|
||||
OnRemove();
|
||||
|
||||
#if DEBUG
|
||||
if (LifeStage != ComponentLifeStage.Deleted)
|
||||
{
|
||||
DebugTools.Assert($"Component {this.GetType().Name} did not call base {nameof(OnRemove)} in derived method.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public bool Initialized => LifeStage >= ComponentLifeStage.Initializing;
|
||||
@@ -62,18 +152,24 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public GameTick CreationTick { get; internal set; }
|
||||
public GameTick CreationTick { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[ViewVariables]
|
||||
public GameTick LastModifiedTick { get; internal set; }
|
||||
|
||||
private static readonly ComponentAdd CompAddInstance = new();
|
||||
private static readonly ComponentInit CompInitInstance = new();
|
||||
private static readonly ComponentStartup CompStartupInstance = new();
|
||||
private static readonly ComponentShutdown CompShutdownInstance = new();
|
||||
private static readonly ComponentRemove CompRemoveInstance = new();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the component is removed from an entity.
|
||||
/// Shuts down the component.
|
||||
/// The component has already been marked as deleted in the component manager.
|
||||
/// </summary>
|
||||
protected internal virtual void OnRemove()
|
||||
protected virtual void OnRemove()
|
||||
{
|
||||
LifeStage = ComponentLifeStage.Deleted;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace Robust.Shared.GameObjects
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
if (comp is { LifeStage: ComponentLifeStage.Added })
|
||||
LifeInitialize(comp, CompIdx.Index(comp.GetType()));
|
||||
comp.LifeInitialize(this, CompIdx.Index(comp.GetType()));
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@@ -155,20 +155,20 @@ namespace Robust.Shared.GameObjects
|
||||
// Init transform first, we always have it.
|
||||
var transform = GetComponent<TransformComponent>(uid);
|
||||
if (transform.LifeStage == ComponentLifeStage.Initialized)
|
||||
LifeStartup(transform);
|
||||
transform.LifeStartup(this);
|
||||
|
||||
// Init physics second if it exists.
|
||||
if (TryGetComponent<PhysicsComponent>(uid, out var phys)
|
||||
&& phys.LifeStage == ComponentLifeStage.Initialized)
|
||||
{
|
||||
LifeStartup(phys);
|
||||
phys.LifeStartup(this);
|
||||
}
|
||||
|
||||
// Do rest of components.
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
if (comp is { LifeStage: ComponentLifeStage.Initialized })
|
||||
LifeStartup(comp);
|
||||
comp.LifeStartup(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,10 +216,10 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
if (!Comp.Initialized)
|
||||
((EntityManager) _entMan).LifeInitialize(Comp, CompType);
|
||||
Comp.LifeInitialize(_entMan, CompType);
|
||||
|
||||
if (metadata.EntityInitialized && !Comp.Running)
|
||||
((EntityManager) _entMan).LifeStartup(Comp);
|
||||
Comp.LifeStartup(_entMan);
|
||||
}
|
||||
|
||||
public static implicit operator T(CompInitializeHandle<T> handle)
|
||||
@@ -229,7 +229,6 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete]
|
||||
public CompInitializeHandle<T> AddComponentUninitialized<T>(EntityUid uid) where T : Component, new()
|
||||
{
|
||||
var reg = _componentFactory.GetRegistration<T>();
|
||||
@@ -329,7 +328,7 @@ namespace Robust.Shared.GameObjects
|
||||
ComponentAdded?.Invoke(eventArgs);
|
||||
_eventBus.OnComponentAdded(eventArgs);
|
||||
|
||||
LifeAddToEntity(component, reg.Idx);
|
||||
component.LifeAddToEntity(this, reg.Idx);
|
||||
|
||||
if (skipInit)
|
||||
return;
|
||||
@@ -342,10 +341,10 @@ namespace Robust.Shared.GameObjects
|
||||
if (component.Networked)
|
||||
DirtyEntity(uid, metadata);
|
||||
|
||||
LifeInitialize(component, reg.Idx);
|
||||
component.LifeInitialize(this, reg.Idx);
|
||||
|
||||
if (metadata.EntityInitialized)
|
||||
LifeStartup(component);
|
||||
component.LifeStartup(this);
|
||||
|
||||
if (metadata.EntityLifeStage >= EntityLifeStage.MapInitialized)
|
||||
EventBus.RaiseComponentEvent(component, MapInitEventInstance);
|
||||
@@ -516,7 +515,7 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
if (component.LifeStage >= ComponentLifeStage.Initialized && component.LifeStage <= ComponentLifeStage.Running)
|
||||
LifeShutdown(component);
|
||||
component.LifeShutdown(this);
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -548,10 +547,10 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
if (component.Running)
|
||||
LifeShutdown(component);
|
||||
component.LifeShutdown(this);
|
||||
|
||||
if (component.LifeStage != ComponentLifeStage.PreAdd)
|
||||
LifeRemoveFromEntity(component); // Sets delete
|
||||
component.LifeRemoveFromEntity(this); // Sets delete
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
@@ -582,11 +581,11 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
// TODO add options to cancel deferred deletion?
|
||||
_sawmill.Warning($"Found a running component while culling deferred deletions, owner={ToPrettyString(uid)}, type={component.GetType()}");
|
||||
LifeShutdown(component);
|
||||
component.LifeShutdown(this);
|
||||
}
|
||||
|
||||
if (component.LifeStage != ComponentLifeStage.PreAdd)
|
||||
LifeRemoveFromEntity(component);
|
||||
component.LifeRemoveFromEntity(this);
|
||||
|
||||
#if EXCEPTION_TOLERANCE
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
public partial class EntityManager
|
||||
{
|
||||
private static readonly ComponentAdd CompAddInstance = new();
|
||||
private static readonly ComponentInit CompInitInstance = new();
|
||||
private static readonly ComponentStartup CompStartupInstance = new();
|
||||
private static readonly ComponentShutdown CompShutdownInstance = new();
|
||||
private static readonly ComponentRemove CompRemoveInstance = new();
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.PreAdd" /> to <see cref="ComponentLifeStage.Added" />,
|
||||
/// after raising a <see cref="ComponentAdd"/> event.
|
||||
/// </summary>
|
||||
internal void LifeAddToEntity(Component component, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.PreAdd);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Adding;
|
||||
component.CreationTick = CurrentTick;
|
||||
// networked components are assumed to be dirty when added to entities. See also: ClearTicks()
|
||||
component.LastModifiedTick = CurrentTick;
|
||||
EventBus.RaiseComponentEvent(component, type, CompAddInstance);
|
||||
component.LifeStage = ComponentLifeStage.Added;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
|
||||
/// calling <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
internal void LifeInitialize(Component component, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Added);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Initializing;
|
||||
EventBus.RaiseComponentEvent(component, type, CompInitInstance);
|
||||
component.LifeStage = ComponentLifeStage.Initialized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Initialized" /> to
|
||||
/// <see cref="ComponentLifeStage.Running" />, calling <see cref="Startup" />.
|
||||
/// </summary>
|
||||
internal void LifeStartup(Component component)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage == ComponentLifeStage.Initialized);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Starting;
|
||||
EventBus.RaiseComponentEvent(component, CompStartupInstance);
|
||||
component.LifeStage = ComponentLifeStage.Running;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Running" /> to <see cref="ComponentLifeStage.Stopped" />,
|
||||
/// calling <see cref="Shutdown" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Components are allowed to remove themselves in their own Startup function.
|
||||
/// </remarks>
|
||||
internal void LifeShutdown(Component component)
|
||||
{
|
||||
DebugTools.Assert(component.LifeStage is >= ComponentLifeStage.Initializing and < ComponentLifeStage.Stopping);
|
||||
|
||||
if (component.LifeStage <= ComponentLifeStage.Initialized)
|
||||
{
|
||||
// Component was never started, no shutdown logic necessary. Simply mark it as stopped.
|
||||
component.LifeStage = ComponentLifeStage.Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Stopping;
|
||||
EventBus.RaiseComponentEvent(component, CompShutdownInstance);
|
||||
component.LifeStage = ComponentLifeStage.Stopped;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Stopped" /> to <see cref="ComponentLifeStage.Deleted" />,
|
||||
/// calling <see cref="Component.OnRemove" />.
|
||||
/// </summary>
|
||||
internal void LifeRemoveFromEntity(Component component)
|
||||
{
|
||||
// can be called at any time after PreAdd, including inside other life stage events.
|
||||
DebugTools.Assert(component.LifeStage != ComponentLifeStage.PreAdd);
|
||||
|
||||
component.LifeStage = ComponentLifeStage.Removing;
|
||||
EventBus.RaiseComponentEvent(component, CompRemoveInstance);
|
||||
|
||||
component.OnRemove();
|
||||
|
||||
#if DEBUG
|
||||
if (component.LifeStage != ComponentLifeStage.Deleted)
|
||||
{
|
||||
DebugTools.Assert($"Component {component.GetType().Name} did not call base {nameof(component.OnRemove)} in derived method.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public partial class EntityManager
|
||||
return true;
|
||||
}
|
||||
|
||||
entity = null;
|
||||
entity = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public partial class EntityManager
|
||||
{
|
||||
if (nEntity == null)
|
||||
{
|
||||
entity = null;
|
||||
entity = EntityUid.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public partial class EntityManager
|
||||
{
|
||||
if (uid == EntityUid.Invalid)
|
||||
{
|
||||
netEntity = null;
|
||||
netEntity = NetEntity.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public partial class EntityManager
|
||||
{
|
||||
if (uid == null)
|
||||
{
|
||||
netEntity = null;
|
||||
netEntity = NetEntity.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -311,19 +311,19 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
public EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
return CreateEntity(prototypeName, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, ComponentRegistry? overrides = null)
|
||||
{
|
||||
return CreateEntity(prototypeName, out _, overrides);
|
||||
return CreateEntity(prototypeName, overrides);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, EntityCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, overrides);
|
||||
_xforms.SetCoordinates(newEntity, TransformQuery.GetComponent(newEntity), coordinates, unanchor: false);
|
||||
return newEntity;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public virtual EntityUid CreateEntityUninitialized(string? prototypeName, MapCoordinates coordinates, ComponentRegistry? overrides = null)
|
||||
{
|
||||
var newEntity = CreateEntity(prototypeName, out _, overrides);
|
||||
var newEntity = CreateEntity(prototypeName, overrides);
|
||||
var transform = TransformQuery.GetComponent(newEntity);
|
||||
|
||||
if (coordinates.MapId == MapId.Nullspace)
|
||||
@@ -435,13 +435,8 @@ namespace Robust.Shared.GameObjects
|
||||
// Notify all entities they are being terminated prior to detaching & deleting
|
||||
RecursiveFlagEntityTermination(e, meta);
|
||||
|
||||
var xform = TransformQuery.GetComponent(e);
|
||||
TransformComponent? parentXform = null;
|
||||
if (xform.ParentUid.IsValid())
|
||||
TransformQuery.Resolve(xform.ParentUid, ref parentXform);
|
||||
|
||||
// Then actually delete them
|
||||
RecursiveDeleteEntity(e, meta, xform, parentXform);
|
||||
RecursiveDeleteEntity(e, meta);
|
||||
}
|
||||
|
||||
private void RecursiveFlagEntityTermination(
|
||||
@@ -476,14 +471,11 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
private void RecursiveDeleteEntity(
|
||||
EntityUid uid,
|
||||
MetaDataComponent metadata,
|
||||
TransformComponent transform,
|
||||
TransformComponent? parentXform)
|
||||
MetaDataComponent metadata)
|
||||
{
|
||||
DebugTools.Assert(transform.ParentUid.IsValid() == (parentXform != null));
|
||||
DebugTools.Assert(parentXform == null || parentXform.ChildEntities.Contains(uid));
|
||||
|
||||
// Note about this method: #if EXCEPTION_TOLERANCE is not used here because we're gonna it in the future...
|
||||
var netEntity = GetNetEntity(uid, metadata);
|
||||
var transform = TransformQuery.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
|
||||
{
|
||||
_xforms.DetachParentToNull(uid, transform, parentXform);
|
||||
_xforms.DetachParentToNull(uid, transform);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -503,10 +495,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
var childMeta = MetaQuery.GetComponent(child);
|
||||
var childXform = TransformQuery.GetComponent(child);
|
||||
DebugTools.AssertEqual(childXform.ParentUid, uid);
|
||||
RecursiveDeleteEntity(child, childMeta, childXform, transform);
|
||||
RecursiveDeleteEntity(child, MetaQuery.GetComponent(child));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@@ -524,7 +513,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
try
|
||||
{
|
||||
LifeShutdown(component);
|
||||
component.LifeShutdown(this);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -549,7 +538,7 @@ 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(metadata.NetEntity);
|
||||
NetEntityLookup.Remove(netEntity);
|
||||
}
|
||||
|
||||
public virtual void QueueDeleteEntity(EntityUid? uid)
|
||||
@@ -667,23 +656,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, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
private protected virtual EntityUid CreateEntity(string? prototypeName, IEntityLoadContext? context = null)
|
||||
{
|
||||
if (prototypeName == null)
|
||||
return AllocEntity(out metadata);
|
||||
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, out metadata, context);
|
||||
return CreateEntity(prototype, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates an entity and loads components but does not do initialization.
|
||||
/// </summary>
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, out MetaDataComponent metadata, IEntityLoadContext? context = null)
|
||||
private protected EntityUid CreateEntity(EntityPrototype prototype, IEntityLoadContext? context = null)
|
||||
{
|
||||
var entity = AllocEntity(prototype, out metadata);
|
||||
var entity = AllocEntity(prototype, out var metadata);
|
||||
try
|
||||
{
|
||||
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, this, _serManager, context);
|
||||
@@ -751,34 +740,36 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
/// <inheritdoc />
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
public EntityStringRepresentation? ToPrettyString(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
public virtual EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
{
|
||||
return uid == null ? null : ToPrettyString(uid.Value, metadata);
|
||||
}
|
||||
// We want to retrieve the MetaData component even if it is deleted.
|
||||
if (uid == null)
|
||||
return null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null)
|
||||
{
|
||||
if (!MetaQuery.Resolve(uid, ref metadata, false))
|
||||
return new EntityStringRepresentation(uid, true);
|
||||
if (!_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid.Value, out var component))
|
||||
return new EntityStringRepresentation(uid.Value, true);
|
||||
|
||||
return new EntityStringRepresentation(uid, metadata.EntityDeleted, metadata.EntityName, metadata.EntityPrototype?.ID);
|
||||
var metadata = (MetaDataComponent) component;
|
||||
|
||||
return ToPrettyString(uid.Value, metadata);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[return: NotNullIfNotNull("netEntity")]
|
||||
public EntityStringRepresentation? ToPrettyString(NetEntity? netEntity)
|
||||
{
|
||||
return netEntity == null ? null : ToPrettyString(netEntity.Value);
|
||||
return ToPrettyString(GetEntity(netEntity));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
{
|
||||
if (!TryGetEntityData(netEntity, out var uid, out var meta))
|
||||
return new EntityStringRepresentation(EntityUid.Invalid, true);
|
||||
public EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
=> ToPrettyString((EntityUid?) uid).Value;
|
||||
|
||||
return ToPrettyString(uid.Value, meta);
|
||||
public EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
=> ToPrettyString((NetEntity?) netEntity).Value;
|
||||
|
||||
private EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent metadata)
|
||||
{
|
||||
return new EntityStringRepresentation(uid, metadata.EntityDeleted, metadata.EntityName, metadata.EntityPrototype?.ID);
|
||||
}
|
||||
|
||||
#endregion Entity Management
|
||||
|
||||
@@ -399,9 +399,9 @@ public partial class EntitySystem
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
protected EntityStringRepresentation? ToPrettyString(EntityUid? uid, MetaDataComponent? metadata = null)
|
||||
protected EntityStringRepresentation? ToPrettyString(EntityUid? uid)
|
||||
{
|
||||
return EntityManager.ToPrettyString(uid, metadata);
|
||||
return EntityManager.ToPrettyString(uid);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
@@ -412,20 +412,15 @@ public partial class EntitySystem
|
||||
return EntityManager.ToPrettyString(netEntity);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata)
|
||||
=> EntityManager.ToPrettyString(uid, metadata);
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(EntityUid uid)
|
||||
=> EntityManager.ToPrettyString(uid);
|
||||
=> ToPrettyString((EntityUid?) uid).Value;
|
||||
|
||||
/// <inheritdoc cref="IEntityManager.ToPrettyString"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected EntityStringRepresentation ToPrettyString(NetEntity netEntity)
|
||||
=> EntityManager.ToPrettyString(netEntity);
|
||||
=> ToPrettyString((NetEntity?) netEntity).Value;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Robust.Shared.GameObjects
|
||||
var found = EntityManager.TryGetComponent(uid, out component);
|
||||
|
||||
if(logMissing && !found)
|
||||
Log.Error($"Can't resolve \"{typeof(TComp)}\" on entity {ToPrettyString(uid)}!\n{new StackTrace(1, true)}");
|
||||
Log.Error($"Can't resolve \"{typeof(TComp)}\" on entity {uid}!\n{new StackTrace(1, true)}");
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,6 @@ namespace Robust.Shared.GameObjects
|
||||
/// <typeparam name="T">Concrete component type to add.</typeparam>
|
||||
/// <param name="uid">Entity being modified.</param>
|
||||
/// <returns>Component initialization handle. When you are done setting up the component, make sure to dispose this.</returns>
|
||||
[Obsolete]
|
||||
EntityManager.CompInitializeHandle<T> AddComponentUninitialized<T>(EntityUid uid) where T : Component, new();
|
||||
|
||||
/// <summary>
|
||||
@@ -222,7 +221,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="uid">Entity to modify.</param>
|
||||
/// <param name="component">The output component after being ensured.</param>
|
||||
/// <typeparam name="T">Component to add.</typeparam>
|
||||
/// <returns>True if the component already existed</returns>
|
||||
/// <returns>The component in question</returns>
|
||||
bool EnsureComponent<T>(EntityUid uid, out T component) where T : Component, new();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
/// </summary>
|
||||
EntityStringRepresentation ToPrettyString(EntityUid uid, MetaDataComponent? metadata = null);
|
||||
EntityStringRepresentation ToPrettyString(EntityUid uid);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
@@ -138,7 +138,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
/// </summary>
|
||||
[return: NotNullIfNotNull("uid")]
|
||||
EntityStringRepresentation? ToPrettyString(EntityUid? uid, MetaDataComponent? metadata = null);
|
||||
EntityStringRepresentation? ToPrettyString(EntityUid? uid);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of an entity with various information regarding it.
|
||||
|
||||
@@ -38,6 +38,4 @@ public readonly record struct LocId(string Id) : IEquatable<string>, IComparable
|
||||
{
|
||||
return string.Compare(Id, other.Id, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override string ToString() => Id ?? String.Empty;
|
||||
}
|
||||
|
||||
@@ -151,9 +151,7 @@ internal partial class MapManager
|
||||
var fallbackParentEuid = GetMapEntityIdOrThrow(currentMapId);
|
||||
EntityManager.GetComponent<TransformComponent>(gridEnt).AttachParent(fallbackParentEuid);
|
||||
|
||||
var meta = EntityManager.GetComponent<MetaDataComponent>(gridEnt);
|
||||
EntityManager.System<MetaDataSystem>().SetEntityName(gridEnt, $"grid", meta);
|
||||
EntityManager.InitializeComponents(gridEnt, meta);
|
||||
EntityManager.InitializeComponents(gridEnt);
|
||||
EntityManager.StartComponents(gridEnt);
|
||||
return grid;
|
||||
}
|
||||
|
||||
@@ -236,10 +236,8 @@ internal partial class MapManager
|
||||
|
||||
var mapComp = EntityManager.AddComponent<MapComponent>(newEnt);
|
||||
mapComp.MapId = actualId;
|
||||
var meta = EntityManager.GetComponent<MetaDataComponent>(newEnt);
|
||||
EntityManager.System<MetaDataSystem>().SetEntityName(newEnt, $"map {actualId}", meta);
|
||||
EntityManager.Dirty(newEnt, mapComp, meta);
|
||||
EntityManager.InitializeComponents(newEnt, meta);
|
||||
EntityManager.Dirty(mapComp);
|
||||
EntityManager.InitializeComponents(newEnt);
|
||||
EntityManager.StartComponents(newEnt);
|
||||
_sawmill.Debug($"Binding map {actualId} to entity {newEnt}");
|
||||
}
|
||||
|
||||
@@ -39,6 +39,4 @@ public readonly record struct EntProtoId(string Id) : IEquatable<string>, ICompa
|
||||
{
|
||||
return string.Compare(Id, other.Id, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override string ToString() => Id ?? string.Empty;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,4 @@ public readonly record struct ProtoId<T>(string Id) : IEquatable<string>, ICompa
|
||||
{
|
||||
return string.Compare(Id, other.Id, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public override string ToString() => Id ?? string.Empty;
|
||||
}
|
||||
|
||||
@@ -60,18 +60,24 @@ public partial class PrototypeManager
|
||||
if (!TryGetFieldPrototype(field, out var proto, out var canBeNull, out var canBeEmpty))
|
||||
return;
|
||||
|
||||
if (field.FieldType != typeof(string))
|
||||
{
|
||||
errors.Add($"Prototype id field failed validation. Field is not a string. Field: {field.Name} in {type.FullName}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetFieldValue(field, type, ref instance, errors, out var value))
|
||||
return;
|
||||
|
||||
var id = value?.ToString();
|
||||
|
||||
if (id == null)
|
||||
if (value == null)
|
||||
{
|
||||
if (!canBeNull)
|
||||
errors.Add($"Prototype id field failed validation. Fields should not be null. Field: {field.Name} in {type.FullName}");
|
||||
errors.Add($"Prototype id field failed validation. Const/Static fields should not be null. Field: {field.Name} in {type.FullName}");
|
||||
return;
|
||||
}
|
||||
|
||||
var id = (string) value;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(id))
|
||||
{
|
||||
if (!canBeEmpty)
|
||||
@@ -135,6 +141,7 @@ public partial class PrototypeManager
|
||||
canBeNull = false;
|
||||
canBeEmpty = false;
|
||||
|
||||
// Check for a [PrototypeId] attribute.
|
||||
var attrib = field.GetCustomAttribute(typeof(ValidatePrototypeIdAttribute<>), false);
|
||||
if (attrib != null)
|
||||
{
|
||||
@@ -142,39 +149,16 @@ public partial class PrototypeManager
|
||||
return true;
|
||||
}
|
||||
|
||||
// Next, check for a data field attribute.
|
||||
if (!field.TryGetCustomAttribute(out DataFieldAttribute? dataField))
|
||||
return false;
|
||||
|
||||
var fieldType = field.FieldType;
|
||||
canBeEmpty = dataField.Required;
|
||||
DebugTools.Assert(!field.IsStatic);
|
||||
|
||||
// Resolve nullable structs
|
||||
if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
fieldType = fieldType.GetGenericArguments().Single();
|
||||
canBeNull = true;
|
||||
}
|
||||
|
||||
if (fieldType == typeof(EntProtoId))
|
||||
{
|
||||
proto = typeof(EntityPrototype);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fieldType.IsGenericType && field.FieldType.GetGenericTypeDefinition() == typeof(ProtoId<>))
|
||||
{
|
||||
proto = field.FieldType.GetGenericArguments().Single();
|
||||
return true;
|
||||
}
|
||||
|
||||
// As far as I know there is no way to check for the nullability of a string field, so we will assume that null
|
||||
// values imply that the field itself is properly marked as nullable.
|
||||
canBeNull = true;
|
||||
|
||||
if (dataField.CustomTypeSerializer == null)
|
||||
return false;
|
||||
|
||||
// Check that this is a prototype id serializer
|
||||
if (!dataField.CustomTypeSerializer.IsGenericType)
|
||||
return false;
|
||||
|
||||
@@ -182,6 +166,12 @@ public partial class PrototypeManager
|
||||
return false;
|
||||
|
||||
proto = dataField.CustomTypeSerializer.GetGenericArguments().First();
|
||||
canBeEmpty = dataField.Required;
|
||||
|
||||
// We will assume null values imply that the field itself is marked as nullable.
|
||||
// Unless someone can tell me how to figure out the nullability of a string field.
|
||||
canBeNull = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<PackageReference Include="Linguini.Bundle" Version="0.1.3" />
|
||||
<PackageReference Include="SharpZstd.Interop" Version="1.5.2-beta2" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.Sodium" Version="0.2.1" PrivateAssets="compile" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.7" />
|
||||
<PackageReference Include="TerraFX.Interop.Windows" Version="10.0.20348-rc2" PrivateAssets="compile" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,61 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Makes a type always be copied by reference when using it as the generic parameter in
|
||||
/// <see cref="ISerializationManager.CopyTo"/> and <see cref="ISerializationManager.CreateCopy"/>.
|
||||
/// This means that the source instance is returned directly.
|
||||
/// This attribute is not inherited.
|
||||
/// <remarks>
|
||||
/// Note that when calling any of the generic <see cref="ISerializationManager.CopyTo{T}"/> and
|
||||
/// <see cref="ISerializationManager.CreateCopy{T}"/> methods, this attribute will only be respected
|
||||
/// if the generic parameter passed to the copying methods has this attribute.
|
||||
/// For example, if a copy method is called with a generic parameter T that is not annotated with this attribute,
|
||||
/// but the actual type of the source parameter is annotated with this attribute, it will not be copied by ref.
|
||||
/// Conversely, if the generic parameter T is annotated with this attribute, but the actual type of the source
|
||||
/// is an inheritor which is not annotated with this attribute, it will still be copied by ref.
|
||||
/// If the generic parameter T is a type derived from another that is annotated with the attribute,
|
||||
/// but it itself is not annotated with this attribute, source will not be copied by ref as this attribute
|
||||
/// is not inherited.
|
||||
/// <code>
|
||||
/// public class A {}
|
||||
///
|
||||
/// [CopyByRef]
|
||||
/// public class B : A {}
|
||||
///
|
||||
/// public class C : B {}
|
||||
///
|
||||
/// public class Copier(ISerializationManager manager)
|
||||
/// {
|
||||
/// var a = new A();
|
||||
/// var b = new B();
|
||||
/// var c = new C();
|
||||
///
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy(a) == a
|
||||
///
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy<A>(b) == b
|
||||
///
|
||||
/// // true, copied by ref
|
||||
/// manager.CreateCopy(b) == b
|
||||
///
|
||||
/// // false, not copied by ref
|
||||
/// manager.CreateCopy(c) == c
|
||||
///
|
||||
/// // true, copied by ref
|
||||
/// manager.CreateCopy<B>(c) == c
|
||||
/// }
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Class |
|
||||
AttributeTargets.Struct |
|
||||
AttributeTargets.Enum |
|
||||
AttributeTargets.Interface,
|
||||
Inherited = false)]
|
||||
public sealed class CopyByRefAttribute : Attribute
|
||||
namespace Robust.Shared.Serialization.Manager.Attributes
|
||||
{
|
||||
[AttributeUsage(
|
||||
AttributeTargets.Class |
|
||||
AttributeTargets.Struct |
|
||||
AttributeTargets.Enum |
|
||||
AttributeTargets.Interface,
|
||||
Inherited = false)]
|
||||
public sealed class CopyByRefAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.Markdown;
|
||||
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||
using Robust.Shared.Serialization.Markdown.Validation;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||
|
||||
[TypeSerializer]
|
||||
public sealed class SortedSetSerializer<T> :
|
||||
ITypeSerializer<SortedSet<T>, SequenceDataNode>,
|
||||
ITypeCopyCreator<SortedSet<T>>
|
||||
{
|
||||
SortedSet<T> ITypeReader<SortedSet<T>, SequenceDataNode>.Read(ISerializationManager serializationManager,
|
||||
SequenceDataNode node,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context,
|
||||
ISerializationManager.InstantiationDelegate<SortedSet<T>>? instanceProvider)
|
||||
{
|
||||
var set = instanceProvider != null ? instanceProvider() : new SortedSet<T>();
|
||||
|
||||
foreach (var dataNode in node.Sequence)
|
||||
{
|
||||
set.Add(serializationManager.Read<T>(dataNode, hookCtx, context));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
ValidationNode ITypeValidator<SortedSet<T>, SequenceDataNode>.Validate(ISerializationManager serializationManager,
|
||||
SequenceDataNode node, IDependencyCollection dependencies, ISerializationContext? context)
|
||||
{
|
||||
var list = new List<ValidationNode>();
|
||||
foreach (var elem in node.Sequence)
|
||||
{
|
||||
list.Add(serializationManager.ValidateNode<T>(elem, context));
|
||||
}
|
||||
|
||||
return new ValidatedSequenceNode(list);
|
||||
}
|
||||
|
||||
public DataNode Write(ISerializationManager serializationManager, SortedSet<T> value,
|
||||
IDependencyCollection dependencies, bool alwaysWrite = false,
|
||||
ISerializationContext? context = null)
|
||||
{
|
||||
var sequence = new SequenceDataNode();
|
||||
|
||||
foreach (var elem in value)
|
||||
{
|
||||
sequence.Add(serializationManager.WriteValue(elem, alwaysWrite, context));
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
SortedSet<T> ITypeCopyCreator<SortedSet<T>>.CreateCopy(ISerializationManager serializationManager, SortedSet<T> source,
|
||||
IDependencyCollection dependencies,
|
||||
SerializationHookContext hookCtx,
|
||||
ISerializationContext? context)
|
||||
{
|
||||
var target = new SortedSet<T>();
|
||||
|
||||
foreach (var val in source)
|
||||
{
|
||||
target.Add(serializationManager.CreateCopy(val, hookCtx, context));
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Robust.Shared.Spawners;
|
||||
|
||||
public abstract class SharedTimedDespawnSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
UpdatesOutsidePrediction = true;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
// Client both needs to predict this, but also can't properly handle prediction resetting.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
var query = EntityQueryEnumerator<TimedDespawnComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
comp.Lifetime -= frameTime;
|
||||
|
||||
if (!CanDelete(uid))
|
||||
continue;
|
||||
|
||||
if (comp.Lifetime <= 0)
|
||||
{
|
||||
var ev = new TimedDespawnEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
QueueDel(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool CanDelete(EntityUid uid);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Robust.Shared.Spawners;
|
||||
|
||||
/// <summary>
|
||||
/// Put this component on something you would like to despawn after a certain amount of time
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// NOT networked as we don't want clients predicting networked entity deletions.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TimedDespawnComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// How long the entity will exist before despawning
|
||||
/// </summary>
|
||||
[DataField("lifetime")]
|
||||
public float Lifetime = 5f;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Robust.Shared.Spawners;
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on an entity when its timed despawn is over.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct TimedDespawnEvent;
|
||||
@@ -88,16 +88,13 @@ namespace Robust.Shared.Utility
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
[AssertionMethod]
|
||||
public static void AssertOwner(EntityUid? uid, IComponent? component)
|
||||
public static void AssertOwner(EntityUid? uid, IComponent component)
|
||||
{
|
||||
if (component == null)
|
||||
return;
|
||||
|
||||
if (uid == null)
|
||||
throw new DebugAssertException($"Null entity uid cannot own a component. Component: {component.GetType().Name}");
|
||||
|
||||
// Whenever .owner is removed this will need to be replaced by something.
|
||||
// As long as components are just reference types, we could just get the component and check if the references are equal?
|
||||
// We need some way to ensure that people don't mix up uids & components when calling methods.
|
||||
if (component.Owner != uid)
|
||||
throw new DebugAssertException($"Entity {uid} is not the owner of the component. Component: {component.GetType().Name}");
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ using Robust.Server.ServerStatus;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Asynchronous;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.ContentPack;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Input;
|
||||
@@ -270,7 +269,6 @@ namespace Robust.UnitTesting
|
||||
public ISharedPlayerManager PlayerMan { get; private set; } = default!;
|
||||
public IGameTiming Timing { get; private set; } = default!;
|
||||
public IMapManager MapMan { get; private set; } = default!;
|
||||
public IConsoleHost ConsoleHost { get; private set; } = default!;
|
||||
|
||||
protected virtual void ResolveIoC(IDependencyCollection deps)
|
||||
{
|
||||
@@ -280,7 +278,6 @@ namespace Robust.UnitTesting
|
||||
PlayerMan = deps.Resolve<ISharedPlayerManager>();
|
||||
Timing = deps.Resolve<IGameTiming>();
|
||||
MapMan = deps.Resolve<IMapManager>();
|
||||
ConsoleHost = deps.Resolve<IConsoleHost>();
|
||||
}
|
||||
|
||||
public T System<T>() where T : IEntitySystem
|
||||
@@ -298,11 +295,6 @@ namespace Robust.UnitTesting
|
||||
return EntMan.GetComponent<MetaDataComponent>(uid);
|
||||
}
|
||||
|
||||
public async Task ExecuteCommand(string cmd)
|
||||
{
|
||||
await WaitPost(() => ConsoleHost.ExecuteCommand(cmd));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the instance is still alive.
|
||||
/// "Alive" indicates that it is able to receive and process commands.
|
||||
|
||||
@@ -128,7 +128,6 @@ namespace Robust.UnitTesting
|
||||
|
||||
// Required components for the engine to work
|
||||
// Why are we still here? Just to suffer? Why can't we just use [RegisterComponent] magic?
|
||||
// TODO End Suffering.
|
||||
var compFactory = deps.Resolve<IComponentFactory>();
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(EyeComponent)))
|
||||
@@ -226,11 +225,6 @@ namespace Robust.UnitTesting
|
||||
compFactory.RegisterClass<Gravity2DComponent>();
|
||||
}
|
||||
|
||||
if (!compFactory.AllRegisteredTypes.Contains(typeof(ActorComponent)))
|
||||
{
|
||||
compFactory.RegisterClass<ActorComponent>();
|
||||
}
|
||||
|
||||
// So by default EntityManager does its own EntitySystemManager initialize during Startup.
|
||||
// We want to bypass this and load our own systems hence we will manually initialize it here.
|
||||
entMan.Initialize();
|
||||
|
||||
@@ -61,6 +61,7 @@ entities:
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
compFactory.RegisterClass<MapDeserializeTestComponent>();
|
||||
compFactory.RegisterClass<VisibilityComponent>();
|
||||
compFactory.RegisterClass<ActorComponent>();
|
||||
compFactory.RegisterClass<IgnoreUIRangeComponent>();
|
||||
compFactory.GenerateNetIds();
|
||||
IoCManager.Resolve<ISerializationManager>().Initialize();
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||
using Robust.Shared.Serialization.Markdown.Value;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic;
|
||||
|
||||
// ReSharper disable AccessToStaticMemberViaDerivedType
|
||||
|
||||
namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers;
|
||||
|
||||
[TestFixture]
|
||||
[TestOf(typeof(SortedSetSerializer<>))]
|
||||
public sealed class SortedSetSerializerTest : SerializationTest
|
||||
{
|
||||
[Test]
|
||||
public void SerializationTest()
|
||||
{
|
||||
var list = new SortedSet<string> {"A", "B", "C"};
|
||||
var node = Serialization.WriteValueAs<SequenceDataNode>(list);
|
||||
|
||||
Assert.That(node.Cast<ValueDataNode>(0).Value, Is.EqualTo("A"));
|
||||
Assert.That(node.Cast<ValueDataNode>(1).Value, Is.EqualTo("B"));
|
||||
Assert.That(node.Cast<ValueDataNode>(2).Value, Is.EqualTo("C"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializationTest()
|
||||
{
|
||||
var list = new SortedSet<string> {"A", "B", "C"};
|
||||
var node = new SequenceDataNode("A", "B", "C");
|
||||
var deserializedList = Serialization.Read<SortedSet<string>>(node, notNullableOverride: true);
|
||||
|
||||
Assert.That(deserializedList, Is.EqualTo(list));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user