Remove Static Component NetIds (#1842)

* ComponentNames are not sent over the network when components are created.

* Removed ComponentStates array from EntityState, now the state is stored directly inside the CompChange struct.

* Remove the unnecessary NetID property from ComponentState.

* Remove Component.NetworkSynchronizeExistence.

* Change GetNetComponents to return both the component and the component NetId.

* Remove public usages of the Component.NetID property.

* Adds the NetIDAttribute that can be applied to components.

* Removed Component.NetID.

* Revert changes to GetComponentState and how prediction works.

* Adds component netID automatic generation.

* Modifies ClientConsoleHost so that commands can be called before Initialize().

* Completely remove static NetIds.

* Renamed NetIDAttribute to NetworkedComponentAttribute.

* Fixing unit tests.
This commit is contained in:
Acruid
2021-07-12 01:23:13 -07:00
committed by GitHub
parent baef2bc7f8
commit dadd7b4cc3
69 changed files with 450 additions and 328 deletions

View File

@@ -46,13 +46,15 @@ namespace Robust.Client
IoCManager.Register<IClientMapManager, ClientMapManager>();
IoCManager.Register<IEntityManager, ClientEntityManager>();
IoCManager.Register<IEntityLookup, EntityLookup>();
IoCManager.Register<IReflectionManager, ClientReflectionManager>();
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
IoCManager.Register<IComponentFactory, ClientComponentFactory>();
IoCManager.Register<ITileDefinitionManager, ClydeTileDefinitionManager>();
IoCManager.Register<IClydeTileDefinitionManager, ClydeTileDefinitionManager>();
IoCManager.Register<GameController, GameController>();
IoCManager.Register<IGameController, GameController>();
IoCManager.Register<IGameControllerInternal, GameController>();
IoCManager.Register<IReflectionManager, ClientReflectionManager>();
IoCManager.Register<IResourceManager, ResourceCache>();
IoCManager.Register<IResourceManagerInternal, ResourceCache>();
IoCManager.Register<IResourceCache, ResourceCache>();
@@ -72,8 +74,6 @@ namespace Robust.Client
IoCManager.Register<IDebugDrawingManager, DebugDrawingManager>();
IoCManager.Register<ILightManager, LightManager>();
IoCManager.Register<IDiscordRichPresence, DiscordRichPresence>();
IoCManager.Register<IClientConsoleHost, ClientConsoleHost>();
IoCManager.Register<IConsoleHost, ClientConsoleHost>();
IoCManager.Register<IMidiManager, MidiManager>();
IoCManager.Register<IAuthManager, AuthManager>();
switch (mode)

View File

@@ -49,7 +49,11 @@ namespace Robust.Client.Console
NetManager.RegisterNetMessage<MsgConCmdAck>(HandleConCmdAck);
NetManager.RegisterNetMessage<MsgConCmd>(ProcessCommand);
Reset();
_requestedCommands = false;
NetManager.Connected += OnNetworkConnected;
LoadConsoleCommands();
SendServerCommandRequest();
LogManager.RootSawmill.AddHandler(new DebugConsoleLogHandler(this));
}
@@ -61,17 +65,6 @@ namespace Robust.Client.Console
ExecuteCommand(null, text);
}
/// <inheritdoc />
public void Reset()
{
AvailableCommands.Clear();
_requestedCommands = false;
NetManager.Connected += OnNetworkConnected;
LoadConsoleCommands();
SendServerCommandRequest();
}
/// <inheritdoc />
public event EventHandler<AddStringArgs>? AddString;
@@ -97,7 +90,7 @@ namespace Robust.Client.Console
return;
// echo the command locally
WriteError(null, "> " + command);
WriteLine(null, "> " + command);
//Commands are processed locally and then sent to the server to be processed there again.
var args = new List<string>();
@@ -142,6 +135,9 @@ namespace Robust.Client.Console
private void OutputText(string text, bool local, bool error)
{
AddString?.Invoke(this, new AddStringArgs(text, local, error));
var level = error ? LogLevel.Warning : LogLevel.Info;
Logger.LogS(level, "CON", text);
}
private void OnNetworkConnected(object? sender, NetChannelArgs netChannelArgs)

View File

@@ -77,7 +77,7 @@ namespace Robust.Client.Console.Commands
message.Append($"net ID: {registration.NetID}");
}
message.Append($", NSE: {registration.NetworkSynchronizeExistence}, references:");
message.Append($", References:");
shell.WriteLine(message.ToString());

View File

@@ -11,11 +11,6 @@ namespace Robust.Client.Console
/// </summary>
void Initialize();
/// <summary>
/// Resets the console to a post-initialized state.
/// </summary>
void Reset();
event EventHandler<AddStringArgs> AddString;
event EventHandler<AddFormattedMessageArgs> AddFormatted;

View File

