mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-09-15 06:42:25 +02:00
Entity creation performance improvements (#2911)
This commit is contained in:
@@ -94,3 +94,12 @@ cmd-dump_net_comps-help = Usage: dump_net-comps
|
||||
|
||||
cmd-dump_net_comps-error-writeable = Registration still writeable, network ids have not been generated.
|
||||
cmd-dump_net_comps-header = Networked Component Registrations:
|
||||
|
||||
## 'dump_event_tables' command
|
||||
cmd-dump_event_tables-desc = Prints directed event tables for an entity.
|
||||
cmd-dump_event_tables-help = Usage: dump_event_tables <entityUid>
|
||||
|
||||
cmd-dump_event_tables-missing-arg-entity = Missing entity argument
|
||||
cmd-dump_event_tables-error-entity = Invalid entity
|
||||
cmd-dump_event_tables-arg-entity = <entityUid>
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace Robust.Client.Console
|
||||
|
||||
if (AvailableCommands.ContainsKey(commandName))
|
||||
{
|
||||
#if !DEBUG
|
||||
#if FULL_RELEASE
|
||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
if (!_conGroup.CanCommand(commandName) && playerManager.LocalPlayer?.Session.Status > SessionStatus.Connecting)
|
||||
{
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Robust.Client.GameObjects
|
||||
return null;
|
||||
}
|
||||
|
||||
return TextureForConfig((IconComponent)compData, resourceCache);
|
||||
return TextureForConfig((IconComponent)compData.Component, resourceCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public sealed partial class SpriteSystem
|
||||
{
|
||||
// IconComponent takes precedence. If it has a valid icon, return that. Otherwise, continue as normal.
|
||||
if (prototype.Components.TryGetValue("Icon", out var compData)
|
||||
&& compData is IconComponent {Icon: {} icon})
|
||||
&& compData.Component is IconComponent {Icon: {} icon})
|
||||
{
|
||||
return icon.Default;
|
||||
}
|
||||
|
||||
@@ -937,7 +937,7 @@ namespace Robust.Server.Maps
|
||||
prototypeCompCache[prototype.ID] = cache = new Dictionary<string, MappingDataNode>();
|
||||
foreach (var (compType, comp) in prototype.Components)
|
||||
{
|
||||
cache.Add(compType, serializationManager.WriteValueAs<MappingDataNode>(comp.GetType(), comp));
|
||||
cache.Add(compType, serializationManager.WriteValueAs<MappingDataNode>(comp.Component.GetType(), comp.Component));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Robust.Shared.Console.Commands;
|
||||
|
||||
internal sealed class DumpEventTablesCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "dump_event_tables";
|
||||
public string Description => Loc.GetString("cmd-dump_event_tables-desc");
|
||||
public string Help => Loc.GetString("cmd-dump_event_tables-help");
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var entMgr = IoCManager.Resolve<EntityManager>();
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-dump_event_tables-missing-arg-entity"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[0], out var entity) || !entMgr.EntityExists(entity))
|
||||
{
|
||||
shell.WriteError(Loc.GetString("cmd-dump_event_tables-error-entity"));
|
||||
return;
|
||||
}
|
||||
|
||||
var eventBus = (EntityEventBus)entMgr.EventBus;
|
||||
|
||||
var table = eventBus._entEventTables[entity];
|
||||
foreach (var (evType, comps) in table.EventIndices)
|
||||
{
|
||||
shell.WriteLine($"{evType}:");
|
||||
|
||||
var idx = comps;
|
||||
while (idx != -1)
|
||||
{
|
||||
ref var entry = ref table.ComponentLists[idx];
|
||||
idx = entry.Next;
|
||||
|
||||
var reg = compFactory.IdxToType(entry.Component);
|
||||
shell.WriteLine($" {reg.Name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||
{
|
||||
if (args.Length == 1)
|
||||
return CompletionResult.FromHint(Loc.GetString("cmd-dump_event_tables-arg-entity"));
|
||||
|
||||
return CompletionResult.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public sealed class ByRefEventAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -38,13 +38,13 @@ namespace Robust.Shared.GameObjects
|
||||
/// 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)
|
||||
internal void LifeAddToEntity(IEntityManager entManager, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.PreAdd);
|
||||
|
||||
LifeStage = ComponentLifeStage.Adding;
|
||||
CreationTick = entManager.CurrentTick;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompAddInstance);
|
||||
entManager.EventBus.RaiseComponentEvent(this, type, CompAddInstance);
|
||||
LifeStage = ComponentLifeStage.Added;
|
||||
}
|
||||
|
||||
@@ -52,12 +52,12 @@ namespace Robust.Shared.GameObjects
|
||||
/// Increases the life stage from <see cref="ComponentLifeStage.Added" /> to <see cref="ComponentLifeStage.Initialized" />,
|
||||
/// calling <see cref="Initialize" />.
|
||||
/// </summary>
|
||||
internal void LifeInitialize(IEntityManager entManager)
|
||||
internal void LifeInitialize(IEntityManager entManager, CompIdx type)
|
||||
{
|
||||
DebugTools.Assert(LifeStage == ComponentLifeStage.Added);
|
||||
|
||||
LifeStage = ComponentLifeStage.Initializing;
|
||||
entManager.EventBus.RaiseComponentEvent(this, CompInitInstance);
|
||||
entManager.EventBus.RaiseComponentEvent(this, type, CompInitInstance);
|
||||
Initialize();
|
||||
|
||||
#if DEBUG
|
||||
@@ -299,29 +299,34 @@ namespace Robust.Shared.GameObjects
|
||||
/// The component has been added to the entity. This is the first function
|
||||
/// to be called after the component has been allocated and (optionally) deserialized.
|
||||
/// </summary>
|
||||
[ComponentEvent]
|
||||
public sealed class ComponentAdd : EntityEventArgs { }
|
||||
|
||||
/// <summary>
|
||||
/// Raised when all of the entity's other components have been added and are available,
|
||||
/// But are not necessarily initialized yet. DO NOT depend on the values of other components to be correct.
|
||||
/// </summary>
|
||||
[ComponentEvent]
|
||||
public sealed class ComponentInit : EntityEventArgs { }
|
||||
|
||||
/// <summary>
|
||||
/// Starts up a component. This is called automatically after all components are Initialized and the entity is Initialized.
|
||||
/// This can be called multiple times during the component's life, and at any time.
|
||||
/// </summary>
|
||||
[ComponentEvent]
|
||||
public sealed class ComponentStartup : EntityEventArgs { }
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the component. The is called Automatically by OnRemove. This can be called multiple times during
|
||||
/// the component's life, and at any time.
|
||||
/// </summary>
|
||||
[ComponentEvent]
|
||||
public sealed class ComponentShutdown : EntityEventArgs { }
|
||||
|
||||
/// <summary>
|
||||
/// The component has been removed from the entity. This is the last function
|
||||
/// that is called before the component is freed.
|
||||
/// </summary>
|
||||
[ComponentEvent]
|
||||
public sealed class ComponentRemove : EntityEventArgs { }
|
||||
}
|
||||
|
||||
@@ -31,10 +31,12 @@ namespace Robust.Shared.GameObjects
|
||||
public readonly struct AddedComponentEventArgs
|
||||
{
|
||||
public readonly ComponentEventArgs BaseArgs;
|
||||
public readonly CompIdx ComponentType;
|
||||
|
||||
public AddedComponentEventArgs(ComponentEventArgs baseArgs)
|
||||
public AddedComponentEventArgs(ComponentEventArgs baseArgs, CompIdx componentType)
|
||||
{
|
||||
BaseArgs = baseArgs;
|
||||
ComponentType = componentType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -279,6 +279,11 @@ namespace Robust.Shared.GameObjects
|
||||
return _typeFactory.CreateInstanceUnchecked<T>(types[typeof(T)].Type);
|
||||
}
|
||||
|
||||
public IComponent GetComponent(ComponentRegistration reg)
|
||||
{
|
||||
return (IComponent) _typeFactory.CreateInstanceUnchecked(reg.Type);
|
||||
}
|
||||
|
||||
public IComponent GetComponent(string componentName, bool ignoreCase = false)
|
||||
{
|
||||
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
|
||||
|
||||
@@ -35,7 +35,9 @@ public sealed class ComponentRegistration
|
||||
|
||||
public ValueList<CompIdx> References;
|
||||
|
||||
public ComponentRegistration(string name, Type type, CompIdx idx)
|
||||
// Internal for sandboxing.
|
||||
// Avoid content passing an instance of this to ComponentFactory to get any type they want instantiated.
|
||||
internal ComponentRegistration(string name, Type type, CompIdx idx)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
|
||||
@@ -24,10 +24,11 @@ internal sealed partial class EntityEventBus : IEventBus
|
||||
private readonly Queue<(EventSource source, object args)> _eventQueue = new();
|
||||
|
||||
// eUid -> EventType -> { CompType1, ... CompTypeN }
|
||||
private Dictionary<EntityUid, Dictionary<Type, HashSet<CompIdx>>> _entEventTables = new();
|
||||
// See EventTable declaration for layout details
|
||||
internal Dictionary<EntityUid, EventTable> _entEventTables = new();
|
||||
|
||||
// CompType -> EventType -> Handler
|
||||
private Dictionary<Type, DirectedRegistration>?[] _entSubscriptions =
|
||||
internal Dictionary<Type, DirectedRegistration>?[] _entSubscriptions =
|
||||
Array.Empty<Dictionary<Type, DirectedRegistration>?>();
|
||||
|
||||
// EventType -> { CompType1, ... CompType N }
|
||||
@@ -72,6 +73,10 @@ internal sealed partial class EntityEventBus : IEventBus
|
||||
/// </summary>
|
||||
private sealed class EventData
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ComponentEventAttribute"/> set?
|
||||
/// </summary>
|
||||
public bool ComponentEvent;
|
||||
public bool IsOrdered;
|
||||
public bool OrderingUpToDate;
|
||||
public ValueList<BroadcastRegistration> BroadcastRegistrations;
|
||||
@@ -81,7 +86,7 @@ internal sealed partial class EntityEventBus : IEventBus
|
||||
|
||||
// It should always be cast to/from with Unsafe.As<,>
|
||||
|
||||
private readonly struct Unit
|
||||
internal readonly struct Unit
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -64,6 +65,20 @@ namespace Robust.Shared.GameObjects
|
||||
internal void RaiseComponentEvent<TEvent>(IComponent component, TEvent args)
|
||||
where TEvent : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event directly to a specific component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This has a very specific purpose, and has massive potential to be abused.
|
||||
/// DO NOT EXPOSE THIS TO CONTENT.
|
||||
/// </remarks>
|
||||
/// <typeparam name="TEvent">Event to dispatch.</typeparam>
|
||||
/// <param name="component">Component receiving the event.</param>
|
||||
/// <param name="idx">Type of the component, for faster lookups.</param>
|
||||
/// <param name="args">Event arguments for the event.</param>
|
||||
internal void RaiseComponentEvent<TEvent>(IComponent component, CompIdx idx, TEvent args)
|
||||
where TEvent : notnull;
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches an event directly to a specific component, by-ref.
|
||||
/// </summary>
|
||||
@@ -82,7 +97,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
internal partial class EntityEventBus : IDisposable
|
||||
{
|
||||
private delegate void DirectedEventHandler(EntityUid uid, IComponent comp, ref Unit args);
|
||||
internal delegate void DirectedEventHandler(EntityUid uid, IComponent comp, ref Unit args);
|
||||
|
||||
private delegate void DirectedEventHandler<TEvent>(EntityUid uid, IComponent comp, ref TEvent args)
|
||||
where TEvent : notnull;
|
||||
@@ -116,7 +131,24 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
ref var unitRef = ref Unsafe.As<TEvent, Unit>(ref args);
|
||||
|
||||
DispatchComponent<TEvent>(component.Owner, component, ref unitRef, false);
|
||||
DispatchComponent<TEvent>(
|
||||
component.Owner,
|
||||
component,
|
||||
CompIdx.Index(component.GetType()),
|
||||
ref unitRef,
|
||||
false);
|
||||
}
|
||||
|
||||
void IDirectedEventBus.RaiseComponentEvent<TEvent>(IComponent component, CompIdx type, TEvent args)
|
||||
{
|
||||
ref var unitRef = ref Unsafe.As<TEvent, Unit>(ref args);
|
||||
|
||||
DispatchComponent<TEvent>(
|
||||
component.Owner,
|
||||
component,
|
||||
type,
|
||||
ref unitRef,
|
||||
false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -124,7 +156,12 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
ref var unitRef = ref Unsafe.As<TEvent, Unit>(ref args);
|
||||
|
||||
DispatchComponent<TEvent>(component.Owner, component, ref unitRef, true);
|
||||
DispatchComponent<TEvent>(
|
||||
component.Owner,
|
||||
component,
|
||||
CompIdx.Index(component.GetType()),
|
||||
ref unitRef,
|
||||
true);
|
||||
}
|
||||
|
||||
public void OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare()
|
||||
@@ -290,14 +327,14 @@ namespace Robust.Shared.GameObjects
|
||||
EntRemoveEntity(e);
|
||||
}
|
||||
|
||||
public void OnComponentAdded(AddedComponentEventArgs e)
|
||||
public void OnComponentAdded(in AddedComponentEventArgs e)
|
||||
{
|
||||
_subscriptionLock = true;
|
||||
|
||||
EntAddComponent(e.BaseArgs.Owner, CompIdx.Index(e.BaseArgs.Component.GetType()));
|
||||
EntAddComponent(e.BaseArgs.Owner, e.ComponentType);
|
||||
}
|
||||
|
||||
public void OnComponentRemoved(RemovedComponentEventArgs e)
|
||||
public void OnComponentRemoved(in RemovedComponentEventArgs e)
|
||||
{
|
||||
EntRemoveComponent(e.BaseArgs.Owner, CompIdx.Index(e.BaseArgs.Component.GetType()));
|
||||
}
|
||||
@@ -334,7 +371,8 @@ namespace Robust.Shared.GameObjects
|
||||
var invSubs = _entSubscriptionsInv.GetOrNew(eventType);
|
||||
invSubs.Add(compType);
|
||||
|
||||
RegisterCommon(eventType, registration.Ordering, out _);
|
||||
RegisterCommon(eventType, registration.Ordering, out var data);
|
||||
data.ComponentEvent = eventType.HasCustomAttribute<ComponentEventAttribute>();
|
||||
}
|
||||
|
||||
private void EntSubscribe<TEvent>(
|
||||
@@ -375,7 +413,7 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
// odds are at least 1 component will subscribe to an event on the entity, so just
|
||||
// preallocate the table now. Dispatch does not need to check this later.
|
||||
_entEventTables.Add(euid, new Dictionary<Type, HashSet<CompIdx>>());
|
||||
_entEventTables.Add(euid, new EventTable());
|
||||
}
|
||||
|
||||
private void EntRemoveEntity(EntityUid euid)
|
||||
@@ -392,19 +430,63 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var compSubs = _entSubscriptions[type.Value]!;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
foreach (var (evType, _) in compSubs)
|
||||
{
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
{
|
||||
subscribedComps = new HashSet<CompIdx>();
|
||||
eventTable.Add(kvSub.Key, subscribedComps);
|
||||
}
|
||||
// Skip adding this to significantly reduce memory use and GC noise on entity create.
|
||||
if (_eventData[evType].ComponentEvent)
|
||||
continue;
|
||||
|
||||
subscribedComps.Add(type);
|
||||
if (eventTable.Free < 0)
|
||||
GrowEventTable(eventTable);
|
||||
|
||||
DebugTools.Assert(eventTable.Free >= 0);
|
||||
|
||||
ref var eventStartIdx = ref CollectionsMarshal.GetValueRefOrAddDefault(
|
||||
eventTable.EventIndices,
|
||||
evType,
|
||||
out var exists);
|
||||
|
||||
// Allocate linked list entry by popping free list.
|
||||
var entryIdx = eventTable.Free;
|
||||
ref var entry = ref eventTable.ComponentLists[entryIdx];
|
||||
eventTable.Free = entry.Next;
|
||||
|
||||
// Set it up
|
||||
entry.Component = type;
|
||||
entry.Next = exists ? eventStartIdx : -1;
|
||||
|
||||
// Assign new list entry to EventIndices dictionary.
|
||||
eventStartIdx = entryIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GrowEventTable(EventTable table)
|
||||
{
|
||||
var newSize = table.ComponentLists.Length * 2;
|
||||
|
||||
var oldArray = table.ComponentLists;
|
||||
var newArray = GC.AllocateUninitializedArray<EventTableListEntry>(newSize);
|
||||
Array.Copy(oldArray, newArray, oldArray.Length);
|
||||
|
||||
InitEventTableFreeList(newArray, newArray.Length, oldArray.Length);
|
||||
|
||||
table.Free = oldArray.Length;
|
||||
table.ComponentLists = newArray;
|
||||
}
|
||||
|
||||
private static void InitEventTableFreeList(EventTableListEntry[] entries, int end, int start)
|
||||
{
|
||||
var lastFree = -1;
|
||||
for (var i = end - 1; i >= start; i--)
|
||||
{
|
||||
ref var entry = ref entries[i];
|
||||
entry.Component = default;
|
||||
entry.Next = lastFree;
|
||||
lastFree = i;
|
||||
}
|
||||
}
|
||||
|
||||
private void EntRemoveComponent(EntityUid euid, CompIdx compType)
|
||||
{
|
||||
var eventTable = _entEventTables[euid];
|
||||
@@ -414,12 +496,44 @@ namespace Robust.Shared.GameObjects
|
||||
{
|
||||
var compSubs = _entSubscriptions[type.Value]!;
|
||||
|
||||
foreach (var kvSub in compSubs)
|
||||
foreach (var (evType, _) in compSubs)
|
||||
{
|
||||
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
|
||||
return;
|
||||
ref var dictIdx = ref CollectionsMarshal.GetValueRefOrNullRef(eventTable.EventIndices, evType);
|
||||
if (Unsafe.IsNullRef(ref dictIdx))
|
||||
continue;
|
||||
|
||||
subscribedComps.Remove(type);
|
||||
ref var updateNext = ref dictIdx;
|
||||
|
||||
// Go over linked list to find index of component.
|
||||
var entryIdx = dictIdx;
|
||||
ref var entry = ref Unsafe.NullRef<EventTableListEntry>();
|
||||
while (true)
|
||||
{
|
||||
entry = ref eventTable.ComponentLists[entryIdx];
|
||||
if (entry.Component == type)
|
||||
{
|
||||
// Found
|
||||
break;
|
||||
}
|
||||
|
||||
entryIdx = entry.Next;
|
||||
updateNext = ref entry.Next;
|
||||
}
|
||||
|
||||
if (entry.Next == -1 && Unsafe.AreSame(ref dictIdx, ref updateNext))
|
||||
{
|
||||
// Last entry for this event type, remove from dict.
|
||||
eventTable.EventIndices.Remove(evType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rewrite previous index to point to next in chain.
|
||||
updateNext = entry.Next;
|
||||
}
|
||||
|
||||
// Push entry back onto free list.
|
||||
entry.Next = eventTable.Free;
|
||||
eventTable.Free = entryIdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,9 +543,8 @@ namespace Robust.Shared.GameObjects
|
||||
if (!EntTryGetSubscriptions(eventType, euid, out var enumerator))
|
||||
return;
|
||||
|
||||
while (enumerator.MoveNext(out var tuple))
|
||||
while (enumerator.MoveNext(out var component, out var reg))
|
||||
{
|
||||
var (component, reg) = tuple.Value;
|
||||
if (reg.ReferenceEvent != dispatchByReference)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
@@ -445,32 +558,27 @@ namespace Robust.Shared.GameObjects
|
||||
ref ValueList<OrderedEventDispatch> found,
|
||||
bool byRef)
|
||||
{
|
||||
var eventTable = _entEventTables[euid];
|
||||
|
||||
if (!eventTable.TryGetValue(eventType, out var subscribedComps))
|
||||
if (!EntTryGetSubscriptions(eventType, euid, out var enumerator))
|
||||
return;
|
||||
|
||||
foreach (var compType in subscribedComps)
|
||||
while (enumerator.MoveNext(out var component, out var reg))
|
||||
{
|
||||
var compSubs = _entSubscriptions[compType.Value]!;
|
||||
|
||||
if (!compSubs.TryGetValue(eventType, out var reg))
|
||||
return;
|
||||
|
||||
if (reg.ReferenceEvent != byRef)
|
||||
ThrowByRefMisMatch();
|
||||
|
||||
var component = _entMan.GetComponent(euid, compType);
|
||||
|
||||
found.Add(new OrderedEventDispatch((ref Unit ev) => reg.Handler(euid, component, ref ev), reg.Order));
|
||||
}
|
||||
}
|
||||
|
||||
private void DispatchComponent<TEvent>(EntityUid euid, IComponent component, ref Unit args,
|
||||
private void DispatchComponent<TEvent>(
|
||||
EntityUid euid,
|
||||
IComponent component,
|
||||
CompIdx baseType,
|
||||
ref Unit args,
|
||||
bool dispatchByReference)
|
||||
where TEvent : notnull
|
||||
{
|
||||
var enumerator = EntGetReferences(CompIdx.Index(component.GetType()));
|
||||
var enumerator = EntGetReferences(baseType);
|
||||
while (enumerator.MoveNext(out var type))
|
||||
{
|
||||
var compSubs = _entSubscriptions[type.Value]!;
|
||||
@@ -505,13 +613,13 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
|
||||
// No subscriptions to this event type, return null.
|
||||
if (!eventTable.TryGetValue(eventType, out var subscribedComps))
|
||||
if (!eventTable.EventIndices.TryGetValue(eventType, out var startEntry))
|
||||
{
|
||||
enumerator = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
enumerator = new(eventType, subscribedComps.GetEnumerator(), _entSubscriptions, euid, _entMan);
|
||||
enumerator = new(eventType, startEntry, eventTable.ComponentLists, _entSubscriptions, euid, _entMan);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -581,58 +689,60 @@ namespace Robust.Shared.GameObjects
|
||||
}
|
||||
}
|
||||
|
||||
private struct SubscriptionsEnumerator : IDisposable
|
||||
private struct SubscriptionsEnumerator
|
||||
{
|
||||
private readonly Type _eventType;
|
||||
private HashSet<CompIdx>.Enumerator _enumerator;
|
||||
private readonly Dictionary<Type, DirectedRegistration>?[] _subscriptions;
|
||||
private readonly EntityUid _uid;
|
||||
private readonly Dictionary<Type, DirectedRegistration>?[] _subscriptions;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly EventTableListEntry[] _list;
|
||||
private int _idx;
|
||||
|
||||
public SubscriptionsEnumerator(
|
||||
Type eventType,
|
||||
HashSet<CompIdx>.Enumerator enumerator,
|
||||
int startEntry,
|
||||
EventTableListEntry[] list,
|
||||
Dictionary<Type, DirectedRegistration>?[] subscriptions,
|
||||
EntityUid uid,
|
||||
IEntityManager entityManager)
|
||||
{
|
||||
_eventType = eventType;
|
||||
_enumerator = enumerator;
|
||||
_list = list;
|
||||
_subscriptions = subscriptions;
|
||||
_idx = startEntry;
|
||||
_entityManager = entityManager;
|
||||
_uid = uid;
|
||||
}
|
||||
|
||||
public bool MoveNext(
|
||||
[NotNullWhen(true)] out (IComponent Component, DirectedRegistration Registration)? tuple)
|
||||
[NotNullWhen(true)] out IComponent? component,
|
||||
[NotNullWhen(true)] out DirectedRegistration? registration)
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
|
||||
if (!_enumerator.MoveNext())
|
||||
if (_idx == -1)
|
||||
{
|
||||
tuple = null;
|
||||
component = null;
|
||||
registration = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var compType = _enumerator.Current;
|
||||
ref var entry = ref _list[_idx];
|
||||
_idx = entry.Next;
|
||||
|
||||
var compType = entry.Component;
|
||||
var compSubs = _subscriptions[compType.Value]!;
|
||||
|
||||
if (!compSubs.TryGetValue(_eventType, out var registration))
|
||||
if (!compSubs.TryGetValue(_eventType, out registration))
|
||||
{
|
||||
tuple = null;
|
||||
component = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
tuple = (_entityManager.GetComponent(_uid, compType), registration);
|
||||
component = _entityManager.GetComponent(_uid, compType);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_enumerator.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DirectedRegistration : OrderedRegistration
|
||||
internal sealed class DirectedRegistration : OrderedRegistration
|
||||
{
|
||||
public readonly Delegate Original;
|
||||
public readonly DirectedEventHandler Handler;
|
||||
@@ -654,6 +764,32 @@ namespace Robust.Shared.GameObjects
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class EventTable
|
||||
{
|
||||
private const int InitialListSize = 8;
|
||||
|
||||
// Event -> { Comp, Comp, ... } is stored in a simple linked list.
|
||||
// EventIndices contains indices into ComponentLists where linked list nodes start.
|
||||
// Free contains the first free linked list node, or -1 if there is none.
|
||||
// Free nodes form their own linked list.
|
||||
// ComponentList is the actual region of memory containing linked list nodes.
|
||||
public readonly Dictionary<Type, int> EventIndices = new();
|
||||
public int Free;
|
||||
public EventTableListEntry[] ComponentLists = new EventTableListEntry[InitialListSize];
|
||||
|
||||
public EventTable()
|
||||
{
|
||||
InitEventTableFreeList(ComponentLists, ComponentLists.Length, 0);
|
||||
Free = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal struct EventTableListEntry
|
||||
{
|
||||
public int Next;
|
||||
public CompIdx Component;
|
||||
}
|
||||
}
|
||||
|
||||
public delegate void ComponentEventHandler<in TComp, in TEvent>(EntityUid uid, TComp component, TEvent args)
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Robust.Shared.GameObjects
|
||||
// Still, it's a single .Sort() now for those instead of the whole topological short shebang.
|
||||
}
|
||||
|
||||
private sealed record OrderingData(Type OrderType, Type[] Before, Type[] After)
|
||||
internal sealed record OrderingData(Type OrderType, Type[] Before, Type[] After)
|
||||
{
|
||||
public bool Equals(OrderingData? other)
|
||||
{
|
||||
@@ -174,7 +174,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <summary>
|
||||
/// Base type for directed and broadcast subscriptions. Contains ordering data.
|
||||
/// </summary>
|
||||
private abstract class OrderedRegistration
|
||||
internal abstract class OrderedRegistration
|
||||
{
|
||||
public int Order;
|
||||
public readonly OrderingData? Ordering;
|
||||
|
||||
@@ -114,20 +114,20 @@ namespace Robust.Shared.GameObjects
|
||||
// Init transform first, we always have it.
|
||||
var transform = GetComponent<TransformComponent>(uid);
|
||||
if (transform.LifeStage < ComponentLifeStage.Initialized)
|
||||
transform.LifeInitialize(this);
|
||||
transform.LifeInitialize(this, CompIdx.Index<TransformComponent>());
|
||||
|
||||
// Init physics second if it exists.
|
||||
if (TryGetComponent<PhysicsComponent>(uid, out var phys)
|
||||
&& phys.LifeStage < ComponentLifeStage.Initialized)
|
||||
{
|
||||
phys.LifeInitialize(this);
|
||||
phys.LifeInitialize(this, CompIdx.Index<PhysicsComponent>());
|
||||
}
|
||||
|
||||
// Do rest of components.
|
||||
foreach (var comp in comps)
|
||||
{
|
||||
if (comp is { LifeStage: < ComponentLifeStage.Initialized })
|
||||
comp.LifeInitialize(this);
|
||||
comp.LifeInitialize(this, CompIdx.Index(comp.GetType()));
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@@ -190,12 +190,14 @@ namespace Robust.Shared.GameObjects
|
||||
where T : Component
|
||||
{
|
||||
private readonly IEntityManager _entMan;
|
||||
public readonly CompIdx CompType;
|
||||
public readonly T Comp;
|
||||
|
||||
public CompInitializeHandle(IEntityManager entityManager, T comp)
|
||||
public CompInitializeHandle(IEntityManager entityManager, T comp, CompIdx compType)
|
||||
{
|
||||
_entMan = entityManager;
|
||||
Comp = comp;
|
||||
CompType = compType;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -206,7 +208,7 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
if (!Comp.Initialized)
|
||||
Comp.LifeInitialize(_entMan);
|
||||
Comp.LifeInitialize(_entMan, CompType);
|
||||
|
||||
if (metadata.EntityInitialized && !Comp.Running)
|
||||
Comp.LifeStartup(_entMan);
|
||||
@@ -221,7 +223,8 @@ namespace Robust.Shared.GameObjects
|
||||
/// <inheritdoc />
|
||||
public CompInitializeHandle<T> AddComponentUninitialized<T>(EntityUid uid) where T : Component, new()
|
||||
{
|
||||
var newComponent = _componentFactory.GetComponent<T>();
|
||||
var reg = _componentFactory.GetRegistration<T>();
|
||||
var newComponent = (T)_componentFactory.GetComponent(reg);
|
||||
newComponent.Owner = uid;
|
||||
|
||||
if (!uid.IsValid() || !EntityExists(uid))
|
||||
@@ -233,7 +236,7 @@ namespace Robust.Shared.GameObjects
|
||||
|
||||
AddComponentInternal(uid, newComponent, false, true);
|
||||
|
||||
return new CompInitializeHandle<T>(this, newComponent);
|
||||
return new CompInitializeHandle<T>(this, newComponent, reg.Idx);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -293,11 +296,11 @@ namespace Robust.Shared.GameObjects
|
||||
Dirty(component);
|
||||
}
|
||||
|
||||
var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid));
|
||||
var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg.Idx);
|
||||
ComponentAdded?.Invoke(eventArgs);
|
||||
_eventBus.OnComponentAdded(eventArgs);
|
||||
|
||||
component.LifeAddToEntity(this);
|
||||
component.LifeAddToEntity(this, reg.Idx);
|
||||
|
||||
if (skipInit)
|
||||
return;
|
||||
@@ -307,7 +310,7 @@ namespace Robust.Shared.GameObjects
|
||||
if (!metadata.EntityInitialized && !metadata.EntityInitializing)
|
||||
return;
|
||||
|
||||
component.LifeInitialize(this);
|
||||
component.LifeInitialize(this, reg.Idx);
|
||||
|
||||
if (metadata.EntityInitialized)
|
||||
component.LifeStartup(this);
|
||||
@@ -707,6 +710,22 @@ namespace Robust.Shared.GameObjects
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetComponent(EntityUid uid, CompIdx type, [NotNullWhen(true)] out IComponent? component)
|
||||
{
|
||||
var dict = _entTraitArray[type.Value];
|
||||
if (dict.TryGetValue(uid, out var comp))
|
||||
{
|
||||
if (!comp.Deleted)
|
||||
{
|
||||
component = comp;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
component = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetComponent([NotNullWhen(true)] EntityUid? uid, Type type,
|
||||
[NotNullWhen(true)] out IComponent? component)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.GameObjects;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public sealed class ByRefEventAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that an eventbus event should only ever be raised through <see cref="IDirectedEventBus.RaiseComponentEvent{TEvent}(IComponent, TEvent)"/>.
|
||||
/// This allows extra optimizations.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
internal sealed class ComponentEventAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -117,6 +117,12 @@ namespace Robust.Shared.GameObjects
|
||||
/// </exception>
|
||||
T GetComponent<T>() where T : IComponent, new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new component instantiated from the specified component registration.
|
||||
/// </summary>
|
||||
/// <returns>A Component</returns>
|
||||
IComponent GetComponent(ComponentRegistration reg);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a new component instantiated of the specified <see cref="IComponent.Name"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -241,6 +241,15 @@ namespace Robust.Shared.GameObjects
|
||||
/// <returns>If the component existed in the entity.</returns>
|
||||
bool TryGetComponent(EntityUid uid, Type type, [NotNullWhen(true)] out IComponent? component);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
/// </summary>
|
||||
/// <param name="uid">Entity UID to check.</param>
|
||||
/// <param name="type">A trait or component type to check for.</param>
|
||||
/// <param name="component">Component of the specified type (if exists).</param>
|
||||
/// <returns>If the component existed in the entity.</returns>
|
||||
bool TryGetComponent([NotNullWhen(true)] EntityUid uid, CompIdx type, [NotNullWhen(true)] out IComponent? component);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the component of a specific type.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,7 @@ using Robust.Shared.Players;
|
||||
|
||||
namespace Robust.Shared.GameStates
|
||||
{
|
||||
[ByRefEvent]
|
||||
[ByRefEvent, ComponentEvent]
|
||||
public readonly struct ComponentHandleState
|
||||
{
|
||||
public ComponentState? Current { get; }
|
||||
@@ -19,7 +19,7 @@ namespace Robust.Shared.GameStates
|
||||
/// <summary>
|
||||
/// Component event for getting the component state for a specific player.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
[ByRefEvent, ComponentEvent]
|
||||
public struct ComponentGetState
|
||||
{
|
||||
/// <summary>
|
||||
@@ -28,7 +28,7 @@ namespace Robust.Shared.GameStates
|
||||
public ComponentState? State { get; set; }
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
[ByRefEvent, ComponentEvent]
|
||||
public struct ComponentGetStateAttemptEvent
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -154,9 +154,9 @@ namespace Robust.Shared.Prototypes
|
||||
public EntityPrototype()
|
||||
{
|
||||
// Everybody gets a transform component!
|
||||
Components.Add("Transform", new TransformComponent());
|
||||
Components.Add("Transform", new ComponentRegistryEntry(new TransformComponent(), new MappingDataNode()));
|
||||
// And a metadata component too!
|
||||
Components.Add("MetaData", new MetaDataComponent());
|
||||
Components.Add("MetaData", new ComponentRegistryEntry(new MetaDataComponent(), new MappingDataNode()));
|
||||
}
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
@@ -185,7 +185,7 @@ namespace Robust.Shared.Prototypes
|
||||
|
||||
// There are no duplicate component names
|
||||
// TODO Sanity check with names being in an attribute of the type instead
|
||||
component = (T) componentUnCast;
|
||||
component = (T) componentUnCast.Component;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -262,25 +262,12 @@ namespace Robust.Shared.Prototypes
|
||||
{
|
||||
prototypeManager.TryGetMapping(typeof(EntityPrototype), prototype.ID, out var prototypeData);
|
||||
|
||||
foreach (var (name, _) in prototype.Components)
|
||||
foreach (var (name, entry) in prototype.Components)
|
||||
{
|
||||
MappingDataNode? fullData = null;
|
||||
if (prototypeData != null && prototypeData.TryGet<SequenceDataNode>("components", out var compList))
|
||||
{
|
||||
foreach (var data in compList)
|
||||
{
|
||||
if(data is not MappingDataNode mappingDataNode || !mappingDataNode.TryGet<ValueDataNode>("type", out var typeNode) || typeNode.Value != name ) continue;
|
||||
fullData = mappingDataNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fullData ??= new MappingDataNode();
|
||||
var fullData = entry.Mapping;
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
fullData = context.GetComponentData(name, fullData);
|
||||
}
|
||||
|
||||
EnsureCompExistsAndDeserialize(entity, factory, entityManager, serManager, name, fullData, context as ISerializationContext);
|
||||
}
|
||||
@@ -310,11 +297,12 @@ namespace Robust.Shared.Prototypes
|
||||
IEntityManager entityManager,
|
||||
ISerializationManager serManager,
|
||||
string compName,
|
||||
MappingDataNode data, ISerializationContext? context)
|
||||
MappingDataNode data,
|
||||
ISerializationContext? context)
|
||||
{
|
||||
var compType = factory.GetRegistration(compName).Type;
|
||||
var compReg = factory.GetRegistration(compName);
|
||||
|
||||
if (!entityManager.TryGetComponent(entity, compType, out var component))
|
||||
if (!entityManager.TryGetComponent(entity, compReg.Idx, out var component))
|
||||
{
|
||||
var newComponent = (Component) factory.GetComponent(compName);
|
||||
newComponent.Owner = entity;
|
||||
@@ -323,7 +311,7 @@ namespace Robust.Shared.Prototypes
|
||||
}
|
||||
|
||||
// TODO use this value to support struct components
|
||||
serManager.Read(compType, data, context, value: component);
|
||||
serManager.Read(compReg.Type, data, context, value: component);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
@@ -331,17 +319,30 @@ namespace Robust.Shared.Prototypes
|
||||
return $"EntityPrototype({ID})";
|
||||
}
|
||||
|
||||
public sealed class ComponentRegistry : Dictionary<string, IComponent>
|
||||
public sealed class ComponentRegistry : Dictionary<string, ComponentRegistryEntry>
|
||||
{
|
||||
public ComponentRegistry()
|
||||
{
|
||||
}
|
||||
|
||||
public ComponentRegistry(Dictionary<string, IComponent> components) : base(components)
|
||||
public ComponentRegistry(Dictionary<string, ComponentRegistryEntry> components) : base(components)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ComponentRegistryEntry
|
||||
{
|
||||
public readonly IComponent Component;
|
||||
// Mapping is just a quick reference to speed up entity creation.
|
||||
public readonly MappingDataNode Mapping;
|
||||
|
||||
public ComponentRegistryEntry(IComponent component, MappingDataNode mapping)
|
||||
{
|
||||
Component = component;
|
||||
Mapping = mapping;
|
||||
}
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
public sealed class EntityPlacementProperties
|
||||
{
|
||||
|
||||
@@ -716,9 +716,7 @@ namespace Robust.Shared.Prototypes
|
||||
|
||||
public bool TryGetMapping(Type type, string id, [NotNullWhen(true)] out MappingDataNode? mappings)
|
||||
{
|
||||
var ret = _prototypeResults[type].TryGetValue(id, out var originalMappings);
|
||||
mappings = originalMappings?.Copy();
|
||||
return ret;
|
||||
return _prototypeResults[type].TryGetValue(id, out mappings);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
+7
-2
@@ -60,7 +60,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations
|
||||
var type = factory.GetRegistration(compType).Type;
|
||||
var read = (IComponent)serializationManager.Read(type, copy, skipHook: skipHook)!;
|
||||
|
||||
components[compType] = read;
|
||||
components[compType] = new ComponentRegistryEntry(read, copy);
|
||||
}
|
||||
|
||||
var referenceTypes = new List<CompIdx>();
|
||||
@@ -153,7 +153,12 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations
|
||||
var compSequence = new SequenceDataNode();
|
||||
foreach (var (type, component) in value)
|
||||
{
|
||||
var node = serializationManager.WriteValue(component.GetType(), component, alwaysWrite, context);
|
||||
var node = serializationManager.WriteValue(
|
||||
component.Component.GetType(),
|
||||
component.Component,
|
||||
alwaysWrite,
|
||||
context);
|
||||
|
||||
if (node is not MappingDataNode mapping) throw new InvalidNodeTypeException();
|
||||
|
||||
mapping.Add("type", new ValueDataNode(type));
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid)));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), CompIdx.Index<MetaDataComponent>()));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
@@ -98,7 +98,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid)));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), CompIdx.Index<MetaDataComponent>()));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
@@ -151,7 +151,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
|
||||
// add a component to the system
|
||||
entManMock.Raise(m => m.EntityAdded += null, entUid);
|
||||
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid)));
|
||||
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), CompIdx.Index<MetaDataComponent>()));
|
||||
|
||||
// Raise
|
||||
((IEventBus)bus).RaiseComponentEvent(compInstance, new ComponentInit());
|
||||
@@ -227,9 +227,9 @@ namespace Robust.UnitTesting.Shared.GameObjects
|
||||
|
||||
// add a component to the system
|
||||
bus.OnEntityAdded(entUid);
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid)));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid)));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid)));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), CompIdx.Index<OrderAComponent>()));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), CompIdx.Index<OrderBComponent>()));
|
||||
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid), CompIdx.Index<OrderCComponent>()));
|
||||
|
||||
// Raise
|
||||
var evntArgs = new TestEvent(5);
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
|
||||
var prototype = manager.Index<EntityPrototype>("wrench");
|
||||
Assert.That(prototype.Name, Is.EqualTo("Not a wrench. Tricked!"));
|
||||
|
||||
var mapping = prototype.Components["TestBasicPrototype"] as TestBasicPrototypeComponent;
|
||||
var mapping = prototype.Components["TestBasicPrototype"].Component as TestBasicPrototypeComponent;
|
||||
Assert.That(mapping!.Foo, Is.EqualTo("bar!"));
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
|
||||
Assert.That(prototype.Components, Contains.Key("PointLight"));
|
||||
});
|
||||
|
||||
var componentData = prototype.Components["PointLight"] as PointLightComponent;
|
||||
var componentData = prototype.Components["PointLight"].Component as PointLightComponent;
|
||||
|
||||
Assert.That(componentData!.NetSyncEnabled, Is.EqualTo(false));
|
||||
}
|
||||
@@ -66,7 +66,7 @@ namespace Robust.UnitTesting.Shared.Prototypes
|
||||
var prototype = manager.Index<EntityPrototype>("yamltester");
|
||||
Assert.That(prototype.Components, Contains.Key("TestBasicPrototype"));
|
||||
|
||||
var componentData = prototype.Components["TestBasicPrototype"] as TestBasicPrototypeComponent;
|
||||
var componentData = prototype.Components["TestBasicPrototype"].Component as TestBasicPrototypeComponent;
|
||||
|
||||
Assert.NotNull(componentData);
|
||||
Assert.That(componentData!.Str, Is.EqualTo("hi!"));
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers
|
||||
public void SerializationTest()
|
||||
{
|
||||
var component = new TestComponent();
|
||||
var registry = new ComponentRegistry {{"Test", component}};
|
||||
var registry = new ComponentRegistry {{"Test", new ComponentRegistryEntry(component, new MappingDataNode())}};
|
||||
var node = Serialization.WriteValueAs<SequenceDataNode>(registry);
|
||||
|
||||
Assert.That(node.Sequence.Count, Is.EqualTo(1));
|
||||
@@ -52,7 +52,7 @@ namespace Robust.UnitTesting.Shared.Serialization.TypeSerializers
|
||||
|
||||
Assert.That(deserializedRegistry.Count, Is.EqualTo(1));
|
||||
Assert.That(deserializedRegistry.ContainsKey("Test"));
|
||||
Assert.IsInstanceOf<TestComponent>(deserializedRegistry["Test"]);
|
||||
Assert.IsInstanceOf<TestComponent>(deserializedRegistry["Test"].Component);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user