Minor performance improvements (#2860)

This commit is contained in:
Pieter-Jan Briers
2022-05-28 15:28:08 +02:00
committed by GitHub
parent 72d4d50788
commit bf1cc8fd7c
26 changed files with 855 additions and 442 deletions
@@ -0,0 +1,225 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using BenchmarkDotNet.Attributes;
using Robust.Shared.GameObjects;
namespace Robust.Benchmarks.EntityManager;
public class ComponentIndexBenchmark
{
// Just a bunch of types to bloat the test lists.
private readonly CompIndexFetcher _compIndexFetcherDirect;
private readonly IFetcher _compIndexFetcher;
private readonly DictFetcher _dictFetcherDirect;
private readonly IFetcher _dictFetcher;
public ComponentIndexBenchmark()
{
_compIndexFetcherDirect = new CompIndexFetcher();
_compIndexFetcher = _compIndexFetcherDirect;
_dictFetcherDirect = new DictFetcher();
_dictFetcher = _dictFetcherDirect;
}
[GlobalSetup]
public void Setup()
{
var types = typeof(ComponentIndexBenchmark)
.GetNestedTypes(BindingFlags.NonPublic)
.Where(t => t.Name.StartsWith("TestType"))
.ToArray();
_compIndexFetcher.Init(types);
_dictFetcher.Init(types);
}
[Benchmark]
public int BenchCompIndex() => _compIndexFetcher.Get<TestType50>();
[Benchmark]
public int BenchDict() => _dictFetcher.Get<TestType50>();
[Benchmark]
public int BenchCompIndexDirect() => _compIndexFetcherDirect.Get<TestType50>();
[Benchmark]
public int BenchDictDirect() => _dictFetcherDirect.Get<TestType50>();
private static CompIdx ArrayIndexFor<T>() => CompArrayIndex<T>.Idx;
private static int _compIndexMaster = -1;
private static class CompArrayIndex<T>
{
// ReSharper disable once StaticMemberInGenericType
public static readonly CompIdx Idx = new(Interlocked.Increment(ref _compIndexMaster));
}
private static CompIdx GetCompIdIndex(Type type)
{
return (CompIdx)typeof(CompArrayIndex<>)
.MakeGenericType(type)
.GetField(nameof(CompArrayIndex<int>.Idx), BindingFlags.Static | BindingFlags.Public)!
.GetValue(null)!;
}
private interface IFetcher
{
void Init(Type[] types);
int Get<T>();
}
private sealed class CompIndexFetcher : IFetcher
{
private int[] _values = Array.Empty<int>();
public void Init(Type[] types)
{
var max = types.Max(t => GetCompIdIndex(t).Value);
_values = new int[max + 1];
var i = 0;
foreach (var type in types)
{
_values[GetCompIdIndex(type).Value] = i++;
}
}
public int Get<T>()
{
return _values[CompArrayIndex<T>.Idx.Value];
}
}
private sealed class DictFetcher : IFetcher
{
private readonly Dictionary<Type, int> _values = new();
public void Init(Type[] types)
{
var i = 0;
foreach (var type in types)
{
_values[type] = i++;
}
}
public int Get<T>()
{
return _values[typeof(T)];
}
}
// Just a bunch of types to pad the size of the arrays and such.
// @formatter:off
// ReSharper disable UnusedType.Local
private sealed class TestType1{}
private sealed class TestType2{}
private sealed class TestType3{}
private sealed class TestType4{}
private sealed class TestType5{}
private sealed class TestType6{}
private sealed class TestType7{}
private sealed class TestType8{}
private sealed class TestType9{}
private sealed class TestType10{}
private sealed class TestType11{}
private sealed class TestType12{}
private sealed class TestType13{}
private sealed class TestType14{}
private sealed class TestType15{}
private sealed class TestType16{}
private sealed class TestType17{}
private sealed class TestType18{}
private sealed class TestType19{}
private sealed class TestType20{}
private sealed class TestType21{}
private sealed class TestType22{}
private sealed class TestType23{}
private sealed class TestType24{}
private sealed class TestType25{}
private sealed class TestType26{}
private sealed class TestType27{}
private sealed class TestType28{}
private sealed class TestType29{}
private sealed class TestType30{}
private sealed class TestType31{}
private sealed class TestType32{}
private sealed class TestType33{}
private sealed class TestType34{}
private sealed class TestType35{}
private sealed class TestType36{}
private sealed class TestType37{}
private sealed class TestType38{}
private sealed class TestType39{}
private sealed class TestType40{}
private sealed class TestType41{}
private sealed class TestType42{}
private sealed class TestType43{}
private sealed class TestType44{}
private sealed class TestType45{}
private sealed class TestType46{}
private sealed class TestType47{}
private sealed class TestType48{}
private sealed class TestType49{}
private sealed class TestType50{}
private sealed class TestType51{}
private sealed class TestType52{}
private sealed class TestType53{}
private sealed class TestType54{}
private sealed class TestType55{}
private sealed class TestType56{}
private sealed class TestType57{}
private sealed class TestType58{}
private sealed class TestType59{}
private sealed class TestType60{}
private sealed class TestType61{}
private sealed class TestType62{}
private sealed class TestType63{}
private sealed class TestType64{}
private sealed class TestType65{}
private sealed class TestType66{}
private sealed class TestType67{}
private sealed class TestType68{}
private sealed class TestType69{}
private sealed class TestType70{}
private sealed class TestType71{}
private sealed class TestType72{}
private sealed class TestType73{}
private sealed class TestType74{}
private sealed class TestType75{}
private sealed class TestType76{}
private sealed class TestType77{}
private sealed class TestType78{}
private sealed class TestType79{}
private sealed class TestType80{}
private sealed class TestType81{}
private sealed class TestType82{}
private sealed class TestType83{}
private sealed class TestType84{}
private sealed class TestType85{}
private sealed class TestType86{}
private sealed class TestType87{}
private sealed class TestType88{}
private sealed class TestType89{}
private sealed class TestType90{}
private sealed class TestType91{}
private sealed class TestType92{}
private sealed class TestType93{}
private sealed class TestType94{}
private sealed class TestType95{}
private sealed class TestType96{}
private sealed class TestType97{}
private sealed class TestType98{}
private sealed class TestType99{}
// ReSharper restore UnusedType.Local
// @formatter:on
}
@@ -66,7 +66,7 @@ internal sealed class ClientDirtySystem : EntitySystem
return _dirty;
}
private void OnEntityDirty(object? sender, EntityUid e)
private void OnEntityDirty(EntityUid e)
{
if (e.IsClientSide()) return;
@@ -49,7 +49,7 @@ namespace Robust.Server.GameObjects
RefreshVisibility(ev.Entity);
}
private void OnEntityInit(object? sender, EntityUid uid)
private void OnEntityInit(EntityUid uid)
{
RefreshVisibility(uid);
}
@@ -44,7 +44,7 @@ namespace Robust.Server.GameObjects
EntityUid IServerEntityManagerInternal.AllocEntity(string? prototypeName, EntityUid uid)
{
return AllocEntity(prototypeName, uid);
return AllocEntity(prototypeName, out _, uid);
}
void IServerEntityManagerInternal.FinishEntityLoad(EntityUid entity, IEntityLoadContext? context)
@@ -146,21 +146,21 @@ namespace Robust.Server.GameObjects
return _lastProcessedSequencesCmd[session];
}
private void OnEntityRemoved(object? sender, EntityUid e)
private void OnEntityRemoved(EntityUid e)
{
if (_componentDeletionHistory.ContainsKey(e))
_componentDeletionHistory.Remove(e);
}
private void OnComponentRemoved(object? sender, ComponentEventArgs e)
private void OnComponentRemoved(RemovedComponentEventArgs e)
{
var reg = ComponentFactory.GetRegistration(e.Component.GetType());
var reg = ComponentFactory.GetRegistration(e.BaseArgs.Component.GetType());
// We only keep track of networked components being removed.
if (reg.NetID is not {} netId)
return;
var uid = e.Owner;
var uid = e.BaseArgs.Owner;
if (!_componentDeletionHistory.TryGetValue(uid, out var list))
{
+2 -2
View File
@@ -42,13 +42,13 @@ namespace Robust.Server.GameStates
EntityManager.EntityDirtied -= OnEntityDirty;
}
private void OnEntityAdd(object? sender, EntityUid e)
private void OnEntityAdd(EntityUid e)
{
DebugTools.Assert(_currentIndex == _gameTiming.CurTick.Value % DirtyBufferSize);
_addEntities[_currentIndex].Add(e);
}
private void OnEntityDirty(object? sender, EntityUid uid)
private void OnEntityDirty(EntityUid uid)
{
if (!_addEntities[_currentIndex].Contains(uid))
_dirtyEntities[_currentIndex].Add(uid);
+1 -1
View File
@@ -216,7 +216,7 @@ internal sealed partial class PVSSystem : EntitySystem
#region PVSCollection Event Updates
private void OnEntityDeleted(object? sender, EntityUid e)
private void OnEntityDeleted(EntityUid e)
{
_entityPvsCollection.RemoveIndex(EntityManager.CurrentTick, e);
}
+97
View File
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects;
public readonly struct CompIdx : IEquatable<CompIdx>
{
private static readonly ReaderWriterLockSlim SlowStoreLock = new();
private static readonly Dictionary<Type, CompIdx> SlowStore = new();
internal readonly int Value;
internal static CompIdx Index<T>() => Store<T>.Index;
internal static CompIdx Index(Type t)
{
using (SlowStoreLock.ReadGuard())
{
if (SlowStore.TryGetValue(t, out var idx))
return idx;
}
// Doesn't exist in the store, get a write lock and add it.
using (SlowStoreLock.WriteGuard())
{
var idx = (CompIdx)typeof(Store<>)
.MakeGenericType(t)
.GetField(nameof(Store<int>.Index), BindingFlags.Static | BindingFlags.Public)!
.GetValue(null)!;
SlowStore[t] = idx;
return idx;
}
}
internal static int ArrayIndex<T>() => Index<T>().Value;
internal static int ArrayIndex(Type type) => Index(type).Value;
internal static void AssignArray<T>(ref T[] array, CompIdx idx, T value)
{
RefArray(ref array, idx) = value;
}
internal static ref T RefArray<T>(ref T[] array, CompIdx idx)
{
var curLength = array.Length;
if (curLength <= idx.Value)
{
var newLength = MathHelper.NextPowerOfTwo(Math.Max(8, idx.Value));
Array.Resize(ref array, newLength);
}
return ref array[idx.Value];
}
private static int _CompIdxMaster = -1;
private static class Store<T>
{
// ReSharper disable once StaticMemberInGenericType
public static readonly CompIdx Index = new(Interlocked.Increment(ref _CompIdxMaster));
}
internal CompIdx(int value)
{
Value = value;
}
public bool Equals(CompIdx other)
{
return Value == other.Value;
}
public override bool Equals(object? obj)
{
return obj is CompIdx other && Equals(other);
}
public override int GetHashCode()
{
return Value;
}
public static bool operator ==(CompIdx left, CompIdx right)
{
return left.Equals(right);
}
public static bool operator !=(CompIdx left, CompIdx right)
{
return !left.Equals(right);
}
}
+3 -2
View File
@@ -210,8 +210,9 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public virtual ComponentState GetComponentState()
{
if (!(Attribute.GetCustomAttribute(GetType(), typeof(NetworkedComponentAttribute)) is NetworkedComponentAttribute))
throw new InvalidOperationException($"Calling base {nameof(GetComponentState)} without being networked.");
DebugTools.Assert(
Attribute.GetCustomAttribute(GetType(), typeof(NetworkedComponentAttribute)) != null,
$"Calling base {nameof(GetComponentState)} without being networked.");
return DefaultComponentState;
}
+25 -38
View File
@@ -1,19 +1,15 @@
#nullable enable
using System;
namespace Robust.Shared.GameObjects
{
/// <summary>
/// Arguments for an event related to a component.
/// </summary>
public abstract class ComponentEventArgs : EventArgs
public readonly struct ComponentEventArgs
{
/// <summary>
/// Component that this event relates to.
/// </summary>
public IComponent Component { get; }
public Component Component { get; }
/// <summary>
/// EntityUid of the entity this component belongs to.
@@ -24,50 +20,41 @@ namespace Robust.Shared.GameObjects
/// Constructs a new instance of <see cref="ComponentEventArgs"/>.
/// </summary>
/// <param name="component">The relevant component</param>
/// <param name="Owner">EntityUid of the entity this component belongs to.</param>
protected ComponentEventArgs(IComponent component, EntityUid owner)
/// <param name="owner">EntityUid of the entity this component belongs to.</param>
public ComponentEventArgs(Component component, EntityUid owner)
{
Component = component;
Owner = owner;
}
}
/// <summary>
/// Arguments for an event related to a component being added.
/// </summary>
public sealed class AddedComponentEventArgs : ComponentEventArgs
public readonly struct AddedComponentEventArgs
{
/// <summary>
/// Constructs a new instance of <see cref="AddedComponentEventArgs"/>.
/// </summary>
/// <param name="component">The relevant component</param>
/// <param name="uid">EntityUid of the entity this component belongs to.</param>
public AddedComponentEventArgs(IComponent component, EntityUid uid) : base(component, uid) { }
public readonly ComponentEventArgs BaseArgs;
public AddedComponentEventArgs(ComponentEventArgs baseArgs)
{
BaseArgs = baseArgs;
}
}
/// <summary>
/// Arguments for an event related to a component being removed.
/// </summary>
public sealed class RemovedComponentEventArgs : ComponentEventArgs
public readonly struct RemovedComponentEventArgs
{
/// <summary>
/// Constructs a new instance of <see cref="RemovedComponentEventArgs"/>.
/// </summary>
/// <param name="component">The relevant component</param>
/// <param name="uid">EntityUid of the entity this component belongs to.</param>
public RemovedComponentEventArgs(IComponent component, EntityUid uid) : base(component, uid) { }
public readonly ComponentEventArgs BaseArgs;
public RemovedComponentEventArgs(ComponentEventArgs baseArgs)
{
BaseArgs = baseArgs;
}
}
/// <summary>
/// Arguments for an event related to a component being deleted.
/// </summary>
public sealed class DeletedComponentEventArgs : ComponentEventArgs
public readonly struct DeletedComponentEventArgs
{
/// <summary>
/// Constructs a new instance of <see cref="DeletedComponentEventArgs"/>.
/// </summary>
/// <param name="component">The relevant component</param>
/// <param name="uid">EntityUid of the entity this component belongs to.</param>
public DeletedComponentEventArgs(IComponent component, EntityUid uid) : base(component, uid) { }
public readonly ComponentEventArgs BaseArgs;
public DeletedComponentEventArgs(ComponentEventArgs baseArgs)
{
BaseArgs = baseArgs;
}
}
}
+46 -44
View File
@@ -20,28 +20,6 @@ namespace Robust.Shared.GameObjects
private readonly IDynamicTypeFactoryInternal _typeFactory;
private readonly IReflectionManager _reflectionManager;
private sealed class ComponentRegistration : IComponentRegistration
{
public string Name { 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)
{
Name = name;
NetID = null;
Type = type;
References.Add(type);
}
public override string ToString()
{
return $"ComponentRegistration({Name}: {Type})";
}
}
// Bunch of dictionaries to allow lookups in all directions.
/// <summary>
/// Mapping of component name to type.
@@ -56,23 +34,27 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Mapping of network ID to type.
/// </summary>
private List<IComponentRegistration>? _networkedComponents;
private List<ComponentRegistration>? _networkedComponents;
/// <summary>
/// Mapping of concrete component types to their registration.
/// </summary>
private readonly Dictionary<Type, ComponentRegistration> types = new();
private ComponentRegistration[] _array = Array.Empty<ComponentRegistration>();
/// <summary>
/// Set of components that should be ignored. Probably just the list of components unique to the other project.
/// </summary>
private readonly HashSet<string> IgnoredComponentNames = new();
/// <inheritdoc />
public event Action<IComponentRegistration>? ComponentAdded;
private readonly Dictionary<CompIdx, Type> _idxToType = new();
/// <inheritdoc />
public event Action<(IComponentRegistration, Type)>? ComponentReferenceAdded;
public event Action<ComponentRegistration>? ComponentAdded;
/// <inheritdoc />
public event Action<ComponentRegistration, CompIdx>? ComponentReferenceAdded;
/// <inheritdoc />
public event Action<string>? ComponentIgnoreAdded;
@@ -81,7 +63,7 @@ namespace Robust.Shared.GameObjects
public IEnumerable<Type> AllRegisteredTypes => types.Keys;
/// <inheritdoc />
public IReadOnlyList<IComponentRegistration>? NetworkedComponents => _networkedComponents;
public IReadOnlyList<ComponentRegistration>? NetworkedComponents => _networkedComponents;
private IEnumerable<ComponentRegistration> AllRegistrations => types.Values;
@@ -155,10 +137,15 @@ namespace Robust.Shared.GameObjects
}
}
var registration = new ComponentRegistration(name, type);
var idx = CompIdx.Index(type);
_idxToType[idx] = type;
var registration = new ComponentRegistration(name, type, idx);
names[name] = registration;
_lowerCaseNames[lowerCaseName] = name;
types[type] = registration;
CompIdx.AssignArray(ref _array, idx, registration);
ComponentAdded?.Invoke(registration);
@@ -216,13 +203,19 @@ namespace Robust.Shared.GameObjects
throw new InvalidOperationException($"Unregistered type: {target}");
}
if (@interface == typeof(MetaDataComponent) || @interface == typeof(TransformComponent))
throw new InvalidOperationException("Cannot make Transform or Metadata a reference type!");
var idx = CompIdx.Index(@interface);
_idxToType[idx] = @interface;
var registration = types[target];
if (registration.References.Contains(@interface))
if (registration.References.Contains(idx))
{
throw new InvalidOperationException($"Attempted to register a reference twice: {@interface}");
}
registration.References.Add(@interface);
ComponentReferenceAdded?.Invoke((registration, @interface));
registration.References.Add(idx);
ComponentReferenceAdded?.Invoke(registration, idx);
}
public void IgnoreMissingComponents()
@@ -292,6 +285,11 @@ namespace Robust.Shared.GameObjects
return _typeFactory.CreateInstanceUnchecked<IComponent>(types[componentType].Type);
}
public IComponent GetComponent(CompIdx componentType)
{
return _typeFactory.CreateInstanceUnchecked<IComponent>(_array[componentType.Value].Type);
}
public T GetComponent<T>() where T : IComponent, new()
{
if (!types.ContainsKey(typeof(T)))
@@ -316,7 +314,7 @@ namespace Robust.Shared.GameObjects
return _typeFactory.CreateInstanceUnchecked<IComponent>(GetRegistration(netId).Type);
}
public IComponentRegistration GetRegistration(string componentName, bool ignoreCase = false)
public ComponentRegistration GetRegistration(string componentName, bool ignoreCase = false)
{
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
{
@@ -338,7 +336,7 @@ namespace Robust.Shared.GameObjects
return GetRegistration(componentType).Name;
}
public IComponentRegistration GetRegistration(ushort netID)
public ComponentRegistration GetRegistration(ushort netID)
{
if (_networkedComponents is null)
throw new ComponentRegistrationLockException();
@@ -353,7 +351,7 @@ namespace Robust.Shared.GameObjects
}
}
public IComponentRegistration GetRegistration(Type reference)
public ComponentRegistration GetRegistration(Type reference)
{
try
{
@@ -365,17 +363,19 @@ namespace Robust.Shared.GameObjects
}
}
public IComponentRegistration GetRegistration<T>() where T : IComponent, new()
public ComponentRegistration GetRegistration<T>() where T : IComponent, new()
{
return GetRegistration(typeof(T));
}
public IComponentRegistration GetRegistration(IComponent component)
public ComponentRegistration GetRegistration(IComponent component)
{
return GetRegistration(component.GetType());
}
public bool TryGetRegistration(string componentName, [NotNullWhen(true)] out IComponentRegistration? registration, bool ignoreCase = false)
public ComponentRegistration GetRegistration(CompIdx idx) => _array[idx.Value];
public bool TryGetRegistration(string componentName, [NotNullWhen(true)] out ComponentRegistration? registration, bool ignoreCase = false)
{
if (ignoreCase && _lowerCaseNames.TryGetValue(componentName, out var lowerCaseName))
{
@@ -392,7 +392,7 @@ namespace Robust.Shared.GameObjects
return false;
}
public bool TryGetRegistration(Type reference, [NotNullWhen(true)] out IComponentRegistration? registration)
public bool TryGetRegistration(Type reference, [NotNullWhen(true)] out ComponentRegistration? registration)
{
if (types.TryGetValue(reference, out var tempRegistration))
{
@@ -404,12 +404,12 @@ namespace Robust.Shared.GameObjects
return false;
}
public bool TryGetRegistration<T>([NotNullWhen(true)] out IComponentRegistration? registration) where T : IComponent, new()
public bool TryGetRegistration<T>([NotNullWhen(true)] out ComponentRegistration? registration) where T : IComponent, new()
{
return TryGetRegistration(typeof(T), out registration);
}
public bool TryGetRegistration(ushort netID, [NotNullWhen(true)] out IComponentRegistration? registration)
public bool TryGetRegistration(ushort netID, [NotNullWhen(true)] out ComponentRegistration? registration)
{
if (_networkedComponents is not null && _networkedComponents.TryGetValue(netID, out var tempRegistration))
{
@@ -421,7 +421,7 @@ namespace Robust.Shared.GameObjects
return false;
}
public bool TryGetRegistration(IComponent component, [NotNullWhen(true)] out IComponentRegistration? registration)
public bool TryGetRegistration(IComponent component, [NotNullWhen(true)] out ComponentRegistration? registration)
{
return TryGetRegistration(component.GetType(), out registration);
}
@@ -467,7 +467,7 @@ namespace Robust.Shared.GameObjects
}
}
public IEnumerable<Type> GetAllRefTypes()
public IEnumerable<CompIdx> GetAllRefTypes()
{
return AllRegistrations.SelectMany(r => r.References).Distinct();
}
@@ -480,7 +480,7 @@ namespace Robust.Shared.GameObjects
// component names are 1:1 with component concrete types
// a subset of component names are networked
var networkedRegs = new List<IComponentRegistration>(names.Count);
var networkedRegs = new List<ComponentRegistration>(names.Count);
foreach (var kvRegistration in names)
{
@@ -497,12 +497,14 @@ namespace Robust.Shared.GameObjects
for (ushort i = 0; i < networkedRegs.Count; i++)
{
var registration = (ComponentRegistration) networkedRegs[i];
var registration = networkedRegs[i];
registration.NetID = i;
}
_networkedComponents = networkedRegs;
}
public Type IdxToType(CompIdx idx) => _idxToType[idx];
}
[Serializable]
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameStates;
using Robust.Shared.Utility.Collections;
namespace Robust.Shared.GameObjects;
/// <summary>
/// Represents a component registered into a <see cref="IComponentFactory" />.
/// </summary>
/// <seealso cref="IComponentFactory" />
/// <seealso cref="IComponent" />
public sealed class ComponentRegistration
{
/// <summary>
/// The name of the component.
/// This is used as the <c>type</c> field in the component declarations if entity prototypes.
/// </summary>
/// <seealso cref="IComponent.Name" />
public string Name { get; }
public CompIdx Idx { get; }
/// <summary>
/// ID used to reference the component type across the network.
/// If null, no network synchronization will be available for this component.
/// </summary>
/// <seealso cref="NetworkedComponentAttribute" />
public ushort? NetID { get; internal set; }
/// <summary>
/// The type that will be instantiated if this component is created.
/// </summary>
public Type Type { get; }
public ValueList<CompIdx> References;
public ComponentRegistration(string name, Type type, CompIdx idx)
{
Name = name;
Type = type;
Idx = idx;
References.Add(idx);
}
public override string ToString()
{
return $"ComponentRegistration({Name}: {Type})";
}
}
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Robust.Shared.Utility;
using Robust.Shared.Utility.Collections;
namespace Robust.Shared.GameObjects
{
@@ -75,6 +76,8 @@ namespace Robust.Shared.GameObjects
/// <param name="args">Event arguments for the event.</param>
internal void RaiseComponentEvent<TEvent>(IComponent component, ref TEvent args)
where TEvent : notnull;
public void OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
}
internal partial class EntityEventBus : IDirectedEventBus, IEventBus, IDisposable
@@ -113,6 +116,11 @@ namespace Robust.Shared.GameObjects
_eventTables.DispatchComponent<TEvent>(component.Owner, component, ref unitRef, true);
}
public void OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare()
{
_eventTables.IgnoreUnregisteredComponents = true;
}
/// <inheritdoc />
public void RaiseLocalEvent<TEvent>(EntityUid uid, TEvent args, bool broadcast = true)
where TEvent : notnull
@@ -172,7 +180,13 @@ namespace Robust.Shared.GameObjects
void EventHandler(EntityUid uid, IComponent comp, ref TEvent args)
=> handler(uid, (TComp)comp, args);
_eventTables.Subscribe<TEvent>(typeof(TComp), typeof(TEvent), EventHandler, null, false);
_eventTables.Subscribe<TEvent>(
CompIdx.Index<TComp>(),
typeof(TComp),
typeof(TEvent),
EventHandler,
null,
false);
}
public void SubscribeLocalEvent<TComp, TEvent>(
@@ -188,7 +202,13 @@ namespace Robust.Shared.GameObjects
var orderData = new OrderingData(orderType, before, after);
_eventTables.Subscribe<TEvent>(typeof(TComp), typeof(TEvent), EventHandler, orderData, false);
_eventTables.Subscribe<TEvent>(
CompIdx.Index<TComp>(),
typeof(TComp),
typeof(TEvent),
EventHandler,
orderData,
false);
HandleOrderRegistration(typeof(TEvent), orderData);
}
@@ -198,7 +218,13 @@ namespace Robust.Shared.GameObjects
void EventHandler(EntityUid uid, IComponent comp, ref TEvent args)
=> handler(uid, (TComp)comp, ref args);
_eventTables.Subscribe<TEvent>(typeof(TComp), typeof(TEvent), EventHandler, null, true);
_eventTables.Subscribe<TEvent>(
CompIdx.Index<TComp>(),
typeof(TComp),
typeof(TEvent),
EventHandler,
null,
true);
}
public void SubscribeLocalEvent<TComp, TEvent>(ComponentEventRefHandler<TComp, TEvent> handler, Type orderType,
@@ -210,7 +236,14 @@ namespace Robust.Shared.GameObjects
var orderData = new OrderingData(orderType, before, after);
_eventTables.Subscribe<TEvent>(typeof(TComp), typeof(TEvent), EventHandler, orderData, true);
_eventTables.Subscribe<TEvent>(
CompIdx.Index<TComp>(),
typeof(TComp),
typeof(TEvent),
EventHandler,
orderData,
true);
HandleOrderRegistration(typeof(TEvent), orderData);
}
@@ -219,7 +252,7 @@ namespace Robust.Shared.GameObjects
where TComp : IComponent
where TEvent : notnull
{
_eventTables.Unsubscribe(typeof(TComp), typeof(TEvent));
_eventTables.Unsubscribe(CompIdx.Index<TComp>(), typeof(TEvent));
}
private sealed class EventTables : IDisposable
@@ -231,10 +264,10 @@ namespace Robust.Shared.GameObjects
private IComponentFactory _comFac;
// eUid -> EventType -> { CompType1, ... CompTypeN }
private Dictionary<EntityUid, Dictionary<Type, HashSet<Type>>> _eventTables;
private Dictionary<EntityUid, Dictionary<Type, HashSet<CompIdx>>> _eventTables;
// EventType -> CompType -> Handler
private Dictionary<Type, Dictionary<Type, DirectedRegistration>> _subscriptions;
// CompType -> EventType -> Handler
private Dictionary<Type, DirectedRegistration>?[] _subscriptions;
// prevents shitcode, get your subscriptions figured out before you start spawning entities
private bool _subscriptionLock;
@@ -250,34 +283,60 @@ namespace Robust.Shared.GameObjects
_entMan.ComponentAdded += OnComponentAdded;
_entMan.ComponentRemoved += OnComponentRemoved;
// Dynamic handling of components is only for RobustUnitTest compatibility spaghetti.
_comFac.ComponentAdded += ComFacOnComponentAdded;
_comFac.ComponentReferenceAdded += ComFacOnComponentReferenceAdded;
_eventTables = new();
_subscriptions = new();
_subscriptions = Array.Empty<Dictionary<Type, DirectedRegistration>>();
_subscriptionLock = false;
InitSubscriptionsArray();
}
private void OnEntityAdded(object? sender, EntityUid e)
public bool IgnoreUnregisteredComponents;
private void InitSubscriptionsArray()
{
foreach (var refType in _comFac.GetAllRefTypes())
{
CompIdx.AssignArray(ref _subscriptions, refType, new Dictionary<Type, DirectedRegistration>());
}
}
private void ComFacOnComponentReferenceAdded(ComponentRegistration arg1, CompIdx arg2)
{
CompIdx.RefArray(ref _subscriptions, arg2) ??= new Dictionary<Type, DirectedRegistration>();
}
private void ComFacOnComponentAdded(ComponentRegistration obj)
{
CompIdx.RefArray(ref _subscriptions, obj.Idx) ??= new Dictionary<Type, DirectedRegistration>();
}
private void OnEntityAdded(EntityUid e)
{
AddEntity(e);
}
private void OnEntityDeleted(object? sender, EntityUid e)
private void OnEntityDeleted(EntityUid e)
{
RemoveEntity(e);
}
private void OnComponentAdded(object? sender, ComponentEventArgs e)
private void OnComponentAdded(AddedComponentEventArgs e)
{
_subscriptionLock = true;
AddComponent(e.Owner, e.Component.GetType());
AddComponent(e.BaseArgs.Owner, CompIdx.Index(e.BaseArgs.Component.GetType()));
}
private void OnComponentRemoved(object? sender, ComponentEventArgs e)
private void OnComponentRemoved(RemovedComponentEventArgs e)
{
RemoveComponent(e.Owner, e.Component.GetType());
RemoveComponent(e.BaseArgs.Owner, CompIdx.Index(e.BaseArgs.Component.GetType()));
}
private void AddSubscription(Type compType, Type eventType, DirectedRegistration registration)
private void AddSubscription(CompIdx compType, Type compTypeObj, Type eventType, DirectedRegistration registration)
{
if (_subscriptionLock)
throw new InvalidOperationException("Subscription locked.");
@@ -286,26 +345,32 @@ namespace Robust.Shared.GameObjects
if (referenceEvent != registration.ReferenceEvent)
throw new InvalidOperationException(
$"Attempted to subscribe by-ref and by-value to the same directed event! comp={compType.Name}, event={eventType.Name} eventIsByRef={referenceEvent} subscriptionIsByRef={registration.ReferenceEvent}");
$"Attempted to subscribe by-ref and by-value to the same directed event! comp={compTypeObj.Name}, event={eventType.Name} eventIsByRef={referenceEvent} subscriptionIsByRef={registration.ReferenceEvent}");
if (!_subscriptions.TryGetValue(compType, out var compSubs))
if (compType.Value >= _subscriptions.Length || _subscriptions[compType.Value] is not { } compSubs)
{
compSubs = new Dictionary<Type, DirectedRegistration>();
_subscriptions.Add(compType, compSubs);
if (IgnoreUnregisteredComponents)
return;
throw new InvalidOperationException($"Component is not a valid reference type: {compTypeObj.Name}");
}
if (compSubs.ContainsKey(eventType))
throw new InvalidOperationException(
$"Duplicate Subscriptions for comp={compType.Name}, event={eventType.Name}");
$"Duplicate Subscriptions for comp={compTypeObj}, event={eventType.Name}");
compSubs.Add(eventType, registration);
}
public void Subscribe<TEvent>(Type compType, Type eventType, DirectedEventHandler<TEvent> handler,
public void Subscribe<TEvent>(
CompIdx compType,
Type compTypeObj,
Type eventType,
DirectedEventHandler<TEvent> handler,
OrderingData? order, bool byReference)
where TEvent : notnull
{
AddSubscription(compType, eventType, new DirectedRegistration(handler, order,
AddSubscription(compType, compTypeObj, eventType, new DirectedRegistration(handler, order,
(EntityUid uid, IComponent comp, ref Unit ev) =>
{
ref var tev = ref Unsafe.As<Unit, TEvent>(ref ev);
@@ -313,13 +378,18 @@ namespace Robust.Shared.GameObjects
}, byReference));
}
public void Unsubscribe(Type compType, Type eventType)
public void Unsubscribe(CompIdx compType, Type eventType)
{
if (_subscriptionLock)
throw new InvalidOperationException("Subscription locked.");
if (!_subscriptions.TryGetValue(compType, out var compSubs))
return;
if (compType.Value >= _subscriptions.Length || _subscriptions[compType.Value] is not { } compSubs)
{
if (IgnoreUnregisteredComponents)
return;
throw new InvalidOperationException("Trying to unsubscribe from unregistered component!");
}
compSubs.Remove(eventType);
}
@@ -328,7 +398,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.
_eventTables.Add(euid, new Dictionary<Type, HashSet<Type>>());
_eventTables.Add(euid, new Dictionary<Type, HashSet<CompIdx>>());
}
private void RemoveEntity(EntityUid euid)
@@ -336,21 +406,20 @@ namespace Robust.Shared.GameObjects
_eventTables.Remove(euid);
}
private void AddComponent(EntityUid euid, Type compType)
private void AddComponent(EntityUid euid, CompIdx compType)
{
var eventTable = _eventTables[euid];
var enumerator = GetReferences(compType);
while (enumerator.MoveNext(out var type))
{
if (!_subscriptions.TryGetValue(type, out var compSubs))
continue;
var compSubs = _subscriptions[type.Value]!;
foreach (var kvSub in compSubs)
{
if (!eventTable.TryGetValue(kvSub.Key, out var subscribedComps))
{
subscribedComps = new HashSet<Type>();
subscribedComps = new HashSet<CompIdx>();
eventTable.Add(kvSub.Key, subscribedComps);
}
@@ -359,15 +428,14 @@ namespace Robust.Shared.GameObjects
}
}
private void RemoveComponent(EntityUid euid, Type compType)
private void RemoveComponent(EntityUid euid, CompIdx compType)
{
var eventTable = _eventTables[euid];
var enumerator = GetReferences(compType);
while (enumerator.MoveNext(out var type))
{
if (!_subscriptions.TryGetValue(type, out var compSubs))
continue;
var compSubs = _subscriptions[type.Value]!;
foreach (var kvSub in compSubs)
{
@@ -407,8 +475,7 @@ namespace Robust.Shared.GameObjects
foreach (var compType in subscribedComps)
{
if (!_subscriptions.TryGetValue(compType, out var compSubs))
return;
var compSubs = _subscriptions[compType.Value]!;
if (!compSubs.TryGetValue(eventType, out var reg))
return;
@@ -425,11 +492,10 @@ namespace Robust.Shared.GameObjects
public void DispatchComponent<TEvent>(EntityUid euid, IComponent component, ref Unit args, bool dispatchByReference)
where TEvent : notnull
{
var enumerator = GetReferences(component.GetType());
var enumerator = GetReferences(CompIdx.Index(component.GetType()));
while (enumerator.MoveNext(out var type))
{
if (!_subscriptions.TryGetValue(type, out var compSubs))
continue;
var compSubs = _subscriptions[type.Value]!;
if (!compSubs.TryGetValue(typeof(TEvent), out var reg))
continue;
@@ -450,7 +516,11 @@ namespace Robust.Shared.GameObjects
public void Clear()
{
ClearEntities();
_subscriptions = new();
foreach (var sub in _subscriptions)
{
sub?.Clear();
}
}
public void Dispose()
@@ -461,6 +531,9 @@ namespace Robust.Shared.GameObjects
_entMan.ComponentAdded -= OnComponentAdded;
_entMan.ComponentRemoved -= OnComponentRemoved;
_comFac.ComponentAdded -= ComFacOnComponentAdded;
_comFac.ComponentReferenceAdded -= ComFacOnComponentReferenceAdded;
// punishment for use-after-free
_entMan = null!;
_eventTables = null!;
@@ -470,7 +543,7 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Enumerates the type's component references, returning the type itself last.
/// </summary>
private ReferencesEnumerator GetReferences(Type type)
private ReferencesEnumerator GetReferences(CompIdx type)
{
return new(type, _comFac.GetRegistration(type).References);
}
@@ -499,12 +572,12 @@ namespace Robust.Shared.GameObjects
private struct ReferencesEnumerator
{
private readonly Type _baseType;
private readonly IReadOnlyList<Type> _list;
private readonly CompIdx _baseType;
private readonly ValueList<CompIdx> _list;
private readonly int _totalLength;
private int _idx;
public ReferencesEnumerator(Type baseType, IReadOnlyList<Type> list)
public ReferencesEnumerator(CompIdx baseType, ValueList<CompIdx> list)
{
_baseType = baseType;
_list = list;
@@ -512,7 +585,7 @@ namespace Robust.Shared.GameObjects
_idx = 0;
}
public bool MoveNext([NotNullWhen(true)] out Type? type)
public bool MoveNext([NotNullWhen(true)] out CompIdx type)
{
if (_idx >= _totalLength)
{
@@ -522,7 +595,7 @@ namespace Robust.Shared.GameObjects
return true;
}
type = null;
type = default;
return false;
}
@@ -537,13 +610,16 @@ namespace Robust.Shared.GameObjects
private struct SubscriptionsEnumerator : IDisposable
{
private readonly Type _eventType;
private HashSet<Type>.Enumerator _enumerator;
private readonly IReadOnlyDictionary<Type, Dictionary<Type, DirectedRegistration>> _subscriptions;
private HashSet<CompIdx>.Enumerator _enumerator;
private readonly Dictionary<Type, DirectedRegistration>?[] _subscriptions;
private readonly EntityUid _uid;
private readonly IEntityManager _entityManager;
public SubscriptionsEnumerator(Type eventType, HashSet<Type>.Enumerator enumerator,
IReadOnlyDictionary<Type, Dictionary<Type, DirectedRegistration>> subscriptions, EntityUid uid,
public SubscriptionsEnumerator(
Type eventType,
HashSet<CompIdx>.Enumerator enumerator,
Dictionary<Type, DirectedRegistration>?[] subscriptions,
EntityUid uid,
IEntityManager entityManager)
{
_eventType = eventType;
@@ -556,22 +632,15 @@ namespace Robust.Shared.GameObjects
public bool MoveNext(
[NotNullWhen(true)] out (IComponent Component, DirectedRegistration Registration)? tuple)
{
_enumerator.MoveNext();
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (_enumerator.Current == null)
if (!_enumerator.MoveNext())
{
tuple = null;
return false;
}
var compType = _enumerator.Current;
if (!_subscriptions.TryGetValue(compType, out var compSubs))
{
tuple = null;
return false;
}
var compSubs = _subscriptions[compType.Value]!;
if (!compSubs.TryGetValue(_eventType, out var registration))
{
@@ -1,17 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Reflection;
using Robust.Shared.GameStates;
using Robust.Shared.Physics;
using Robust.Shared.Players;
using Robust.Shared.Utility;
using System.Runtime.CompilerServices;
using System.Threading;
using Robust.Shared.Maths;
using Robust.Shared.Log;
using System.Diagnostics;
#if EXCEPTION_TOLERANCE
@@ -51,13 +46,13 @@ namespace Robust.Shared.GameObjects
new(ComponentCollectionCapacity);
/// <inheritdoc />
public event EventHandler<ComponentEventArgs>? ComponentAdded;
public event Action<AddedComponentEventArgs>? ComponentAdded;
/// <inheritdoc />
public event EventHandler<ComponentEventArgs>? ComponentRemoved;
public event Action<RemovedComponentEventArgs>? ComponentRemoved;
/// <inheritdoc />
public event EventHandler<ComponentEventArgs>? ComponentDeleted;
public event Action<DeletedComponentEventArgs>? ComponentDeleted;
public void InitializeComponents()
{
@@ -83,41 +78,21 @@ namespace Robust.Shared.GameObjects
FillComponentDict();
}
private void AddComponentRefType(Type type)
private void AddComponentRefType(CompIdx type)
{
var dict = new Dictionary<EntityUid, Component>();
_entTraitDict.Add(type, dict);
var index = GetCompIdIndex(type);
EnsureEntTraitIndexCapacity(index);
_entTraitArray[index] = dict;
_entTraitDict.Add(_componentFactory.IdxToType(type), dict);
CompIdx.AssignArray(ref _entTraitArray, type, dict);
}
private void OnComponentAdded(IComponentRegistration obj)
private void OnComponentAdded(ComponentRegistration obj)
{
AddComponentRefType(obj.Type);
AddComponentRefType(obj.Idx);
}
private void OnComponentReferenceAdded((IComponentRegistration, Type type) obj)
private void OnComponentReferenceAdded(ComponentRegistration reg, CompIdx type)
{
AddComponentRefType(obj.Item2);
}
private static int GetCompIdIndex(Type type)
{
return (int)typeof(CompArrayIndex<>)
.MakeGenericType(type)
.GetField(nameof(CompArrayIndex<int>.Index), BindingFlags.Static | BindingFlags.Public)!
.GetValue(null)!;
}
private void EnsureEntTraitIndexCapacity(int index)
{
var curLength = _entTraitArray.Length;
if (curLength > index)
return;
var newLength = MathHelper.NextPowerOfTwo(Math.Max(8, index));
Array.Resize(ref _entTraitArray, newLength);
AddComponentRefType(type);
}
#region Component Management
@@ -128,22 +103,31 @@ namespace Robust.Shared.GameObjects
DebugTools.Assert(metadata.EntityLifeStage == EntityLifeStage.PreInit);
metadata.EntityLifeStage = EntityLifeStage.Initializing;
// Initialize() can modify the collection of components.
var components = GetComponents(uid)
.OrderBy(x => x switch
{
TransformComponent _ => 0,
IPhysBody _ => 1,
_ => int.MaxValue
});
// Initialize() can modify the collection of components. Copy them.
FixedArray32<Component?> compsFixed = default;
foreach (var component in components)
var comps = compsFixed.AsSpan;
CopyComponentsInto(ref comps, uid);
// TODO: please for the love of god remove these initialization order hacks.
// Init transform first, we always have it.
var transform = GetComponent<TransformComponent>(uid);
if (transform.LifeStage < ComponentLifeStage.Initialized)
transform.LifeInitialize(this);
// Init physics second if it exists.
if (TryGetComponent<PhysicsComponent>(uid, out var phys)
&& phys.LifeStage < ComponentLifeStage.Initialized)
{
var comp = (Component)component;
if (comp.Initialized)
continue;
phys.LifeInitialize(this);
}
comp.LifeInitialize(this);
// Do rest of components.
foreach (var comp in comps)
{
if (comp is { LifeStage: < ComponentLifeStage.Initialized })
comp.LifeInitialize(this);
}
#if DEBUG
@@ -165,24 +149,32 @@ namespace Robust.Shared.GameObjects
public void StartComponents(EntityUid uid)
{
// TODO: Move this to EntityManager.
// Startup() can modify _components
// This code can only handle additions to the list. Is there a better way? Probably not.
var comps = GetComponents(uid)
.OrderBy(x => x switch
{
TransformComponent _ => 0,
IPhysBody _ => 1,
_ => int.MaxValue
});
FixedArray32<Component?> compsFixed = default;
foreach (var component in comps)
var comps = compsFixed.AsSpan;
CopyComponentsInto(ref comps, uid);
// TODO: please for the love of god remove these initialization order hacks.
// Init transform first, we always have it.
var transform = GetComponent<TransformComponent>(uid);
if (transform.LifeStage == ComponentLifeStage.Initialized)
transform.LifeStartup(this);
// Init physics second if it exists.
if (TryGetComponent<PhysicsComponent>(uid, out var phys)
&& phys.LifeStage == ComponentLifeStage.Initialized)
{
var comp = (Component)component;
if (comp.LifeStage == ComponentLifeStage.Initialized)
{
phys.LifeStartup(this);
}
// Do rest of components.
foreach (var comp in comps)
{
if (comp is { LifeStage: ComponentLifeStage.Initialized })
comp.LifeStartup(this);
}
}
}
@@ -265,7 +257,7 @@ namespace Robust.Shared.GameObjects
// Check that there are no overlapping references.
foreach (var type in reg.References)
{
var dict = _entTraitDict[type];
var dict = _entTraitArray[type.Value];
if (!dict.TryGetValue(uid, out var duplicate))
continue;
@@ -273,17 +265,13 @@ namespace Robust.Shared.GameObjects
throw new InvalidOperationException(
$"Component reference type {type} already occupied by {duplicate}");
// these two components are required on all entities and cannot be overwritten.
if (duplicate is TransformComponent || duplicate is MetaDataComponent)
throw new InvalidOperationException("Tried to overwrite a protected component.");
RemoveComponentImmediate(duplicate, uid, false);
}
// add the component to the grid
foreach (var type in reg.References)
{
_entTraitDict[type].Add(uid, component);
_entTraitArray[type.Value].Add(uid, component);
_entCompIndex.Add(uid, component);
}
@@ -305,7 +293,7 @@ namespace Robust.Shared.GameObjects
Dirty(component);
}
ComponentAdded?.Invoke(this, new AddedComponentEventArgs(component, uid));
ComponentAdded?.Invoke(new AddedComponentEventArgs(new ComponentEventArgs(component, uid)));
component.LifeAddToEntity(this);
@@ -437,7 +425,7 @@ namespace Robust.Shared.GameObjects
if (component.LifeStage != ComponentLifeStage.PreAdd)
component.LifeRemoveFromEntity(this);
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, uid));
ComponentRemoved?.Invoke(new RemovedComponentEventArgs(new ComponentEventArgs(component, uid)));
#if EXCEPTION_TOLERANCE
}
catch (Exception e)
@@ -471,7 +459,7 @@ namespace Robust.Shared.GameObjects
if (component.LifeStage != ComponentLifeStage.PreAdd)
component.LifeRemoveFromEntity(this); // Sets delete
ComponentRemoved?.Invoke(this, new RemovedComponentEventArgs(component, uid));
ComponentRemoved?.Invoke(new RemovedComponentEventArgs(new ComponentEventArgs(component, uid)));
}
#if EXCEPTION_TOLERANCE
}
@@ -516,18 +504,18 @@ namespace Robust.Shared.GameObjects
foreach (var refType in reg.References)
{
_entTraitDict[refType].Remove(entityUid);
_entTraitArray[refType.Value].Remove(entityUid);
}
_entCompIndex.Remove(entityUid, component);
ComponentDeleted?.Invoke(this, new DeletedComponentEventArgs(component, entityUid));
ComponentDeleted?.Invoke(new DeletedComponentEventArgs(new ComponentEventArgs(component, entityUid)));
}
/// <inheritdoc />
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool HasComponent<T>(EntityUid uid)
{
return _entTraitArray[ArrayIndexFor<T>()].TryGetValue(uid, out var comp) && !comp.Deleted;
return _entTraitArray[CompIdx.ArrayIndex<T>()].TryGetValue(uid, out var comp) && !comp.Deleted;
}
/// <inheritdoc />
@@ -607,7 +595,7 @@ namespace Robust.Shared.GameObjects
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetComponent<T>(EntityUid uid)
{
var dict = _entTraitArray[ArrayIndexFor<T>()];
var dict = _entTraitArray[CompIdx.ArrayIndex<T>()];
if (dict.TryGetValue(uid, out var comp))
{
if (!comp.Deleted)
@@ -619,6 +607,18 @@ namespace Robust.Shared.GameObjects
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {typeof(T)}");
}
public IComponent GetComponent(EntityUid uid, CompIdx type)
{
var dict = _entTraitArray[type.Value];
if (dict.TryGetValue(uid, out var comp))
{
if (!comp.Deleted)
return comp;
}
throw new KeyNotFoundException($"Entity {uid} does not have a component of type {_componentFactory.IdxToType(type)}");
}
/// <inheritdoc />
public IComponent GetComponent(EntityUid uid, Type type)
{
@@ -644,7 +644,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public bool TryGetComponent<T>(EntityUid uid, [NotNullWhen(true)] out T component)
{
var dict = _entTraitArray[ArrayIndexFor<T>()];
var dict = _entTraitArray[CompIdx.ArrayIndex<T>()];
if (dict.TryGetValue(uid, out var comp))
{
if (!comp.Deleted)
@@ -758,7 +758,7 @@ namespace Robust.Shared.GameObjects
public EntityQuery<TComp1> GetEntityQuery<TComp1>() where TComp1 : Component
{
return new EntityQuery<TComp1>(_entTraitArray[ArrayIndexFor<TComp1>()]);
return new EntityQuery<TComp1>(_entTraitArray[CompIdx.ArrayIndex<TComp1>()]);
}
/// <inheritdoc />
@@ -773,6 +773,25 @@ namespace Robust.Shared.GameObjects
}
}
/// <summary>
/// Copy the components for an entity into the given span,
/// or re-allocate the span as an array if there's not enough space.
/// </summary>
private void CopyComponentsInto(ref Span<Component?> comps, EntityUid uid)
{
var set = _entCompIndex[uid];
if (set.Count > comps.Length)
{
comps = new Component[set.Count];
}
var i = 0;
foreach (var c in set)
{
comps[i++] = c;
}
}
/// <inheritdoc />
public IEnumerable<T> GetComponents<T>(EntityUid uid)
{
@@ -796,7 +815,7 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public IEnumerable<T> EntityQuery<T>(bool includePaused = false) where T : IComponent
{
var comps = _entTraitArray[ArrayIndexFor<T>()];
var comps = _entTraitArray[CompIdx.ArrayIndex<T>()];
if (includePaused)
{
@@ -809,7 +828,7 @@ namespace Robust.Shared.GameObjects
}
else
{
var metaComps = _entTraitArray[ArrayIndexFor<MetaDataComponent>()];
var metaComps = _entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()];
foreach (var t1Comp in comps.Values)
{
@@ -830,8 +849,8 @@ namespace Robust.Shared.GameObjects
where TComp2 : IComponent
{
// this would prob be faster if trait1 was a list (or an array of structs hue).
var trait1 = _entTraitArray[ArrayIndexFor<TComp1>()];
var trait2 = _entTraitArray[ArrayIndexFor<TComp2>()];
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
// you really want trait1 to be the smaller set of components
if (includePaused)
@@ -848,7 +867,7 @@ namespace Robust.Shared.GameObjects
}
else
{
var metaComps = _entTraitArray[ArrayIndexFor<MetaDataComponent>()];
var metaComps = _entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()];
foreach (var (uid, t1Comp) in trait1)
{
@@ -875,9 +894,9 @@ namespace Robust.Shared.GameObjects
where TComp2 : IComponent
where TComp3 : IComponent
{
var trait1 = _entTraitArray[ArrayIndexFor<TComp1>()];
var trait2 = _entTraitArray[ArrayIndexFor<TComp2>()];
var trait3 = _entTraitArray[ArrayIndexFor<TComp3>()];
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
var trait3 = _entTraitArray[CompIdx.ArrayIndex<TComp3>()];
if (includePaused)
{
@@ -897,7 +916,7 @@ namespace Robust.Shared.GameObjects
}
else
{
var metaComps = _entTraitArray[ArrayIndexFor<MetaDataComponent>()];
var metaComps = _entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()];
foreach (var (uid, t1Comp) in trait1)
{
@@ -930,10 +949,10 @@ namespace Robust.Shared.GameObjects
where TComp3 : IComponent
where TComp4 : IComponent
{
var trait1 = _entTraitArray[ArrayIndexFor<TComp1>()];
var trait2 = _entTraitArray[ArrayIndexFor<TComp2>()];
var trait3 = _entTraitArray[ArrayIndexFor<TComp3>()];
var trait4 = _entTraitArray[ArrayIndexFor<TComp4>()];
var trait1 = _entTraitArray[CompIdx.ArrayIndex<TComp1>()];
var trait2 = _entTraitArray[CompIdx.ArrayIndex<TComp2>()];
var trait3 = _entTraitArray[CompIdx.ArrayIndex<TComp3>()];
var trait4 = _entTraitArray[CompIdx.ArrayIndex<TComp4>()];
if (includePaused)
{
@@ -957,7 +976,7 @@ namespace Robust.Shared.GameObjects
}
else
{
var metaComps = _entTraitArray[ArrayIndexFor<MetaDataComponent>()];
var metaComps = _entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()];
foreach (var (uid, t1Comp) in trait1)
{
@@ -1044,16 +1063,6 @@ namespace Robust.Shared.GameObjects
AddComponentRefType(refType);
}
}
private static int ArrayIndexFor<T>() => CompArrayIndex<T>.Index;
private static int _compIndexMaster = -1;
private static class CompArrayIndex<T>
{
// ReSharper disable once StaticMemberInGenericType
public static readonly int Index = Interlocked.Increment(ref _compIndexMaster);
}
}
public readonly struct NetComponentEnumerable
+29 -25
View File
@@ -57,11 +57,11 @@ namespace Robust.Shared.GameObjects
/// <inheritdoc />
public IEventBus EventBus => _eventBus;
public event EventHandler<EntityUid>? EntityAdded;
public event EventHandler<EntityUid>? EntityInitialized;
public event EventHandler<EntityUid>? EntityStarted;
public event EventHandler<EntityUid>? EntityDeleted;
public event EventHandler<EntityUid>? EntityDirtied; // only raised after initialization
public event Action<EntityUid>? EntityAdded;
public event Action<EntityUid>? EntityInitialized;
public event Action<EntityUid>? EntityStarted;
public event Action<EntityUid>? EntityDeleted;
public event Action<EntityUid>? EntityDirtied; // only raised after initialization
public bool Started { get; protected set; }
public bool Initialized { get; protected set; }
@@ -233,7 +233,7 @@ namespace Robust.Shared.GameObjects
var currentTick = CurrentTick;
// We want to retrieve MetaDataComponent even if its Deleted flag is set.
if (!_entTraitArray[ArrayIndexFor<MetaDataComponent>()].TryGetValue(uid, out var component))
if (!_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid, out var component))
throw new KeyNotFoundException($"Entity {uid} does not exist, cannot dirty it.");
var metadata = (MetaDataComponent)component;
@@ -244,7 +244,7 @@ namespace Robust.Shared.GameObjects
if (metadata.EntityLifeStage > EntityLifeStage.Initializing)
{
EntityDirtied?.Invoke(this, uid);
EntityDirtied?.Invoke(uid);
}
}
@@ -273,7 +273,7 @@ namespace Robust.Shared.GameObjects
// Networking blindly spams entities at this function, they can already be
// deleted from being a child of a previously deleted entity
// TODO: Why does networking need to send deletes for child entities?
if (!_entTraitArray[ArrayIndexFor<MetaDataComponent>()].TryGetValue(e, out var comp)
if (!_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(e, out var comp)
|| comp is not MetaDataComponent meta || meta.EntityDeleted)
return;
@@ -322,7 +322,7 @@ namespace Robust.Shared.GameObjects
DisposeComponents(uid);
metadata.EntityLifeStage = EntityLifeStage.Deleted;
EntityDeleted?.Invoke(this, uid);
EntityDeleted?.Invoke(uid);
EventBus.RaiseEvent(EventSource.Local, new EntityDeletedMessage(uid));
Entities.Remove(uid);
}
@@ -337,7 +337,7 @@ namespace Robust.Shared.GameObjects
public bool EntityExists(EntityUid uid)
{
return _entTraitArray[ArrayIndexFor<MetaDataComponent>()].ContainsKey(uid);
return _entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].ContainsKey(uid);
}
public bool EntityExists(EntityUid? uid)
@@ -347,12 +347,12 @@ namespace Robust.Shared.GameObjects
public bool Deleted(EntityUid uid)
{
return !_entTraitArray[ArrayIndexFor<MetaDataComponent>()].TryGetValue(uid, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
return !_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
}
public bool Deleted(EntityUid? uid)
{
return !uid.HasValue || !_entTraitArray[ArrayIndexFor<MetaDataComponent>()].TryGetValue(uid.Value, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
return !uid.HasValue || !_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid.Value, out var comp) || ((MetaDataComponent) comp).EntityDeleted;
}
/// <summary>
@@ -369,7 +369,10 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Allocates an entity and stores it but does not load components or do initialization.
/// </summary>
private protected EntityUid AllocEntity(string? prototypeName, EntityUid uid = default)
private protected EntityUid AllocEntity(
string? prototypeName,
out MetaDataComponent metadata,
EntityUid uid = default)
{
EntityPrototype? prototype = null;
if (!string.IsNullOrWhiteSpace(prototypeName))
@@ -378,9 +381,10 @@ namespace Robust.Shared.GameObjects
prototype = PrototypeManager.Index<EntityPrototype>(prototypeName);
}
var entity = AllocEntity(uid);
var entity = AllocEntity(out metadata, uid);
GetComponent<MetaDataComponent>(entity).EntityPrototype = prototype;
metadata._entityPrototype = prototype;
Dirty(metadata);
return entity;
}
@@ -388,7 +392,7 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// Allocates an entity and stores it but does not load components or do initialization.
/// </summary>
private protected EntityUid AllocEntity(EntityUid uid = default)
private protected EntityUid AllocEntity(out MetaDataComponent metadata, EntityUid uid = default)
{
if (uid == default)
{
@@ -401,9 +405,9 @@ namespace Robust.Shared.GameObjects
}
// we want this called before adding components
EntityAdded?.Invoke(this, uid);
EntityAdded?.Invoke(uid);
var metadata = new MetaDataComponent { Owner = uid };
metadata = new MetaDataComponent { Owner = uid };
Entities.Add(uid);
// add the required MetaDataComponent directly.
@@ -421,12 +425,12 @@ namespace Robust.Shared.GameObjects
private protected virtual EntityUid CreateEntity(string? prototypeName, EntityUid uid = default)
{
if (prototypeName == null)
return AllocEntity(uid);
return AllocEntity(out _, uid);
var entity = AllocEntity(prototypeName, uid);
var entity = AllocEntity(prototypeName, out var metadata, uid);
try
{
EntityPrototype.LoadEntity(GetComponent<MetaDataComponent>(entity).EntityPrototype, entity, ComponentFactory, PrototypeManager, this, _serManager, null);
EntityPrototype.LoadEntity(metadata.EntityPrototype, entity, ComponentFactory, PrototypeManager, this, _serManager, null);
return entity;
}
catch (Exception e)
@@ -465,17 +469,17 @@ namespace Robust.Shared.GameObjects
protected void InitializeEntity(EntityUid entity, MetaDataComponent? meta = null)
{
InitializeComponents(entity, meta);
EntityInitialized?.Invoke(this, entity);
EntityInitialized?.Invoke(entity);
}
protected void StartEntity(EntityUid entity)
{
StartComponents(entity);
EntityStarted?.Invoke(this, entity);
EntityStarted?.Invoke(entity);
}
public void RunMapInit(EntityUid entity, MetaDataComponent meta)
{
{
if (meta.EntityLifeStage == EntityLifeStage.MapInitialized)
return; // Already map initialized, do nothing.
@@ -489,7 +493,7 @@ namespace Robust.Shared.GameObjects
public virtual EntityStringRepresentation ToPrettyString(EntityUid uid)
{
// We want to retrieve the MetaData component even if it is deleted.
if (!_entTraitArray[ArrayIndexFor<MetaDataComponent>()].TryGetValue(uid, out var component))
if (!_entTraitArray[CompIdx.ArrayIndex<MetaDataComponent>()].TryGetValue(uid, out var component))
return new EntityStringRepresentation(uid, true);
var metadata = (MetaDataComponent) component;
@@ -1,9 +1,9 @@
namespace Robust.Shared.GameObjects
{
public sealed class EntityInitializedMessage : EntityEventArgs
public readonly struct EntityInitializedMessage
{
public EntityUid Entity { get; }
public EntityInitializedMessage(EntityUid entity)
{
Entity = entity;
+19 -47
View File
@@ -50,8 +50,8 @@ namespace Robust.Shared.GameObjects
/// <seealso cref="IComponent" />
public interface IComponentFactory
{
event Action<IComponentRegistration> ComponentAdded;
event Action<(IComponentRegistration, Type)> ComponentReferenceAdded;
event Action<ComponentRegistration> ComponentAdded;
event Action<ComponentRegistration, CompIdx> ComponentReferenceAdded;
event Action<string> ComponentIgnoreAdded;
/// <summary>
@@ -67,7 +67,7 @@ namespace Robust.Shared.GameObjects
/// This will be null if the network Ids have not been generated yet.
/// </remarks>
/// <seealso cref="GenerateNetIds"/>
IReadOnlyList<IComponentRegistration>? NetworkedComponents { get; }
IReadOnlyList<ComponentRegistration>? NetworkedComponents { get; }
/// <summary>
/// Get whether a component is available right now.
@@ -105,6 +105,8 @@ namespace Robust.Shared.GameObjects
/// </exception>
IComponent GetComponent(Type componentType);
IComponent GetComponent(CompIdx componentType);
/// <summary>
/// Gets a new component instantiated of the specified type.
/// </summary>
@@ -154,7 +156,7 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no component exists with the given name <see cref="componentName"/>.
/// </exception>
IComponentRegistration GetRegistration(string componentName, bool ignoreCase = false);
ComponentRegistration GetRegistration(string componentName, bool ignoreCase = false);
/// <summary>
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
@@ -163,7 +165,7 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no component exists of type <see cref="reference"/>.
/// </exception>
IComponentRegistration GetRegistration(Type reference);
ComponentRegistration GetRegistration(Type reference);
/// <summary>
/// Gets the registration belonging to a component, throwing an exception if it does not exist.
@@ -172,7 +174,7 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no component of type <see cref="T"/> exists.
/// </exception>
IComponentRegistration GetRegistration<T>() where T : IComponent, new();
ComponentRegistration GetRegistration<T>() where T : IComponent, new();
/// <summary>
/// Gets the registration belonging to a component, throwing an
@@ -183,7 +185,7 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no component with id <see cref="netID"/> exists.
/// </exception>
IComponentRegistration GetRegistration(ushort netID);
ComponentRegistration GetRegistration(ushort netID);
/// <summary>
/// Gets the registration of a component, throwing an exception if
@@ -194,7 +196,9 @@ namespace Robust.Shared.GameObjects
/// <exception cref="UnknownComponentException">
/// Thrown if no registration exists for component <see cref="component"/>.
/// </exception>
IComponentRegistration GetRegistration(IComponent component);
ComponentRegistration GetRegistration(IComponent component);
ComponentRegistration GetRegistration(CompIdx idx);
/// <summary>
/// Tries to get the registration belonging to a component.
@@ -203,7 +207,7 @@ namespace Robust.Shared.GameObjects
/// <param name="registration">The registration if found, null otherwise.</param>
/// <param name="ignoreCase">Whether or not to ignore casing on <see cref="componentName"/></param>
/// <returns>true it found, false otherwise.</returns>
bool TryGetRegistration(string componentName, [NotNullWhen(true)] out IComponentRegistration? registration, bool ignoreCase = false);
bool TryGetRegistration(string componentName, [NotNullWhen(true)] out ComponentRegistration? registration, bool ignoreCase = false);
/// <summary>
/// Tries to get the registration belonging to a component.
@@ -211,7 +215,7 @@ namespace Robust.Shared.GameObjects
/// <param name="reference">A reference corresponding to the component to look up.</param>
/// <param name="registration">The registration if found, null otherwise.</param>
/// <returns>true it found, false otherwise.</returns>
bool TryGetRegistration(Type reference, [NotNullWhen(true)] out IComponentRegistration? registration);
bool TryGetRegistration(Type reference, [NotNullWhen(true)] out ComponentRegistration? registration);
/// <summary>
/// Tries to get the registration belonging to a component.
@@ -219,7 +223,7 @@ namespace Robust.Shared.GameObjects
/// <typeparam name="T">A type referencing the component.</typeparam>
/// <param name="registration">The registration if found, null otherwise.</param>
/// <returns>true it found, false otherwise.</returns>
bool TryGetRegistration<T>([NotNullWhen(true)] out IComponentRegistration? registration) where T : IComponent, new();
bool TryGetRegistration<T>([NotNullWhen(true)] out ComponentRegistration? registration) where T : IComponent, new();
/// <summary>
/// Tries to get the registration belonging to a component.
@@ -227,7 +231,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(ushort netID, [NotNullWhen(true)] out IComponentRegistration? registration);
bool TryGetRegistration(ushort netID, [NotNullWhen(true)] out ComponentRegistration? registration);
/// <summary>
/// Tries to get the registration of a component.
@@ -235,48 +239,16 @@ namespace Robust.Shared.GameObjects
/// <param name="component">An instance of the component.</param>
/// <param name="registration">The registration if found, null otherwise.</param>
/// <returns>true it found, false otherwise.</returns>
bool TryGetRegistration(IComponent component, [NotNullWhen(true)] out IComponentRegistration? registration);
bool TryGetRegistration(IComponent component, [NotNullWhen(true)] out ComponentRegistration? registration);
/// <summary>
/// Automatically create registrations for all components with a <see cref="RegisterComponentAttribute" />
/// </summary>
void DoAutoRegistrations();
IEnumerable<Type> GetAllRefTypes();
IEnumerable<CompIdx> GetAllRefTypes();
void GenerateNetIds();
}
/// <summary>
/// Represents a component registered into a <see cref="IComponentFactory" />.
/// </summary>
/// <seealso cref="IComponentFactory" />
/// <seealso cref="IComponent" />
public interface IComponentRegistration
{
/// <summary>
/// The name of the component.
/// This is used as the <c>type</c> field in the component declarations if entity prototypes.
/// </summary>
/// <seealso cref="IComponent.Name" />
string Name { get; }
/// <summary>
/// ID used to reference the component type across the network.
/// If null, no network synchronization will be available for this component.
/// </summary>
/// <seealso cref="NetworkedComponentAttribute" />
ushort? NetID { get; }
/// <summary>
/// The type that will be instantiated if this component is created.
/// </summary>
Type Type { get; }
/// <summary>
/// A list of type references that can be used to get a reference to an instance of this component,
/// for methods like GetComponent.
/// These are not unique and can overlap with other components.
/// </summary>
IReadOnlyList<Type> References { get; }
Type IdxToType(CompIdx idx);
}
}
@@ -10,18 +10,18 @@ namespace Robust.Shared.GameObjects
/// <summary>
/// A component was added to the manager.
/// </summary>
event EventHandler<ComponentEventArgs>? ComponentAdded;
event Action<AddedComponentEventArgs>? ComponentAdded;
/// <summary>
/// A component was removed from the manager.
/// </summary>
event EventHandler<ComponentEventArgs>? ComponentRemoved;
event Action<RemovedComponentEventArgs>? ComponentRemoved;
/// <summary>
/// A component was deleted. This is usually deferred until some time after it was removed.
/// Usually you will want to subscribe to <see cref="ComponentRemoved"/>.
/// </summary>
event EventHandler<ComponentEventArgs>? ComponentDeleted;
event Action<DeletedComponentEventArgs>? ComponentDeleted;
/// <summary>
/// Calls Initialize() on all registered components of the entity.
@@ -46,7 +46,7 @@ namespace Robust.Shared.GameObjects
/// </summary>
/// <remarks>
/// This function returns a disposable initialize handle that you can use in a <see langword="using" /> statement, to set up a component
/// before initialization is ran on it.
/// before initialization is ran on it.
/// </remarks>
/// <typeparam name="T">Concrete component type to add.</typeparam>
/// <param name="uid">Entity being modified.</param>
@@ -189,6 +189,14 @@ namespace Robust.Shared.GameObjects
/// <returns>The component of Type from the Entity.</returns>
T GetComponent<T>(EntityUid uid);
/// <summary>
/// Returns the component of a specific type.
/// </summary>
/// <param name="uid">Entity UID to look on.</param>
/// <param name="type">A trait or component type to check for.</param>
/// <returns>The component of Type from the Entity.</returns>
IComponent GetComponent(EntityUid uid, CompIdx type);
/// <summary>
/// Returns the component of a specific type.
/// </summary>
+5 -5
View File
@@ -45,11 +45,11 @@ namespace Robust.Shared.GameObjects
#region Entity Management
event EventHandler<EntityUid>? EntityAdded;
event EventHandler<EntityUid>? EntityInitialized;
event EventHandler<EntityUid>? EntityStarted;
event EventHandler<EntityUid>? EntityDeleted;
event EventHandler<EntityUid>? EntityDirtied; // only raised after initialization
event Action<EntityUid>? EntityAdded;
event Action<EntityUid>? EntityInitialized;
event Action<EntityUid>? EntityStarted;
event Action<EntityUid>? EntityDeleted;
event Action<EntityUid>? EntityDirtied; // only raised after initialization
EntityUid CreateEntityUninitialized(string? prototypeName, EntityUid euid);
@@ -204,7 +204,7 @@ namespace Robust.Shared.GameObjects
RemoveFromEntityTree(args.Owner, false);
}
private void OnEntityInit(object? sender, EntityUid uid)
private void OnEntityInit(EntityUid uid)
{
if (_container.IsEntityInContainer(uid)) return;
@@ -63,7 +63,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations
components[compType] = read;
}
var referenceTypes = new List<Type>();
var referenceTypes = new List<CompIdx>();
// Assert that there are no conflicting component references.
foreach (var componentName in components.Keys)
{
@@ -125,7 +125,7 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations
list.Add(serializationManager.ValidateNode(type, copy, context));
}
var referenceTypes = new List<Type>();
var referenceTypes = new List<CompIdx>();
// Assert that there are no conflicting component references.
foreach (var componentName in components.Keys)
@@ -204,9 +204,9 @@ namespace Robust.Shared.Serialization.TypeSerializers.Implementations
return newCompReg;
}
private Dictionary<IComponentRegistration, int> ToTypeIndexedDictionary(SequenceDataNode node, IComponentFactory componentFactory)
private Dictionary<ComponentRegistration, int> ToTypeIndexedDictionary(SequenceDataNode node, IComponentFactory componentFactory)
{
var dict = new Dictionary<IComponentRegistration, int>();
var dict = new Dictionary<ComponentRegistration, int>();
for (var i = 0; i < node.Count; i++)
{
var mapping = (MappingDataNode)node[i];
+3 -11
View File
@@ -1,5 +1,4 @@
#nullable enable
using System;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
@@ -18,7 +17,7 @@ namespace Robust.Shared.Utility
/// <typeparam name="TKey">The type of key.</typeparam>
/// <typeparam name="TValue">The type of value.</typeparam>
/// <seealso cref="UniqueIndexExtensions"/>
internal interface IUniqueIndex<TKey, TValue> : IEnumerable<KeyValuePair<TKey, ISet<TValue>>> where TKey : notnull
internal interface IUniqueIndex<TKey, TValue> : IEnumerable<KeyValuePair<TKey, HashSet<TValue>>> where TKey : notnull
{
/// <summary>
@@ -88,13 +87,6 @@ namespace Robust.Shared.Utility
[CollectionAccess(CollectionAccessType.UpdatedContent)]
void Touch(TKey key);
/// <summary>
/// Makes a given key's set immutable.
/// </summary>
/// <param name="key">A given key.</param>
[CollectionAccess(CollectionAccessType.UpdatedContent)]
bool Freeze(TKey key);
/// <summary>
/// Initializes the index from a collection of keys.
/// </summary>
@@ -109,7 +101,7 @@ namespace Robust.Shared.Utility
/// <param name="index">An equivalent collection.</param>
/// <exception cref="InvalidOperationException">Already initialized.</exception>
[CollectionAccess(CollectionAccessType.UpdatedContent)]
void Initialize(IEnumerable<KeyValuePair<TKey, ISet<TValue>>> index);
void Initialize(IEnumerable<KeyValuePair<TKey, HashSet<TValue>>> index);
}
+13 -32
View File
@@ -1,5 +1,4 @@
#nullable enable
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
@@ -28,7 +27,7 @@ namespace Robust.Shared.Utility
public struct UniqueIndex<TKey, TValue> : IUniqueIndex<TKey, TValue> where TKey : notnull
{
private ImmutableDictionary<TKey, ISet<TValue>>? _index;
private ImmutableDictionary<TKey, HashSet<TValue>>? _index;
/// <inheritdoc />
public int KeyCount => _index?.Count ?? 0;
@@ -36,12 +35,12 @@ namespace Robust.Shared.Utility
/// <inheritdoc />
public bool Add(TKey key, TValue value)
{
ISet<TValue>? set;
HashSet<TValue>? set;
if (_index is null)
{
set = new HashSet<TValue> {value};
_index = ImmutableDictionary.CreateRange(new[] {new KeyValuePair<TKey, ISet<TValue>>(key, set)});
_index = ImmutableDictionary.CreateRange(new[] {new KeyValuePair<TKey, HashSet<TValue>>(key, set)});
return true;
}
@@ -57,12 +56,12 @@ namespace Robust.Shared.Utility
/// <inheritdoc />
public int AddRange(TKey key, IEnumerable<TValue> values)
{
ISet<TValue>? set;
HashSet<TValue>? set;
if (_index is null)
{
set = new HashSet<TValue>(values);
_index = ImmutableDictionary.CreateRange(new[] {new KeyValuePair<TKey, ISet<TValue>>(key, set)});
_index = ImmutableDictionary.CreateRange(new[] {new KeyValuePair<TKey, HashSet<TValue>>(key, set)});
return set.Count;
}
@@ -152,37 +151,19 @@ namespace Robust.Shared.Utility
/// <inheritdoc />
public void Touch(TKey key)
{
_index ??= ImmutableDictionary<TKey, ISet<TValue>>.Empty;
_index ??= ImmutableDictionary<TKey, HashSet<TValue>>.Empty;
if (_index.ContainsKey(key)) return;
_index = _index.Add(key, new HashSet<TValue>());
}
/// <inheritdoc />
public bool Freeze(TKey key)
{
if (_index is null)
{
return false;
}
if (!_index.TryGetValue(key, out var set)
|| set is ImmutableHashSet<TValue>)
{
return false;
}
_index = _index.SetItem(key, ImmutableHashSet.CreateRange(set));
return true;
}
/// <inheritdoc />
public void Initialize(IEnumerable<TKey> keys)
=> Initialize(keys.Select(k => new KeyValuePair<TKey, ISet<TValue>>(k, new HashSet<TValue>())));
=> Initialize(keys.Select(k => new KeyValuePair<TKey, HashSet<TValue>>(k, new HashSet<TValue>())));
/// <inheritdoc />
public void Initialize(IEnumerable<KeyValuePair<TKey, ISet<TValue>>> index)
public void Initialize(IEnumerable<KeyValuePair<TKey, HashSet<TValue>>> index)
{
if (_index != null) throw new InvalidOperationException("Already initialized.");
@@ -193,11 +174,11 @@ namespace Robust.Shared.Utility
{
get
{
ISet<TValue>? set;
HashSet<TValue>? set;
if (_index is null)
{
_index = ImmutableDictionary<TKey, ISet<TValue>>.Empty;
_index = ImmutableDictionary<TKey, HashSet<TValue>>.Empty;
}
else
{
@@ -215,14 +196,14 @@ namespace Robust.Shared.Utility
/// <inheritdoc cref="IEnumerable{T}"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerator<KeyValuePair<TKey, ISet<TValue>>> GetEnumerator()
public IEnumerator<KeyValuePair<TKey, HashSet<TValue>>> GetEnumerator()
{
if (_index != null)
{
return _index.GetEnumerator();
}
return Enumerable.Empty<KeyValuePair<TKey, ISet<TValue>>>().GetEnumerator();
return Enumerable.Empty<KeyValuePair<TKey, HashSet<TValue>>>().GetEnumerator();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
+7 -24
View File
@@ -1,8 +1,6 @@
#nullable enable
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
@@ -29,10 +27,10 @@ namespace Robust.Shared.Utility
{
[NotNull]
private readonly Dictionary<TKey, ISet<TValue>> _index;
private readonly Dictionary<TKey, HashSet<TValue>> _index;
public UniqueIndexHkm(int capacity)
=> _index = new Dictionary<TKey, ISet<TValue>>(capacity);
=> _index = new Dictionary<TKey, HashSet<TValue>>(capacity);
/// <inheritdoc />
public int KeyCount => _index.Count;
@@ -142,27 +140,12 @@ namespace Robust.Shared.Utility
_index.Add(key, new HashSet<TValue>());
}
/// <inheritdoc />
public bool Freeze(TKey key)
{
InitializedCheck();
if (!_index.TryGetValue(key, out var set)
|| set is ImmutableHashSet<TValue>)
{
return false;
}
_index[key] = ImmutableHashSet.CreateRange(set);
return true;
}
/// <inheritdoc />
public void Initialize(IEnumerable<TKey> keys)
=> Initialize(keys.Select(k => new KeyValuePair<TKey, ISet<TValue>>(k, new HashSet<TValue>())));
=> Initialize(keys.Select(k => new KeyValuePair<TKey, HashSet<TValue>>(k, new HashSet<TValue>())));
/// <inheritdoc />
public void Initialize(IEnumerable<KeyValuePair<TKey, ISet<TValue>>> index)
public void Initialize(IEnumerable<KeyValuePair<TKey, HashSet<TValue>>> index)
{
InitializedCheck();
@@ -175,7 +158,7 @@ namespace Robust.Shared.Utility
}
/// <inheritdoc />
public ISet<TValue> this[TKey key]
public HashSet<TValue> this[TKey key]
{
get
{
@@ -191,7 +174,7 @@ namespace Robust.Shared.Utility
/// <inheritdoc cref="IEnumerable{T}"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerator<KeyValuePair<TKey, ISet<TValue>>> GetEnumerator()
public IEnumerator<KeyValuePair<TKey, HashSet<TValue>>> GetEnumerator()
{
InitializedCheck();
+20 -12
View File
@@ -84,18 +84,6 @@ namespace Robust.UnitTesting
var entMan = IoCManager.Resolve<IEntityManager>();
var mapMan = IoCManager.Resolve<IMapManager>();
// So by default EntityManager does its own EntitySystemManager initialize during Startup.
// We want to bypass this and load our own systems hence we will manually initialize it here.
entMan.Initialize();
mapMan.Initialize();
systems.Initialize();
IoCManager.Resolve<IReflectionManager>().LoadAssemblies(assemblies);
var modLoader = IoCManager.Resolve<TestingModLoader>();
modLoader.Assemblies = contentAssemblies;
modLoader.TryLoadModulesFrom(ResourcePath.Root, "");
// Required components for the engine to work
var compFactory = IoCManager.Resolve<IComponentFactory>();
@@ -124,6 +112,26 @@ namespace Robust.UnitTesting
compFactory.RegisterClass<FixturesComponent>();
}
if (!compFactory.AllRegisteredTypes.Contains(typeof(EntityLookupComponent)))
{
compFactory.RegisterClass<EntityLookupComponent>();
}
// So by default EntityManager does its own EntitySystemManager initialize during Startup.
// We want to bypass this and load our own systems hence we will manually initialize it here.
entMan.Initialize();
// RobustUnitTest is complete hot garbage.
// This makes EventTables ignore *all* the screwed up component abuse it causes.
entMan.EventBus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
mapMan.Initialize();
systems.Initialize();
IoCManager.Resolve<IReflectionManager>().LoadAssemblies(assemblies);
var modLoader = IoCManager.Resolve<TestingModLoader>();
modLoader.Assemblies = contentAssemblies;
modLoader.TryLoadModulesFrom(ResourcePath.Root, "");
entMan.Startup();
mapMan.Startup();
}
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Moq;
using NUnit.Framework;
using Robust.Shared.GameObjects;
@@ -19,14 +20,17 @@ namespace Robust.UnitTesting.Shared.GameObjects
var entUid = new EntityUid(7);
var compInstance = new MetaDataComponent();
var compRegistration = new Mock<IComponentRegistration>();
var compRegistration = new ComponentRegistration(
"MetaData",
typeof(MetaDataComponent),
CompIdx.Index<MetaDataComponent>());
var entManMock = new Mock<IEntityManager>();
var compFacMock = new Mock<IComponentFactory>();
compRegistration.Setup(m => m.References).Returns(new List<Type> {typeof(MetaDataComponent)});
compFacMock.Setup(m => m.GetRegistration(typeof(MetaDataComponent))).Returns(compRegistration.Object);
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<MetaDataComponent>())).Returns(compRegistration);
compFacMock.Setup(m => m.GetAllRefTypes()).Returns(new[] { CompIdx.Index<MetaDataComponent>() });
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
IComponent? outIComponent = compInstance;
@@ -36,15 +40,19 @@ namespace Robust.UnitTesting.Shared.GameObjects
entManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent)))
.Returns(compInstance);
entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index<MetaDataComponent>()))
.Returns(compInstance);
var bus = new EntityEventBus(entManMock.Object);
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
// Subscribe
int calledCount = 0;
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
// add a component to the system
entManMock.Raise(m=>m.EntityAdded += null, entManMock.Object, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(compInstance, entUid));
entManMock.Raise(m => m.EntityAdded += null, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid)));
// Raise
var evntArgs = new TestEvent(5);
@@ -71,12 +79,15 @@ namespace Robust.UnitTesting.Shared.GameObjects
var entManMock = new Mock<IEntityManager>();
var compRegistration = new Mock<IComponentRegistration>();
var compRegistration = new ComponentRegistration(
"MetaData",
typeof(MetaDataComponent),
CompIdx.Index<MetaDataComponent>());
var compFacMock = new Mock<IComponentFactory>();
compRegistration.Setup(m => m.References).Returns(new List<Type> {typeof(MetaDataComponent)});
compFacMock.Setup(m => m.GetRegistration(typeof(MetaDataComponent))).Returns(compRegistration.Object);
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<MetaDataComponent>())).Returns(compRegistration);
compFacMock.Setup(m => m.GetAllRefTypes()).Returns(new[] { CompIdx.Index<MetaDataComponent>() });
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
IComponent? outIComponent = compInstance;
@@ -87,6 +98,7 @@ namespace Robust.UnitTesting.Shared.GameObjects
.Returns(compInstance);
var bus = new EntityEventBus(entManMock.Object);
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
// Subscribe
int calledCount = 0;
@@ -94,8 +106,8 @@ namespace Robust.UnitTesting.Shared.GameObjects
bus.UnsubscribeLocalEvent<MetaDataComponent, TestEvent>();
// add a component to the system
entManMock.Raise(m => m.EntityAdded += null, entManMock.Object, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(compInstance, entUid));
entManMock.Raise(m => m.EntityAdded += null, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid)));
// Raise
var evntArgs = new TestEvent(5);
@@ -121,12 +133,15 @@ namespace Robust.UnitTesting.Shared.GameObjects
compInstance.Owner = entUid;
var compRegistration = new Mock<IComponentRegistration>();
var compRegistration = new ComponentRegistration(
"MetaData",
typeof(MetaDataComponent),
CompIdx.Index<MetaDataComponent>());
var compFacMock = new Mock<IComponentFactory>();
compRegistration.Setup(m => m.References).Returns(new List<Type> {typeof(MetaDataComponent)});
compFacMock.Setup(m => m.GetRegistration(typeof(MetaDataComponent))).Returns(compRegistration.Object);
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<MetaDataComponent>())).Returns(compRegistration);
compFacMock.Setup(m => m.GetAllRefTypes()).Returns(new[] { CompIdx.Index<MetaDataComponent>() });
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
IComponent? outIComponent = compInstance;
@@ -137,14 +152,15 @@ namespace Robust.UnitTesting.Shared.GameObjects
.Returns(compInstance);
var bus = new EntityEventBus(entManMock.Object);
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
// Subscribe
int calledCount = 0;
bus.SubscribeLocalEvent<MetaDataComponent, ComponentInit>(HandleTestEvent);
// add a component to the system
entManMock.Raise(m=>m.EntityAdded += null, entManMock.Object, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(compInstance, entUid));
entManMock.Raise(m => m.EntityAdded += null, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid)));
// Raise
((IEventBus)bus).RaiseComponentEvent(compInstance, new ComponentInit());
@@ -168,23 +184,30 @@ namespace Robust.UnitTesting.Shared.GameObjects
var entManMock = new Mock<IEntityManager>();
var compFacMock = new Mock<IComponentFactory>();
List<CompIdx> allRefTypes = new();
void Setup<T>(out T instance) where T : IComponent, new()
{
IComponent? inst = instance = new T();
var reg = new Mock<IComponentRegistration>();
reg.Setup(m => m.References).Returns(new Type[] {typeof(T)});
var reg = new ComponentRegistration(
typeof(T).Name,
typeof(T),
CompIdx.Index<T>());
compFacMock.Setup(m => m.GetRegistration(typeof(T))).Returns(reg.Object);
compFacMock.Setup(m => m.GetRegistration(CompIdx.Index<T>())).Returns(reg);
entManMock.Setup(m => m.TryGetComponent(entUid, typeof(T), out inst)).Returns(true);
entManMock.Setup(m => m.GetComponent(entUid, typeof(T))).Returns(inst);
allRefTypes.Add(CompIdx.Index<T>());
}
Setup<OrderAComponent>(out var instA);
Setup<OrderBComponent>(out var instB);
Setup<OrderCComponent>(out var instC);
compFacMock.Setup(m => m.GetAllRefTypes()).Returns(allRefTypes.ToArray());
entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object);
var bus = new EntityEventBus(entManMock.Object);
bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare();
// Subscribe
var a = false;
@@ -212,10 +235,10 @@ namespace Robust.UnitTesting.Shared.GameObjects
bus.SubscribeLocalEvent<OrderCComponent, TestEvent>(HandlerC, typeof(OrderCComponent));
// add a component to the system
entManMock.Raise(m=>m.EntityAdded += null, entManMock.Object, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(instA, entUid));
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(instB, entUid));
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(instC, entUid));
entManMock.Raise(m => m.EntityAdded += null, entUid);
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid)));
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid)));
entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid)));
// Raise
var evntArgs = new TestEvent(5);
@@ -11,7 +11,9 @@ namespace Robust.UnitTesting.Shared.GameObjects
{
private static EntityEventBus BusFactory()
{
var compFacMock = new Mock<IComponentFactory>();
var entManMock = new Mock<IEntityManager>();
entManMock.SetupGet(e => e.ComponentFactory).Returns(compFacMock.Object);
var bus = new EntityEventBus(entManMock.Object);
return bus;
}