@@ -1,12 +1,15 @@
using Robust.Shared.Console;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
namespace Robust.Client.GameObjects
{
public class ClientComponentFactory : ComponentFactory
internal class ClientComponentFactory : ComponentFactory
{
public ClientComponentFactory()
public ClientComponentFactory(IDynamicTypeFactoryInternal typeFactory, IReflectionManager reflectionManager, IConsoleHost conHost)
: base(typeFactory, reflectionManager, conHost)
{
// Required for the engine to work
RegisterIgnore("KeyBindingInput");

View File

@@ -109,13 +109,15 @@ namespace Robust.Client.GameObjects
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public void SendComponentNetworkMessage(INetChannel? channel, IEntity entity, IComponent component, ComponentMessage message)
{
if (!component.NetID.HasValue)
var netId = ComponentFactory.GetRegistration(component.GetType()).NetID;
if (!netId.HasValue)
throw new ArgumentException($"Component {component.Name} does not have a NetID.", nameof(component));
var msg = _networkManager.CreateNetMessage<MsgEntity>();
msg.Type = EntityMessageType.ComponentMessage;
msg.EntityUid = entity.Uid;
msg.NetId = component.NetID.Value;
msg.NetId = netId.Value;
msg.ComponentMessage = message;
msg.SourceTick = _gameTiming.CurTick;

View File

@@ -5,6 +5,7 @@ using Robust.Client.ResourceManagement;
using Robust.Shared.Animations;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -17,12 +18,12 @@ namespace Robust.Client.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IPointLightComponent))]
[NetworkedComponent()]
public class PointLightComponent : Component, IPointLightComponent, ISerializationHooks
{
[Dependency] private readonly IResourceCache _resourceCache = default!;
public override string Name => "PointLight";
public override uint? NetID => NetIDs.POINT_LIGHT;
internal bool TreeUpdateQueued { get; set; }

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Input;
using Robust.Client.Map;
@@ -25,6 +26,7 @@ using Robust.Shared.Utility;
namespace Robust.Client.GameStates
{
/// <inheritdoc />
[UsedImplicitly]
public class ClientGameStateManager : IClientGameStateManager
{
private GameStateProcessor _processor = default!;
@@ -36,6 +38,8 @@ namespace Robust.Client.GameStates
_pendingSystemMessages
= new();
private uint _metaCompNetId;
[Dependency] private readonly IComponentFactory _compFactory = default!;
[Dependency] private readonly IClientEntityManagerInternal _entities = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
@@ -99,6 +103,12 @@ namespace Robust.Client.GameStates
Predicting = _config.GetCVar(CVars.NetPredict);
PredictTickBias = _config.GetCVar(CVars.NetPredictTickBias);
PredictLagBias = _config.GetCVar(CVars.NetPredictLagBias);
var metaId = _compFactory.GetRegistration(typeof(MetaDataComponent)).NetID;
if (!metaId.HasValue)
throw new InvalidOperationException("MetaDataComponent does not have a NetId.");
_metaCompNetId = metaId.Value;
}
/// <inheritdoc />
@@ -343,11 +353,11 @@ namespace Robust.Client.GameStates
}
// TODO: handle component deletions/creations.
foreach (var comp in _componentManager.GetNetComponents(entity.Uid))
foreach (var (netId, comp) in _componentManager.GetNetComponents(entity.Uid))
{
DebugTools.AssertNotNull(comp.NetID);
DebugTools.AssertNotNull(netId);
if (comp.LastModifiedTick < curTick || !last.TryGetValue(comp.NetID!.Value, out var compState))
if (comp.LastModifiedTick < curTick || !last.TryGetValue(netId, out var compState))
{
continue;
}
@@ -368,24 +378,22 @@ namespace Robust.Client.GameStates
// so that we can later roll back to it (if necessary).
var outputData = new Dictionary<EntityUid, Dictionary<uint, ComponentState>>();
Debug.Assert(_players.LocalPlayer != null, "_players.LocalPlayer != null");
var player = _players.LocalPlayer.Session;
foreach (var createdEntity in createdEntities)
{
var compData = new Dictionary<uint, ComponentState>();
outputData.Add(createdEntity, compData);
foreach (var component in _componentManager.GetNetComponents(createdEntity))
foreach (var (netId, component) in _componentManager.GetNetComponents(createdEntity))
{
Debug.Assert(_players.LocalPlayer != null, "_players.LocalPlayer != null");
var player = _players.LocalPlayer.Session;
var state = component.GetComponentState(player);
if (state.GetType() == typeof(ComponentState))
{
if(state.GetType() == typeof(ComponentState))
continue;
}
compData.Add(state.NetID, state);
compData.Add(netId, state);
}
}
@@ -432,8 +440,7 @@ namespace Robust.Client.GameStates
}
else //Unknown entities
{
var metaState = (MetaDataComponentState?) es.ComponentStates
?.FirstOrDefault(c => c.NetID == NetIDs.META_DATA);
var metaState = (MetaDataComponentState?) es.ComponentChanges?.FirstOrDefault(c => c.NetID == _metaCompNetId).State;
if (metaState == null)
{
throw new InvalidOperationException($"Server sent new entity state for {es.Uid} without metadata component!");
@@ -543,7 +550,7 @@ namespace Robust.Client.GameStates
private void HandleEntityState(IComponentManager compMan, IEntity entity, EntityEventBus bus, EntityState? curState,
EntityState? nextState)
{
var compStateWork = new Dictionary<uint, (ComponentState? curState, ComponentState? nextState)>();
var compStateWork = new Dictionary<ushort, (ComponentState? curState, ComponentState? nextState)>();
var entityUid = entity.Uid;
if (curState?.ComponentChanges != null)
@@ -559,42 +566,46 @@ namespace Robust.Client.GameStates
}
else
{
//Right now we just assume every state from an unseen entity is added
if (compMan.HasComponent(entityUid, compChange.NetID))
continue;
var newComp = (Component) _compFactory.GetComponent(compChange.ComponentName!);
var newComp = (Component) _compFactory.GetComponent(compChange.NetID);
newComp.Owner = entity;
compMan.AddComponent(entity, newComp, true);
compStateWork[compChange.NetID] = (compChange.State, null);
}
}
}
if (curState?.ComponentStates != null)
if (curState?.ComponentChanges != null)
{
foreach (var compState in curState.ComponentStates)
foreach (var compChange in curState.ComponentChanges)
{
compStateWork[compState.NetID] = (compState, null);
compStateWork[compChange.NetID] = (compChange.State, null);
}
}
if (nextState?.ComponentStates != null)
if (nextState?.ComponentChanges != null)
{
foreach (var compState in nextState.ComponentStates)
foreach (var compState in nextState.ComponentChanges)
{
if (compStateWork.TryGetValue(compState.NetID, out var state))
{
compStateWork[compState.NetID] = (state.curState, compState);
compStateWork[compState.NetID] = (state.curState, compState.State);
}
else
{
compStateWork[compState.NetID] = (null, compState);
compStateWork[compState.NetID] = (null, compState.State);
}
}
}
foreach (var (netId, (cur, next)) in compStateWork)
{
if (compMan.TryGetComponent(entityUid, netId, out var component))
if (compMan.TryGetComponent(entityUid, (ushort) netId, out var component))
{
try
{

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.GameObjects;
@@ -195,14 +195,10 @@ namespace Robust.Client.GameStates
{
compData.Remove(change.NetID);
}
}
}
if (entityState.ComponentStates != null)
{
foreach (var compState in entityState.ComponentStates)
{
compData[compState.NetID] = compState;
else if (change.State is not null)
{
compData[change.NetID] = change.State;
}
}
}
}

View File

@@ -21,6 +21,7 @@ namespace Robust.Client.GameStates
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IClientNetManager _netManager = default!;
[Dependency] private readonly IClientGameStateManager _gameStateManager = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;
private const int HistorySize = 60 * 3; // number of ticks to keep in history.
private const int TargetPayloadBps = 56000 / 8; // Target Payload size in Bytes per second. A mind-numbing fifty-six thousand bits per second, who would ever need more?
@@ -90,17 +91,14 @@ namespace Robust.Client.GameStates
sb.Append($"\n Changes:");
foreach (var compChange in entState.ComponentChanges)
{
var del = compChange.Deleted ? 'D' : 'C';
sb.Append($"\n [{del}]{compChange.NetID}:{compChange.ComponentName}");
}
}
var registration = _componentFactory.GetRegistration(compChange.NetID);
var create = compChange.Created ? 'C' : '\0';
var mod = !(compChange.Created || compChange.Created) ? 'M' : '\0';
var del = compChange.Deleted ? 'D' : '\0';
sb.Append($"\n [{create}{mod}{del}]{compChange.NetID}:{registration.Name}");
if (entState.ComponentStates is not null)
{
sb.Append($"\n States:");
foreach (var compState in entState.ComponentStates)
{
sb.Append($"\n {compState.NetID}:{compState.GetType().Name}");
if(compChange.State is not null)
sb.Append($"\n STATE:{compChange.State.GetType().Name}");
}
}
}

View File

@@ -32,12 +32,12 @@ namespace Robust.Client.Map
//get shared euid of map comp entity
foreach (var entityState in entityStates!)
{
if(entityState.ComponentStates is null)
if(entityState.ComponentChanges is null)
continue;
foreach (var compState in entityState.ComponentStates)
foreach (var compChange in entityState.ComponentChanges)
{
if (compState is not MapComponentState mapCompState || mapCompState.MapId != mapId)
if (compChange.State is not MapComponentState mapCompState || mapCompState.MapId != mapId)
continue;
mapEuid = entityState.Uid;
@@ -67,12 +67,12 @@ namespace Robust.Client.Map
//get shared euid of map comp entity
foreach (var entityState in entityStates!)
{
if(entityState.ComponentStates is null)
if (entityState.ComponentChanges is null)
continue;
foreach (var compState in entityState.ComponentStates)
foreach (var compState in entityState.ComponentChanges)
{
if (compState is not MapGridComponentState gridCompState || gridCompState.GridIndex != gridId)
if (compState.State is not MapGridComponentState gridCompState || gridCompState.GridIndex != gridId)
continue;
gridEuid = entityState.Uid;

View File

@@ -10,7 +10,7 @@ namespace Robust.Client.UserInterface.Controls
{
public Label Label { get; }
public Button() : base()
public Button()
{
AddStyleClass(StyleClassButton);
Label = new Label

View File

@@ -14,7 +14,7 @@ namespace Robust.Client.UserInterface.Controls
public Label Label { get; }
public TextureRect TextureRect { get; }
public CheckBox() : base()
public CheckBox()
{
ToggleMode = true;

View File

@@ -13,7 +13,7 @@ namespace Robust.Client.UserInterface.Controls
public const string StylePseudoClassHover = "hover";
public const string StylePseudoClassDisabled = "disabled";
public ContainerButton() : base()
public ContainerButton()
{
DrawModeChanged();
}

View File

@@ -52,7 +52,7 @@ namespace Robust.Client.UserInterface.Controls
public event EventHandler<ValueChangedEventArgs>? ValueChanged;
public SpinBox() : base()
public SpinBox()
{
MouseFilter = MouseFilterMode.Pass;

View File

@@ -349,6 +349,16 @@ namespace Robust.Server
_entityManager.Startup();
IoCManager.Resolve<IEntityLookup>().Startup();
_stateManager.Initialize();
// sometime after content init
{
var reg = _entityManager.ComponentFactory.GetRegistration<TransformComponent>();
if (!reg.NetID.HasValue)
throw new InvalidOperationException("TransformComponent does not have a NetId.");
_stateManager.SetTransformNetId(reg.NetID.Value);
}
_scriptHost.Initialize();
_modLoader.BroadcastRunLevel(ModRunLevel.PostInit);

View File

@@ -1,4 +1,5 @@
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Players;

View File

@@ -1,4 +1,5 @@
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Players;
@@ -10,6 +11,7 @@ namespace Robust.Server.GameObjects
{
[RegisterComponent]
[ComponentReference(typeof(IPointLightComponent))]
[NetworkedComponent()]
public class PointLightComponent : Component, IPointLightComponent
{
[DataField("color")]
@@ -22,7 +24,6 @@ namespace Robust.Server.GameObjects
private Vector2 _offset = Vector2.Zero;
public override string Name => "PointLight";
public override uint? NetID => NetIDs.POINT_LIGHT;
[ViewVariables(VVAccess.ReadWrite)]
public Color Color

View File

@@ -1,11 +1,15 @@
using Robust.Shared.Console;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
namespace Robust.Server.GameObjects
{
public class ServerComponentFactory : ComponentFactory
internal class ServerComponentFactory : ComponentFactory
{
public ServerComponentFactory()
public ServerComponentFactory(IDynamicTypeFactoryInternal typeFactory, IReflectionManager reflectionManager, IConsoleHost conHost)
: base(typeFactory, reflectionManager, conHost)
{
RegisterIgnore("Input");
RegisterIgnore("AnimationPlayer");

View File

@@ -75,7 +75,7 @@ namespace Robust.Server.GameObjects
// As such, we can reset the modified ticks to Zero,
// which indicates "not different from client's own deserialization".
// So the initial data for the component or even the creation doesn't have to be sent over the wire.
foreach (var component in ComponentManager.GetNetComponents(entity.Uid))
foreach (var (netId, component) in ComponentManager.GetNetComponents(entity.Uid))
{
// Make sure to ONLY get components that are defined in the prototype.
// Others could be instantiated directly by AddComponent (e.g. ContainerManager).
@@ -143,13 +143,15 @@ namespace Robust.Server.GameObjects
if (_networkManager.IsClient)
return;
if (!component.NetID.HasValue)
var netId = ComponentFactory.GetRegistration(component.GetType()).NetID;
if (!netId.HasValue)
throw new ArgumentException($"Component {component.Name} does not have a NetID.", nameof(component));
var msg = _networkManager.CreateNetMessage<MsgEntity>();
msg.Type = EntityMessageType.ComponentMessage;
msg.EntityUid = entity.Uid;
msg.NetId = component.NetID.Value;
msg.NetId = netId.Value;
msg.ComponentMessage = message;
msg.SourceTick = _gameTiming.CurTick;

View File

@@ -39,6 +39,8 @@ namespace Robust.Server.GameStates
private readonly ObjectPool<HashSet<EntityUid>> _viewerEntsPool
= new DefaultObjectPool<HashSet<EntityUid>>(new DefaultPooledObjectPolicy<HashSet<EntityUid>>(), MaxVisPoolSize);
private ushort _transformNetId = 0;
/// <summary>
/// Is view culling enabled, or will we send the whole map?
/// </summary>
@@ -58,6 +60,11 @@ namespace Robust.Server.GameStates
_lookup = lookup;
}
public void SetTransformNetId(ushort value)
{
_transformNetId = value;
}
// Not thread safe
public void EntityDeleted(EntityUid e)
{
@@ -204,17 +211,14 @@ namespace Robust.Server.GameStates
var oldState = (TransformComponent.TransformComponentState) xform.GetComponentState(session);
entityStates.Add(new EntityState(entityUid,
new ComponentChanged[]
new[]
{
new(false, NetIDs.TRANSFORM, "Transform")
},
new ComponentState[]
{
new TransformComponent.TransformComponentState(Vector2NaN,
oldState.Rotation,
oldState.ParentID,
oldState.NoLocalRotation,
oldState.Anchored)
ComponentChange.Changed(_transformNetId,
new TransformComponent.TransformComponentState(Vector2NaN,
oldState.Rotation,
oldState.ParentID,
oldState.NoLocalRotation,
oldState.Anchored)),
}));
}

View File

@@ -17,5 +17,6 @@ namespace Robust.Server.GameStates
bool PvsEnabled { get; set; }
float PvsRange { get; set; }
void SetTransformNetId(ushort netId);
}
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Map;
using Robust.Server.Player;
@@ -21,6 +22,7 @@ using Robust.Shared.Utility;
namespace Robust.Server.GameStates
{
/// <inheritdoc cref="IServerGameStateManager"/>
[UsedImplicitly]
public class ServerGameStateManager : IServerGameStateManager, IPostInjectInit
{
// Mapping of net UID of clients -> last known acked state.
@@ -53,6 +55,11 @@ namespace Robust.Server.GameStates
set => _configurationManager.SetCVar(CVars.NetMaxUpdateRange, value);
}
public void SetTransformNetId(ushort netId)
{
_entityView.SetTransformNetId(netId);
}
public void PostInject()
{
_logger = Logger.GetSawmill("PVS");
@@ -249,38 +256,44 @@ namespace Robust.Server.GameStates
/// <returns>New entity State for the given entity.</returns>
internal static EntityState GetEntityState(IComponentManager compMan, ICommonSession player, EntityUid entityUid, GameTick fromTick)
{
var compStates = new List<ComponentState>();
var changed = new List<ComponentChanged>();
var changed = new List<ComponentChange>();
foreach (var comp in compMan.GetNetComponents(entityUid))
foreach (var (netId, component) in compMan.GetNetComponents(entityUid))
{
DebugTools.Assert(comp.Initialized);
DebugTools.Assert(component.Initialized);
// NOTE: When LastModifiedTick or CreationTick are 0 it means that the relevant data is
// "not different from entity creation".
// i.e. when the client spawns the entity and loads the entity prototype,
// the data it deserializes from the prototype SHOULD be equal
// to what the component state / ComponentChanged would send.
// to what the component state / ComponentChange would send.
// As such, we can avoid sending this data in this case since the client "already has it".
if (comp.NetSyncEnabled && comp.LastModifiedTick != GameTick.Zero && comp.LastModifiedTick >= fromTick)
compStates.Add(comp.GetComponentState(player));
DebugTools.Assert(component.LastModifiedTick >= component.CreationTick);
if (comp.CreationTick != GameTick.Zero && comp.CreationTick >= fromTick && !comp.Deleted)
if (component.CreationTick != GameTick.Zero && component.CreationTick >= fromTick && !component.Deleted)
{
ComponentState? state = null;
if (component.NetSyncEnabled && component.LastModifiedTick != GameTick.Zero && component.LastModifiedTick >= fromTick)
state = component.GetComponentState(player);
// Can't be null since it's returned by GetNetComponents
// ReSharper disable once PossibleInvalidOperationException
changed.Add(ComponentChanged.Added(comp.NetID!.Value, comp.Name));
changed.Add(ComponentChange.Added(netId, state));
}
else if (comp.Deleted && comp.LastModifiedTick >= fromTick)
else if (component.NetSyncEnabled && component.LastModifiedTick != GameTick.Zero && component.LastModifiedTick >= fromTick)
{
changed.Add(ComponentChange.Changed(netId, component.GetComponentState(player)));
}
else if (component.Deleted && component.LastModifiedTick >= fromTick)
{
// Can't be null since it's returned by GetNetComponents
// ReSharper disable once PossibleInvalidOperationException
changed.Add(ComponentChanged.Removed(comp.NetID!.Value));
changed.Add(ComponentChange.Removed(netId));
}
}
return new EntityState(entityUid, changed.ToArray(), compStates.ToArray());
return new EntityState(entityUid, changed.ToArray());
}
/// <summary>

View File

@@ -409,7 +409,7 @@ namespace Robust.Server.Maps
continue;
}
foreach (var component in _componentManager.GetNetComponents(entity.Uid))
foreach (var (netId, component) in _componentManager.GetNetComponents(entity.Uid))
{
var castComp = (Component) component;

View File

@@ -39,10 +39,11 @@ namespace Robust.Server
IoCManager.Register<IBaseServerInternal, BaseServer>();
IoCManager.Register<BaseServer, BaseServer>();
IoCManager.Register<IGameTiming, GameTiming>();
IoCManager.Register<IReflectionManager, ServerReflectionManager>();
IoCManager.Register<IConsoleHost, ServerConsoleHost>();
IoCManager.Register<IServerConsoleHost, ServerConsoleHost>();
IoCManager.Register<IComponentFactory, ServerComponentFactory>();
IoCManager.Register<IConGroupController, ConGroupController>();
IoCManager.Register<IServerConsoleHost, ServerConsoleHost>();
IoCManager.Register<IConsoleHost, ServerConsoleHost>();
IoCManager.Register<IMapManager, ServerMapManager>();
IoCManager.Register<IMapManagerInternal, ServerMapManager>();
IoCManager.Register<IServerMapManager, ServerMapManager>();
@@ -55,7 +56,6 @@ namespace Robust.Server
IoCManager.Register<IPlayerManager, PlayerManager>();
IoCManager.Register<ISharedPlayerManager, PlayerManager>();
IoCManager.Register<IPrototypeManager, ServerPrototypeManager>();
IoCManager.Register<IReflectionManager, ServerReflectionManager>();
IoCManager.Register<IResourceManager, ResourceManager>();
IoCManager.Register<IResourceManagerInternal, ResourceManager>();
IoCManager.Register<IServerEntityManager, ServerEntityManager>();

View File

@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
@@ -17,6 +18,7 @@ namespace Robust.Shared.Containers
/// Holds data about a set of entity containers on this entity.
/// </summary>
[ComponentReference(typeof(IContainerManager))]
[NetworkedComponent()]
public class ContainerManagerComponent : Component, IContainerManager
{
[Dependency] private readonly IRobustSerializer _serializer = default!;
@@ -29,9 +31,6 @@ namespace Robust.Shared.Containers
/// <inheritdoc />
public sealed override string Name => "ContainerContainer";
/// <inheritdoc />
public sealed override uint? NetID => NetIDs.CONTAINER_MANAGER;
/// <inheritdoc />
protected override void OnRemove()
{
@@ -281,7 +280,7 @@ namespace Robust.Shared.Containers
{
public List<ContainerData> ContainerSet;
public ContainerManagerComponentState(List<ContainerData> containers) : base(NetIDs.CONTAINER_MANAGER)
public ContainerManagerComponentState(List<ContainerData> containers)
{
ContainerSet = containers;
}

View File

@@ -1,4 +1,5 @@
using System;
using Robust.Shared.GameStates;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.Reflection;
@@ -18,14 +19,6 @@ namespace Robust.Shared.GameObjects
[ViewVariables]
public abstract string Name { get; }
/// <inheritdoc />
[ViewVariables]
public virtual uint? NetID => null;
/// <inheritdoc />
[ViewVariables]
public virtual bool NetworkSynchronizeExistence => false;
/// <inheritdoc />
[ViewVariables]
[DataField("netsync")]
@@ -249,14 +242,16 @@ namespace Robust.Shared.GameObjects
[Obsolete("Component Messages are deprecated, use Entity Events instead.")]
public virtual void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) { }
private static readonly ComponentState DefaultComponentState = new();
/// <param name="player"></param>
/// <inheritdoc />
public virtual ComponentState GetComponentState(ICommonSession player)
{
if (NetID == null)
throw new InvalidOperationException($"Cannot make state for component without Net ID: {GetType()}");
if (!(Attribute.GetCustomAttribute(GetType(), typeof(NetworkedComponentAttribute)) is NetworkedComponentAttribute))
throw new InvalidOperationException($"Calling base {nameof(GetComponentState)} without being networked.");
return new ComponentState(NetID.Value);
return DefaultComponentState;
}
/// <inheritdoc />

View File

@@ -3,31 +3,33 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.Serialization;
using JetBrains.Annotations;
using Robust.Shared.Console;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Reflection;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
public class ComponentFactory : IComponentFactory
internal class ComponentFactory : IComponentFactory
{
[Dependency] private readonly IDynamicTypeFactoryInternal _typeFactory = default!;
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
private readonly IDynamicTypeFactoryInternal _typeFactory;
private readonly IReflectionManager _reflectionManager;
private class ComponentRegistration : IComponentRegistration
{
public string Name { get; }
public uint? NetID { get; }
public bool NetworkSynchronizeExistence { get; }
public ushort? NetID { get; set; }
public Type Type { get; }
internal readonly List<Type> References = new();
IReadOnlyList<Type> IComponentRegistration.References => References;
public ComponentRegistration(string name, Type type, uint? netID, bool networkSynchronizeExistence)
public ComponentRegistration(string name, Type type)
{
Name = name;
NetID = netID;
NetworkSynchronizeExistence = networkSynchronizeExistence;
NetID = null;
Type = type;
References.Add(type);
}
@@ -52,7 +54,7 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Mapping of network ID to type.
/// </summary>
private readonly Dictionary<uint, ComponentRegistration> netIDs = new();
private List<IComponentRegistration>? _networkedComponents;
/// <summary>
/// Mapping of concrete component types to their registration.
@@ -76,8 +78,34 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public IEnumerable<Type> AllRegisteredTypes => types.Keys;
/// <inheritdoc />
public IReadOnlyList<IComponentRegistration>? NetworkedComponents => _networkedComponents;
private IEnumerable<ComponentRegistration> AllRegistrations => types.Values;
public ComponentFactory(IDynamicTypeFactoryInternal typeFactory, IReflectionManager reflectionManager, IConsoleHost conHost)
{
_typeFactory = typeFactory;
_reflectionManager = reflectionManager;
conHost.RegisterCommand("dump_net_comps", "Prints the table of networked components.", "dump_net_comps", (shell, argStr, args) =>
{
if (_networkedComponents is null)
{
shell.WriteError("Registration still writeable, network ids have not been generated.");
return;
}
shell.WriteLine("Networked Component Registrations:");
for (int netId = 0; netId < _networkedComponents.Count; netId++)
{
var registration = _networkedComponents[netId];
shell.WriteLine($" [{netId,4}] {registration.Name,-16} {registration.Type.Name}");
}
});
}
[Obsolete("Use RegisterClass and Attributes instead of the Register/RegisterReference combo")]
public void Register<T>(bool overwrite = false) where T : IComponent, new()
{
@@ -86,6 +114,9 @@ namespace Robust.Shared.GameObjects
private void Register(Type type, bool overwrite = false)
{
if (_networkedComponents is not null)
throw new ComponentRegistrationLockException();
if (types.ContainsKey(type))
{
throw new InvalidOperationException($"Type is already registered: {type}");
@@ -97,8 +128,6 @@ namespace Robust.Shared.GameObjects
var name = dummy.Name;
var lowerCaseName = name.ToLowerInvariant();
var netID = dummy.NetID;
var netSyncExist = dummy.NetworkSynchronizeExistence;
if (IgnoredComponentNames.Contains(name))
{
@@ -128,24 +157,11 @@ namespace Robust.Shared.GameObjects
}
}
if (netID != null && netIDs.ContainsKey(netID.Value))
{
if (!overwrite)
{
throw new InvalidOperationException($"{name} has duplicate network ID {netID}, previous: {netIDs[netID.Value]}");
}
RemoveComponent(netIDs[netID.Value].Name);
}
var registration = new ComponentRegistration(name, type, netID, netSyncExist);
var registration = new ComponentRegistration(name, type);
names[name] = registration;
_lowerCaseNames[lowerCaseName] = name;
types[type] = registration;
if (netID != null)
{
netIDs[netID.Value] = registration;
}
ComponentAdded?.Invoke(registration);
}
@@ -157,6 +173,9 @@ namespace Robust.Shared.GameObjects
private void RegisterReference(Type target, Type @interface)
{
if (_networkedComponents is not null)
throw new ComponentRegistrationLockException();
if (!types.ContainsKey(target))
{
throw new InvalidOperationException($"Unregistered type: {target}");
@@ -194,15 +213,14 @@ namespace Robust.Shared.GameObjects
private void RemoveComponent(string name)
{
if (_networkedComponents is not null)
throw new ComponentRegistrationLockException();
var registration = names[name];
names.Remove(registration.Name);
_lowerCaseNames.Remove(registration.Name.ToLowerInvariant());
types.Remove(registration.Type);
if (registration.NetID != null)
{
netIDs.Remove(registration.NetID.Value);
}
}
public ComponentAvailability GetComponentAvailability(string componentName, bool ignoreCase = false)
@@ -253,7 +271,7 @@ namespace Robust.Shared.GameObjects
return _typeFactory.CreateInstanceUnchecked<IComponent>(GetRegistration(componentName).Type);
}
public IComponent GetComponent(uint netId)
public IComponent GetComponent(ushort netId)
{
return _typeFactory.CreateInstanceUnchecked<IComponent>(GetRegistration(netId).Type);
}
@@ -275,11 +293,14 @@ namespace Robust.Shared.GameObjects
}
}
public IComponentRegistration GetRegistration(uint netID)
public IComponentRegistration GetRegistration(ushort netID)
{
if (_networkedComponents is null)
throw new ComponentRegistrationLockException();
try
{
return netIDs[netID];
return _networkedComponents[netID];
}
catch (KeyNotFoundException)
{
@@ -343,9 +364,9 @@ namespace Robust.Shared.GameObjects
return TryGetRegistration(typeof(T), out registration);
}
public bool TryGetRegistration(uint netID, [NotNullWhen(true)] out IComponentRegistration? registration)
public bool TryGetRegistration(ushort netID, [NotNullWhen(true)] out IComponentRegistration? registration)
{
if (netIDs.TryGetValue(netID, out var tempRegistration))
if (_networkedComponents is not null && _networkedComponents.TryGetValue(netID, out var tempRegistration))
{
registration = tempRegistration;
return true;
@@ -406,15 +427,36 @@ namespace Robust.Shared.GameObjects
return AllRegistrations.SelectMany(r => r.References).Distinct();
}
public IEnumerable<uint> GetAllNetIds()
/// <inheritdoc />
public void GenerateNetIds()
{
foreach (var registration in AllRegistrations)
// assumptions:
// component names are guaranteed to be unique
// component names are 1:1 with component concrete types
// a subset of component names are networked
var networkedRegs = new List<IComponentRegistration>(names.Count);
foreach (var kvRegistration in names)
{
if (registration.NetID != null)
var registration = kvRegistration.Value;
if (Attribute.GetCustomAttribute(registration.Type, typeof(NetworkedComponentAttribute)) is NetworkedComponentAttribute)
{
yield return registration.NetID.Value;
networkedRegs.Add(registration);
}
}
// The sorting implementation is unstable, but there are no duplicate names, so that isn't a problem.
// Ordinal comparison is used so that the resulting order is always identical on every computer.
networkedRegs.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
for (ushort i = 0; i < networkedRegs.Count; i++)
{
var registration = (ComponentRegistration) networkedRegs[i];
registration.NetID = i;
}
_networkedComponents = networkedRegs;
}
}
@@ -434,4 +476,6 @@ namespace Robust.Shared.GameObjects
SerializationInfo info,
StreamingContext context) : base(info, context) { }
}
public class ComponentRegistrationLockException : Exception { }
}

View File

@@ -1,8 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Internal.TypeSystem;
using Robust.Shared.Exceptions;
using Robust.Shared.Physics;
using Robust.Shared.Serialization;
@@ -28,7 +30,7 @@ namespace Robust.Shared.GameObjects
private const int EntityCapacity = 1024;
private const int NetComponentCapacity = 8;
private readonly Dictionary<EntityUid, Dictionary<uint, Component>> _netComponents
private readonly Dictionary<EntityUid, Dictionary<ushort, Component>> _netComponents
= new(EntityCapacity);
private readonly Dictionary<Type, Dictionary<EntityUid, Component>> _entTraitDict
@@ -144,14 +146,14 @@ namespace Robust.Shared.GameObjects
}
// add the component to the netId grid
if (component.NetID != null)
if (reg.NetID != null)
{
// the main comp grid keeps this in sync
var netId = component.NetID.Value;
var netId = reg.NetID.Value;
if (!_netComponents.TryGetValue(uid, out var netSet))
{
netSet = new Dictionary<uint, Component>(NetComponentCapacity);
netSet = new Dictionary<ushort, Component>(NetComponentCapacity);
_netComponents.Add(uid, netSet);
}
netSet.Add(netId, component);
@@ -191,7 +193,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveComponent(EntityUid uid, uint netId)
public void RemoveComponent(EntityUid uid, ushort netId)
{
RemoveComponentDeferred((Component)GetComponent(uid, netId), uid, false);
}
@@ -335,13 +337,13 @@ namespace Robust.Shared.GameObjects
}
// ReSharper disable once InvertIf
if (component.NetID != null)
if (reg.NetID != null)
{
var netSet = _netComponents[entityUid];
if (netSet.Count == 1)
_netComponents.Remove(entityUid);
else
netSet.Remove(component.NetID.Value);
netSet.Remove(reg.NetID.Value);
component.Owner.Dirty();
}
@@ -367,7 +369,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent(EntityUid uid, uint netId)
public bool HasComponent(EntityUid uid, ushort netId)
{
return _netComponents.TryGetValue(uid, out var netSet)
&& netSet.ContainsKey(netId);
@@ -397,7 +399,7 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public IComponent GetComponent(EntityUid uid, uint netId)
public IComponent GetComponent(EntityUid uid, ushort netId)
{
return _netComponents[uid][netId];
}
@@ -436,7 +438,7 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public bool TryGetComponent(EntityUid uid, uint netId, [MaybeNullWhen(false)] out IComponent component)
public bool TryGetComponent(EntityUid uid, ushort netId, [MaybeNullWhen(false)] out IComponent component)
{
if (_netComponents.TryGetValue(uid, out var netSet)
&& netSet.TryGetValue(netId, out var comp))
@@ -474,9 +476,9 @@ namespace Robust.Shared.GameObjects
}
/// <inheritdoc />
public IEnumerable<IComponent> GetNetComponents(EntityUid uid)
public NetComponentEnumerable GetNetComponents(EntityUid uid)
{
return _netComponents[uid].Values;
return new NetComponentEnumerable(_netComponents[uid]);
}
#region Join Functions
@@ -597,4 +599,30 @@ namespace Robust.Shared.GameObjects
}
}
}
public readonly struct NetComponentEnumerable
{
private readonly Dictionary<ushort, Component> _dictionary;
public NetComponentEnumerable(Dictionary<ushort, Component> dictionary) => _dictionary = dictionary;
public NetComponentEnumerator GetEnumerator() => new(_dictionary);
}
public struct NetComponentEnumerator
{
// DO NOT MAKE THIS READONLY
private Dictionary<ushort, Component>.Enumerator _dictEnum;
public NetComponentEnumerator(Dictionary<ushort, Component> dictionary) => _dictEnum = dictionary.GetEnumerator();
public bool MoveNext() => _dictEnum.MoveNext();
public (ushort netId, IComponent component) Current
{
get
{
var val = _dictEnum.Current;
return (val.Key, val.Value);
}
}
}
}

View File

@@ -1,18 +1,10 @@
using Robust.Shared.Serialization;
using System;
using Robust.Shared.Analyzers;
using Robust.Shared.Serialization;
namespace Robust.Shared.GameObjects
{
[RequiresSerializable]
[Serializable, NetSerializable]
public class ComponentState
{
public uint NetID { get; }
public ComponentState(uint netID)
{
NetID = netID;
}
}
public class ComponentState { }
}

View File

@@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Robust.Shared.GameObjects
@@ -11,10 +12,10 @@ namespace Robust.Shared.GameObjects
/// The data works using a simple key/value system. It is recommended to use enum keys to prevent errors.
/// Visualization works client side with overrides of the <c>AppearanceVisualizer</c> class.
/// </summary>
[NetworkedComponent()]
public abstract class SharedAppearanceComponent : Component
{
public override string Name => "Appearance";
public override uint? NetID => NetIDs.APPEARANCE;
public abstract void SetData(string key, object value);
public abstract void SetData(Enum key, object value);
@@ -30,7 +31,7 @@ namespace Robust.Shared.GameObjects
{
public readonly Dictionary<object, object> Data;
public AppearanceComponentState(Dictionary<object, object> data) : base(NetIDs.APPEARANCE)
public AppearanceComponentState(Dictionary<object, object> data)
{
Data = data;
}

View File

@@ -9,7 +9,7 @@ namespace Robust.Shared.GameObjects
{
public Box2? LocalBounds { get; }
public ClickableComponentState(Box2? localBounds) : base(NetIDs.CLICKABLE)
public ClickableComponentState(Box2? localBounds)
{
LocalBounds = localBounds;
}

View File

@@ -26,6 +26,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -44,6 +45,7 @@ using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects
{
[ComponentReference(typeof(IPhysBody))]
[NetworkedComponent()]
public sealed class PhysicsComponent : Component, IPhysBody, ISerializationHooks
{
[DataField("status", readOnly: true)]
@@ -52,9 +54,6 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override string Name => "Physics";
/// <inheritdoc />
public override uint? NetID => NetIDs.PHYSICS;
/// <summary>
/// Has this body been added to an island previously in this tick.
/// </summary>

View File

@@ -44,7 +44,6 @@ namespace Robust.Shared.GameObjects
Vector2 linearVelocity,
float angularVelocity,
BodyType bodyType)
: base(NetIDs.PHYSICS)
{
CanCollide = canCollide;
SleepingAllowed = sleepingAllowed;

View File

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using Robust.Shared.GameStates;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
@@ -11,10 +12,10 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// An optimisation component for stuff that should be set as collidable when it's awake and non-collidable when asleep.
/// </summary>
[NetworkedComponent()]
public sealed class CollisionWakeComponent : Component
{
public override string Name => "CollisionWake";
public override uint? NetID => NetIDs.COLLISION_WAKE;
[DataField("enabled")]
private bool _enabled = true;
@@ -64,7 +65,7 @@ namespace Robust.Shared.GameObjects
{
public bool Enabled { get; }
public CollisionWakeState(bool enabled) : base(NetIDs.COLLISION_WAKE)
public CollisionWakeState(bool enabled)
{
Enabled = enabled;
}

View File

@@ -1,14 +1,15 @@
using System;
using Robust.Shared.GameStates;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects
{
[NetworkedComponent()]
public class SharedEyeComponent : Component
{
public override string Name => "Eye";
public override uint? NetID => NetIDs.EYE;
[ViewVariables(VVAccess.ReadWrite)]
public virtual bool DrawFov { get; set; }
@@ -39,7 +40,7 @@ namespace Robust.Shared.GameObjects
public Angle Rotation { get; }
public uint VisibilityMask { get; }
public EyeComponentState(bool drawFov, Vector2 zoom, Vector2 offset, Angle rotation, uint visibilityMask) : base(NetIDs.EYE)
public EyeComponentState(bool drawFov, Vector2 zoom, Vector2 offset, Angle rotation, uint visibilityMask)
{
DrawFov = drawFov;
Zoom = zoom;

View File

@@ -1,4 +1,5 @@
using System;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
@@ -9,10 +10,10 @@ using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects
{
[NetworkedComponent()]
public class OccluderComponent : Component
{
public sealed override string Name => "Occluder";
public sealed override uint? NetID => NetIDs.OCCLUDER;
[DataField("enabled")]
private bool _enabled = true;
@@ -94,7 +95,7 @@ namespace Robust.Shared.GameObjects
public bool Enabled { get; }
public Box2 BoundingBox { get; }
public OccluderComponentState(bool enabled, Box2 boundingBox) : base(NetIDs.OCCLUDER)
public OccluderComponentState(bool enabled, Box2 boundingBox)
{
Enabled = enabled;
BoundingBox = boundingBox;

View File

@@ -14,7 +14,6 @@ namespace Robust.Shared.GameObjects
public readonly Vector2 Offset;
public PointLightComponentState(bool enabled, Color color, float radius, Vector2 offset)
: base(NetIDs.POINT_LIGHT)
{
Enabled = enabled;
Color = color;

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Robust.Shared.Enums;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -10,10 +11,10 @@ namespace Robust.Shared.GameObjects.Components.Localization
/// Overrides grammar attributes specified in prototypes or localization files.
/// </summary>
[RegisterComponent]
[NetworkedComponent()]
public class GrammarComponent : Component
{
public override string Name => "Grammar";
public override uint? NetID => NetIDs.GRAMMAR;
[ViewVariables]
[DataField("attributes")]

View File

@@ -1,4 +1,5 @@
using System;
using System;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
@@ -18,6 +19,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc cref="IMapComponent"/>
[ComponentReference(typeof(IMapComponent))]
[NetworkedComponent()]
public class MapComponent : Component, IMapComponent
{
[ViewVariables(VVAccess.ReadOnly)]
@@ -27,9 +29,6 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override string Name => "Map";
/// <inheritdoc />
public override uint? NetID => NetIDs.MAP_MAP;
/// <inheritdoc />
public MapId WorldMap
{
@@ -73,7 +72,6 @@ namespace Robust.Shared.GameObjects
public MapId MapId { get; }
public MapComponentState(MapId mapId)
: base(NetIDs.MAP_MAP)
{
MapId = mapId;
}

View File

@@ -1,4 +1,5 @@
using System;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -25,6 +26,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc cref="IMapGridComponent"/>
[ComponentReference(typeof(IMapGridComponent))]
[NetworkedComponent()]
internal class MapGridComponent : Component, IMapGridComponent
{
[Dependency] private readonly IMapManager _mapManager = default!;
@@ -36,9 +38,6 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override string Name => "MapGrid";
/// <inheritdoc />
public override uint? NetID => NetIDs.MAP_GRID;
/// <inheritdoc />
public GridId GridIndex
{
@@ -145,7 +144,6 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <param name="gridIndex">Index of the grid this component is linked to.</param>
public MapGridComponentState(GridId gridIndex)
: base(NetIDs.MAP_GRID)
{
GridIndex = gridIndex;
}

View File

@@ -1,4 +1,5 @@
using System;
using System;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Players;
using Robust.Shared.Prototypes;
@@ -35,7 +36,6 @@ namespace Robust.Shared.GameObjects
/// <param name="description">The in-game description of this entity.</param>
/// <param name="prototypeId">The prototype this entity was created from, if any.</param>
public MetaDataComponentState(string? name, string? description, string? prototypeId)
: base(NetIDs.META_DATA)
{
Name = name;
Description = description;
@@ -66,6 +66,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc cref="IMetaDataComponent"/>
[ComponentReference(typeof(IMetaDataComponent))]
[NetworkedComponent()]
internal class MetaDataComponent : Component, IMetaDataComponent
{
[Dependency] private readonly IPrototypeManager _prototypes = default!;
@@ -79,9 +80,6 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override string Name => "MetaData";
/// <inheritdoc />
public override uint? NetID => NetIDs.META_DATA;
/// <inheritdoc />
[ViewVariables(VVAccess.ReadWrite)]
public string EntityName

View File

@@ -1,21 +0,0 @@
namespace Robust.Shared.GameObjects
{
public static class NetIDs
{
public const uint MAP_MAP = 1;
public const uint MAP_GRID = 2;
public const uint META_DATA = 4;
public const uint TRANSFORM = 5;
public const uint SPRITE = 8;
public const uint POINT_LIGHT = 10;
public const uint PHYSICS = 12;
public const uint CLICKABLE = 14;
public const uint APPEARANCE = 22;
public const uint USERINTERFACE = 24;
public const uint CONTAINER_MANAGER = 25;
public const uint OCCLUDER = 26;
public const uint EYE = 28;
public const uint GRAMMAR = 29;
public const uint COLLISION_WAKE = 30;
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameStates;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -7,10 +8,10 @@ using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
[NetworkedComponent()]
public abstract class SharedSpriteComponent : Component
{
public override string Name => "Sprite";
public override uint? NetID => NetIDs.SPRITE;
public abstract bool Visible { get; set; }
@@ -42,7 +43,6 @@ namespace Robust.Shared.GameObjects
string? baseRsiPath,
List<PrototypeLayerData> layers,
uint renderOrder)
: base(NetIDs.SPRITE)
{
Visible = visible;
DrawDepth = drawDepth;

View File

@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Robust.Shared.Animations;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
@@ -16,6 +17,7 @@ using Robust.Shared.ViewVariables;
namespace Robust.Shared.GameObjects
{
[ComponentReference(typeof(ITransformComponent))]
[NetworkedComponent()]
internal class TransformComponent : Component, ITransformComponent, IComponentDebug
{
[DataField("parent")]
@@ -54,9 +56,6 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public override string Name => "Transform";
/// <inheritdoc />
public sealed override uint? NetID => NetIDs.TRANSFORM;
/// <inheritdoc />
[ViewVariables]
public MapId MapID { get; private set; }
@@ -896,7 +895,6 @@ namespace Robust.Shared.GameObjects
/// <param name="parentId">Current parent transform of this entity.</param>
/// <param name="noLocalRotation"></param>
public TransformComponentState(Vector2 localPosition, Angle rotation, EntityUid parentId, bool noLocalRotation, bool anchored)
: base(NetIDs.TRANSFORM)
{
LocalPosition = localPosition;
Rotation = rotation;

View File

@@ -1,4 +1,5 @@
using System;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
@@ -6,10 +7,10 @@ using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.Shared.GameObjects
{
[NetworkedComponent()]
public abstract class SharedUserInterfaceComponent : Component
{
public sealed override string Name => "UserInterface";
public sealed override uint? NetID => NetIDs.USERINTERFACE;
[DataDefinition]
public sealed class PrototypeData : ISerializationHooks

View File

@@ -21,7 +21,7 @@ namespace Robust.Shared.GameObjects
[IoC.Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
[IoC.Dependency] protected readonly IEntitySystemManager EntitySystemManager = default!;
[IoC.Dependency] private readonly IComponentFactory ComponentFactory = default!;
[IoC.Dependency] protected readonly IComponentFactory ComponentFactory = default!;
[IoC.Dependency] private readonly IComponentManager _componentManager = default!;
[IoC.Dependency] private readonly IMapManager _mapManager = default!;
[IoC.Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -32,6 +32,8 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public GameTick CurrentTick => _gameTiming.CurTick;
IComponentFactory IEntityManager.ComponentFactory => ComponentFactory;
/// <inheritdoc />
public IComponentManager ComponentManager => _componentManager;
@@ -479,7 +481,7 @@ namespace Robust.Shared.GameObjects
var uid = netMsg.EntityUid;
if (compMsg.Directed)
{
if (_componentManager.TryGetComponent(uid, netMsg.NetId, out var component))
if (_componentManager.TryGetComponent(uid, (ushort) netMsg.NetId, out var component))
component.HandleNetworkMessage(compMsg, compChannel, session);
}
else

View File

@@ -7,61 +7,71 @@ namespace Robust.Shared.GameObjects
public sealed class EntityState
{
public EntityUid Uid { get; }
public ComponentChanged[]? ComponentChanges { get; }
public ComponentState[]? ComponentStates { get; }
public bool Empty => ComponentChanges is null && ComponentStates is null;
public ComponentChange[]? ComponentChanges { get; }
public EntityState(EntityUid uid, ComponentChanged[]? changedComponents, ComponentState[]? componentStates)
public bool Empty => ComponentChanges is null;
public EntityState(EntityUid uid, ComponentChange[]? changedComponents)
{
Uid = uid;
// empty lists are 5 bytes each
ComponentChanges = changedComponents == null || changedComponents.Length == 0 ? null : changedComponents;
ComponentStates = componentStates == null || componentStates.Length == 0 ? null : componentStates;
}
}
[Serializable, NetSerializable]
public readonly struct ComponentChanged
public readonly struct ComponentChange
{
// 15ish bytes to create a component (strings are big), 5 bytes to remove one
/// <summary>
/// Was the component added or removed from the entity.
/// Was the component removed from the entity.
/// </summary>
public readonly bool Deleted;
/// <summary>
/// The Network ID of the component to remove.
/// Was the component added to the entity.
/// </summary>
public readonly uint NetID;
public readonly bool Created;
/// <summary>
/// The prototype name of the component to add.
/// State data for the created/modified component, if any.
/// </summary>
public readonly string? ComponentName;
public readonly ComponentState? State;
public ComponentChanged(bool deleted, uint netId, string? componentName)
/// <summary>
/// The Network ID of the component to remove.
/// </summary>
public readonly ushort NetID;
public ComponentChange(ushort netId, bool created, bool deleted, ComponentState? state)
{
Deleted = deleted;
State = state;
NetID = netId;
ComponentName = componentName;
Created = created;
}
public override string ToString()
{
return $"{(Deleted ? "D" : "C")} {NetID} {ComponentName}";
return $"{(Deleted ? "D" : "C")} {NetID} {State?.GetType().Name}";
}
public static ComponentChanged Added(uint netId, string componentName)
public static ComponentChange Added(ushort netId, ComponentState? state)
{
return new(false, netId, componentName);
return new(netId, true, false, state);
}
public static ComponentChanged Removed(uint netId)
public static ComponentChange Changed(ushort netId, ComponentState state)
{
return new(true, netId, null);
return new(netId, false, false, state);
}
public static ComponentChange Removed(ushort netId)
{
return new(netId, false, true, null);
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using Robust.Shared.GameStates;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Robust.Shared.GameObjects
@@ -13,16 +13,6 @@ namespace Robust.Shared.GameObjects
/// </remarks>
public interface IComponent
{
/// <summary>
/// Represents the network ID for the component.
/// The network ID is used to determine which component will receive the component state
/// on the other side of the network.
/// If this is <c>null</c>, the component is not replicated across the network.
/// </summary>
/// <seealso cref="NetworkSynchronizeExistence" />
/// <seealso cref="IComponentRegistration.NetID" />
uint? NetID { get; }
/// <summary>
/// Name that this component is represented with in prototypes.
/// </summary>
@@ -40,21 +30,11 @@ namespace Robust.Shared.GameObjects
/// </summary>
ComponentLifeStage LifeStage { get; }
/// <summary>
/// Whether the client should synchronize component additions and removals.
/// If this is false and the component gets added or removed server side, the client will not do the same.
/// If this is true and the server adds or removes the component, the client will do as such too.
/// This flag has no effect if <see cref="NetID" /> is <c>null</c>.
/// This is disabled by default, usually the client builds their instance from a prototype.
/// </summary>
/// <seealso cref="IComponentRegistration.NetworkSynchronizeExistence" />
bool NetworkSynchronizeExistence { get; }
/// <summary>
/// Whether this component should be synchronized with clients when modified.
/// If this is true, the server will synchronize all client instances with the data in this instance.
/// If this is false, clients can modify the data in their instances without being overwritten by the server.
/// This flag has no effect if <see cref="NetID" /> is <c>null</c>.
/// This flag has no effect if <see cref="NetworkedComponentAttribute" /> is not defined on the component.
/// This is enabled by default.
/// </summary>
bool NetSyncEnabled { get; }

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Robust.Shared.GameObjects
@@ -58,6 +59,16 @@ namespace Robust.Shared.GameObjects
/// </summary>
IEnumerable<Type> AllRegisteredTypes { get; }
/// <summary>
/// The subset of all registered components that are networked, so that they can be
/// referenced between the client and the server.
/// </summary>
/// <remarks>
/// This will be null if the network Ids have not been generated yet.
/// </remarks>
/// <seealso cref="GenerateNetIds"/>
IReadOnlyList<IComponentRegistration>? NetworkedComponents { get; }
/// <summary>
/// Get whether a component is available right now.
/// </summary>
@@ -142,7 +153,7 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no component exists with the given id <see cref="netId"/>.
/// </exception>
IComponent GetComponent(uint netId);
IComponent GetComponent(ushort netId);
/// <summary>
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
@@ -181,7 +192,7 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no component with id <see cref="netID"/> exists.
/// </exception>
IComponentRegistration GetRegistration(uint netID);
IComponentRegistration GetRegistration(ushort netID);
/// <summary>
/// Gets the registration of a component, throwing an exception if
@@ -225,7 +236,7 @@ namespace Robust.Shared.GameObjects
/// <param name="netID">The network ID corresponding to the component.</param>
/// <param name="registration">The registration if found, null otherwise.</param>
/// <returns>true it found, false otherwise.</returns>
bool TryGetRegistration(uint netID, [NotNullWhen(true)] out IComponentRegistration? registration);
bool TryGetRegistration(ushort netID, [NotNullWhen(true)] out IComponentRegistration? registration);
/// <summary>
/// Tries to get the registration of a component.
@@ -241,8 +252,7 @@ namespace Robust.Shared.GameObjects
void DoAutoRegistrations();
IEnumerable<Type> GetAllRefTypes();
IEnumerable<uint> GetAllNetIds();
void GenerateNetIds();
}
/// <summary>
@@ -263,18 +273,8 @@ namespace Robust.Shared.GameObjects
/// ID used to reference the component type across the network.
/// If null, no network synchronization will be available for this component.
/// </summary>
/// <seealso cref="IComponent.NetID" />
uint? NetID { get; }
/// <summary>
/// True if the addition and removal of the component will be synchronized to clients.
/// This means that if the server adds or removes the component outside of prototype-based creation,
/// the client will update accordingly.
/// If false the client will ignore missing components even when the net ID checks out and could be instantiated.
/// and the client won't delete the component if no state was sent for it.
/// </summary>
/// <seealso cref="IComponent.NetworkSynchronizeExistence" />
bool NetworkSynchronizeExistence { get; }
/// <seealso cref="NetworkedComponentAttribute" />
ushort? NetID { get; }
/// <summary>
/// The type that will be instantiated if this component is created.

View File

@@ -72,7 +72,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <param name="uid">Entity UID to modify.</param>
/// <param name="netID">Network ID of the component to remove.</param>
void RemoveComponent(EntityUid uid, uint netID);
void RemoveComponent(EntityUid uid, ushort netID);
/// <summary>
/// Removes the specified component.
@@ -118,7 +118,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to check.</param>
/// <param name="netId">Network ID to check for.</param>
/// <returns>True if the entity has a component with the given network ID, otherwise false.</returns>
bool HasComponent(EntityUid uid, uint netId);
bool HasComponent(EntityUid uid, ushort netId);
/// <summary>
/// Returns the component of a specific type.
@@ -143,7 +143,7 @@ namespace Robust.Shared.GameObjects
/// <param name="uid">Entity UID to look on.</param>
/// <param name="netId">Network ID of the component to retrieve.</param>
/// <returns>The component with the specified network id.</returns>
IComponent GetComponent(EntityUid uid, uint netId);
IComponent GetComponent(EntityUid uid, ushort netId);
/// <summary>
/// Returns the component of a specific type.
@@ -171,7 +171,7 @@ namespace Robust.Shared.GameObjects
/// <param name="netId">Component Network ID to check for.</param>
/// <param name="component">Component with the specified network id.</param>
/// <returns>If the component existed in the entity.</returns>
bool TryGetComponent(EntityUid uid, uint netId, [NotNullWhen(true)] out IComponent? component);
bool TryGetComponent(EntityUid uid, ushort netId, [NotNullWhen(true)] out IComponent? component);
/// <summary>
/// Returns ALL component type instances on an entity. A single component instance
@@ -195,7 +195,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <param name="uid">Entity UID to look on.</param>
/// <returns>All components that have a network ID.</returns>
IEnumerable<IComponent> GetNetComponents(EntityUid uid);
NetComponentEnumerable GetNetComponents(EntityUid uid);
/// <summary>
/// Returns ALL component instances of a specified type.

View File

@@ -24,6 +24,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
void FrameUpdate(float frameTime);
IComponentFactory ComponentFactory { get; }
IComponentManager ComponentManager { get; }
IEntitySystemManager EntitySysManager { get; }
IEntityNetworkManager? EntityNetManager { get; }

View File

@@ -0,0 +1,10 @@
using System;
namespace Robust.Shared.GameStates
{
/// <summary>
/// This attribute marks a component as networked, so that it is replicated to clients.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class NetworkedComponentAttribute : Attribute { }
}

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using JetBrains.Annotations;
namespace Robust.Shared.Utility
{
@@ -37,6 +38,18 @@ namespace Robust.Shared.Utility
return dict;
}
public static bool TryGetValue<T>(this IList<T> list, int index, out T value)
{
if (list.Count > index)
{
value = list[index];
return true;
}
value = default!;
return false;
}
/// <summary>
/// Remove an item from the list, replacing it with the one at the very end of the list.
/// This means that the order will not be preserved, but it should be an O(1) operation.

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
@@ -267,7 +267,6 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
var containerMan = entity.GetComponent<IContainerManager>();
var state = (ContainerManagerComponent.ContainerManagerComponentState)containerMan.GetComponentState(new Mock<ICommonSession>().Object);
Assert.That(state.NetID, Is.EqualTo(containerMan.NetID));
Assert.That(state.ContainerSet.Count, Is.EqualTo(1));
Assert.That(state.ContainerSet[0].Id, Is.EqualTo("dummy"));
Assert.That(state.ContainerSet[0].OccludesLight, Is.True);

View File

@@ -62,6 +62,8 @@ namespace Robust.UnitTesting.Server.GameObjects.Components
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IComponentFactory>().GenerateNetIds();
EntityManager = IoCManager.Resolve<IServerEntityManagerInternal>();
MapManager = IoCManager.Resolve<IMapManager>();
MapManager.CreateMap();

View File

@@ -41,6 +41,7 @@ namespace Robust.UnitTesting.Server.GameObjects
_componentFactory.RegisterClass<ThrowsInAddComponent>();
_componentFactory.RegisterClass<ThrowsInInitializeComponent>();
_componentFactory.RegisterClass<ThrowsInStartupComponent>();
_componentFactory.GenerateNetIds();
EntityManager = IoCManager.Resolve<IServerEntityManager>();
MapManager = IoCManager.Resolve<IMapManager>();

View File

@@ -84,6 +84,7 @@ entities:
{
var compFactory = IoCManager.Resolve<IComponentFactory>();
compFactory.RegisterClass<MapDeserializeTestComponent>();
compFactory.GenerateNetIds();
IoCManager.Resolve<ISerializationManager>().Initialize();
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();

View File

@@ -10,6 +10,7 @@ using Robust.Server.Reflection;
using Robust.Shared;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.ContentPack;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
@@ -190,6 +191,7 @@ namespace Robust.UnitTesting.Server
container.RegisterInstance<IGameTiming>(new Mock<IGameTiming>().Object); // TODO: get timing working similar to RobustIntegrationTest
//Tier 2: Simulation
container.RegisterInstance<IConsoleHost>(new Mock<IConsoleHost>().Object); //Console is technically a frontend, we want to run headless
container.Register<IEntityManager, EntityManager>();
container.Register<IMapManager, MapManager>();
container.Register<IEntityLookup, EntityLookup>();
@@ -226,6 +228,8 @@ namespace Robust.UnitTesting.Server
_regDelegate?.Invoke(compFactory);
compFactory.GenerateNetIds();
var entityMan = container.Resolve<IEntityManager>();
entityMan.Initialize();

View File

@@ -1,6 +1,7 @@
using System.IO;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
@@ -61,6 +62,9 @@ namespace Robust.UnitTesting.Shared.ContentPack
[OneTimeSetUp]
public void Setup()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
componentFactory.GenerateNetIds();
var stream = new MemoryStream(Data);
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
resourceManager.MountStreamAt(stream, new ResourcePath("/a/b/c.dat"));

View File

@@ -164,6 +164,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
componentFactory.RegisterClass<TestFiveComponent>();
componentFactory.RegisterClass<TestSixComponent>();
componentFactory.RegisterClass<TestSevenComponent>();
componentFactory.GenerateNetIds();
IoCManager.Resolve<ISerializationManager>().Initialize();
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();

View File

@@ -1,6 +1,7 @@
using System.Linq;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.UnitTesting.Server;
@@ -10,7 +11,6 @@ namespace Robust.UnitTesting.Shared.GameObjects
[TestFixture, Parallelizable ,TestOf(typeof(ComponentManager))]
public class ComponentManager_Tests
{
private const uint CompNetId = 3;
private static readonly EntityCoordinates DefaultCoords = new(new EntityUid(1), Vector2.Zero);
[Test]
@@ -98,13 +98,17 @@ namespace Robust.UnitTesting.Shared.GameObjects
{
// Arrange
var sim = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var manager = sim.Resolve<IComponentManager>();
var entity = entMan.SpawnEntity(null, DefaultCoords);
entity.AddComponent<DummyComponent>();
// Act
var result = manager.HasComponent(entity.Uid, CompNetId);
var result = manager.HasComponent(entity.Uid, netId.Value);
// Assert
Assert.That(result, Is.True);
@@ -115,13 +119,17 @@ namespace Robust.UnitTesting.Shared.GameObjects
{
// Arrange
var sim = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var manager = sim.Resolve<IComponentManager>();
var entity = entMan.SpawnEntity(null, DefaultCoords);
var component = entity.AddComponent<DummyComponent>();
// Act
var result = manager.GetComponent(entity.Uid, CompNetId);
var result = manager.GetComponent(entity.Uid, netId.Value);
// Assert
Assert.That(result, Is.EqualTo(component));
@@ -150,13 +158,17 @@ namespace Robust.UnitTesting.Shared.GameObjects
{
// Arrange
var sim = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var manager = sim.Resolve<IComponentManager>();
var entity = entMan.SpawnEntity(null, DefaultCoords);
var component = entity.AddComponent<DummyComponent>();
// Act
var result = manager.TryGetComponent(entity.Uid, CompNetId, out var comp);
var result = manager.TryGetComponent(entity.Uid, netId.Value, out var comp);
// Assert
Assert.That(result, Is.True);
@@ -186,13 +198,17 @@ namespace Robust.UnitTesting.Shared.GameObjects
{
// Arrange
var sim = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var manager = sim.Resolve<IComponentManager>();
var entity = entMan.SpawnEntity(null, DefaultCoords);
var component = entity.AddComponent<DummyComponent>();
// Act
manager.RemoveComponent(entity.Uid, CompNetId);
manager.RemoveComponent(entity.Uid, netId.Value);
manager.CullRemovedComponents();
// Assert
@@ -269,10 +285,10 @@ namespace Robust.UnitTesting.Shared.GameObjects
return sim;
}
[NetworkedComponent()]
private class DummyComponent : Component, ICompType1, ICompType2
{
public override string Name => "Dummy";
public override uint? NetID => CompNetId;
}
private interface ICompType1 { }

View File

@@ -60,11 +60,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
new EntityUid(512),
new []
{
new ComponentChanged(false, NetIDs.MAP_GRID, nameof(MapGridComponent)),
},
new []
{
new MapGridComponentState(new GridId(0)),
new ComponentChange(0, true, false, new MapGridComponentState(new GridId(0)))
});
serializer.Serialize(stream, payload);

View File

@@ -19,7 +19,6 @@ namespace Robust.UnitTesting.Shared.GameObjects.Systems
private class Subscriber : IEntityEventSubscriber { }
private const string Prototypes = @"
- type: entity
name: anchoredEnt

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using NUnit.Framework;
@@ -21,7 +21,9 @@ namespace Robust.UnitTesting.Shared.Localization
public void Setup()
{
IoCManager.Resolve<ISerializationManager>().Initialize();
IoCManager.Resolve<IComponentFactory>().RegisterClass<GrammarComponent>();
var componentFactory = IoCManager.Resolve<IComponentFactory>();
componentFactory.RegisterClass<GrammarComponent>();
componentFactory.GenerateNetIds();
var res = IoCManager.Resolve<IResourceManagerInternal>();
res.MountString("/Locale/en-US/a.ftl", FluentCode);

View File

@@ -1,4 +1,4 @@
using System.IO;
using System.IO;
using Moq;
using NUnit.Framework;
using Robust.Server.GameObjects;
@@ -44,6 +44,9 @@ namespace Robust.UnitTesting.Shared.Map
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
prototypeManager.LoadFromStream(new StringReader(PROTOTYPES));
prototypeManager.Resync();
var factory = IoCManager.Resolve<IComponentFactory>();
factory.GenerateNetIds();
}
/// <summary>

View File

@@ -28,6 +28,12 @@ namespace Robust.UnitTesting.Shared.Map
IoCManager.RegisterInstance<IEntitySystemManager>(mock.Object, true);
}
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IComponentFactory>().GenerateNetIds();
}
[Test]
public void GetTileRefCoords()
{

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -42,6 +42,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
_components = IoCManager.Resolve<IComponentFactory>();
_components.RegisterClass<HotReloadTestComponentOne>();
_components.RegisterClass<HotReloadTestComponentTwo>();
_components.GenerateNetIds();
IoCManager.Resolve<ISerializationManager>().Initialize();
_prototypes = (PrototypeManager) IoCManager.Resolve<IPrototypeManager>();

View File

@@ -51,6 +51,7 @@ namespace Robust.UnitTesting.Shared.Serialization
componentFactory.RegisterClass<BaseComponent>();
componentFactory.RegisterClass<InheritorComponent>();
componentFactory.RegisterClass<FinalComponent>();
componentFactory.GenerateNetIds();
var serializationManager = IoCManager.Resolve<ISerializationManager>();
serializationManager.Initialize();