diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 73aad122d..a97461095 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -35,6 +35,8 @@ END TEMPLATE--> ### Breaking changes +* Events that are raised via `IEventBus.RaiseComponentEvent()` now **must** be annotated with the `ComponentEventAttribute`. + * By default, events annotated with this attribute can **only** be raised via `IEventBus.RaiseComponentEvent()`. This can be configured via `ComponentEventAttribute.Exclusive` * StartCollide and EndCollide events are now buffered until the end of physics substeps instead of being raised during the CollideContacts step. EndCollide events are double-buffered and any new ones raised while the events are being dispatched will now go out on the next tick / substep. ### New features diff --git a/Robust.Server/GameStates/PvsSystem.GetStates.cs b/Robust.Server/GameStates/PvsSystem.GetStates.cs index 04afedeb2..87786ceac 100644 --- a/Robust.Server/GameStates/PvsSystem.GetStates.cs +++ b/Robust.Server/GameStates/PvsSystem.GetStates.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -20,11 +22,11 @@ internal sealed partial class PvsSystem /// New entity State for the given entity. private EntityState GetEntityState(ICommonSession? player, EntityUid entityUid, GameTick fromTick, MetaDataComponent meta) { - var bus = EntityManager.EventBus; var changed = new List(); bool sendCompList = meta.LastComponentRemoved > fromTick; HashSet? netComps = sendCompList ? new() : null; + var stateEv = new ComponentGetState(player, fromTick); foreach (var (netId, component) in meta.NetComponents) { @@ -41,15 +43,15 @@ internal sealed partial class PvsSystem if (component.LastModifiedTick <= fromTick) { - if (sendCompList && (!component.SessionSpecific || player == null || EntityManager.CanGetComponentState(bus, component, player))) + if (sendCompList && (!component.SessionSpecific || player == null || EntityManager.CanGetComponentState(component, player))) netComps!.Add(netId); continue; } - if (component.SessionSpecific && player != null && !EntityManager.CanGetComponentState(bus, component, player)) + if (component.SessionSpecific && player != null && !EntityManager.CanGetComponentState(component, player)) continue; - var state = EntityManager.GetComponentState(bus, component, player, fromTick); + var state = ComponentState(entityUid, component, netId, ref stateEv); changed.Add(new ComponentChange(netId, state, component.LastModifiedTick)); if (state != null) @@ -66,13 +68,23 @@ internal sealed partial class PvsSystem return entState; } + private IComponentState? ComponentState(EntityUid uid, IComponent comp, ushort netId, ref ComponentGetState stateEv) + { + DebugTools.Assert(comp.NetSyncEnabled, $"Attempting to get component state for an un-synced component: {comp.GetType()}"); + stateEv.State = null; + _getStateHandlers![netId]?.Invoke(uid, comp, ref Unsafe.As(ref stateEv)); + var state = stateEv.State; + return state; + } + /// /// Variant of that includes all entity data, including data that can be inferred implicitly from the entity prototype. /// private EntityState GetFullEntityState(ICommonSession player, EntityUid entityUid, MetaDataComponent meta) { - var bus = EntityManager.EventBus; + var bus = EntityManager.EventBusInternal; var changed = new List(); + var stateEv = new ComponentGetState(player, GameTick.Zero); HashSet netComps = new(); @@ -86,7 +98,7 @@ internal sealed partial class PvsSystem if (component.SessionSpecific && !EntityManager.CanGetComponentState(bus, component, player)) continue; - var state = EntityManager.GetComponentState(bus, component, player, GameTick.Zero); + var state = ComponentState(entityUid, component, netId, ref stateEv); DebugTools.Assert(state is not IComponentDeltaState); changed.Add(new ComponentChange(netId, state, component.LastModifiedTick)); netComps.Add(netId); diff --git a/Robust.Server/GameStates/PvsSystem.cs b/Robust.Server/GameStates/PvsSystem.cs index e68ff17e2..26ca25493 100644 --- a/Robust.Server/GameStates/PvsSystem.cs +++ b/Robust.Server/GameStates/PvsSystem.cs @@ -14,6 +14,7 @@ using Robust.Server.Replays; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; @@ -105,6 +106,7 @@ internal sealed partial class PvsSystem : EntitySystem private bool _async; private DefaultObjectPool _threadResourcesPool = default!; + private EntityEventBus.DirectedEventHandler?[]? _getStateHandlers; private static readonly Histogram Histogram = Metrics.CreateHistogram("robust_game_state_update_usage", "Amount of time spent processing different parts of the game state update", new HistogramConfiguration @@ -173,6 +175,7 @@ internal sealed partial class PvsSystem : EntitySystem ClearPvsData(); ShutdownDirty(); + _getStateHandlers = null; } public override void Update(float frameTime) @@ -185,6 +188,8 @@ internal sealed partial class PvsSystem : EntitySystem /// internal void SendGameStates(ICommonSession[] players) { + _getStateHandlers ??= EntityManager.EventBusInternal.GetNetCompEventHandlers(); + // Wait for pending jobs and process disconnected players ProcessDisconnections(); diff --git a/Robust.Shared/GameObjects/CompIdx.cs b/Robust.Shared/GameObjects/CompIdx.cs index 136f45bb2..ab7d537c9 100644 --- a/Robust.Shared/GameObjects/CompIdx.cs +++ b/Robust.Shared/GameObjects/CompIdx.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Threading; using Robust.Shared.Maths; @@ -33,7 +34,7 @@ public readonly struct CompIdx : IEquatable var curLength = array.Length; if (curLength <= idx.Value) { - var newLength = MathHelper.NextPowerOfTwo(Math.Max(8, idx.Value)); + var newLength = MathHelper.NextPowerOfTwo(Math.Max(8, idx.Value + 1)); Array.Resize(ref array, newLength); } diff --git a/Robust.Shared/GameObjects/EntityEventBus.Common.cs b/Robust.Shared/GameObjects/EntityEventBus.Common.cs index 1d8c22859..55ef2dc0c 100644 --- a/Robust.Shared/GameObjects/EntityEventBus.Common.cs +++ b/Robust.Shared/GameObjects/EntityEventBus.Common.cs @@ -12,7 +12,7 @@ namespace Robust.Shared.GameObjects; internal sealed partial class EntityEventBus : IEventBus { - private IEntityManager _entMan; + private EntityManager _entMan; private IComponentFactory _comFac; private IReflectionManager _reflection; @@ -34,18 +34,18 @@ internal sealed partial class EntityEventBus : IEventBus /// /// Array of component events and their handlers. The array is indexed by a component's /// , while the dictionary is indexed by the event type. This does not include events - /// with the + /// with the , unless is false. /// - internal FrozenDictionary[] _eventSubs = default!; + private FrozenDictionary[] _eventSubs = default!; /// - /// Variant of that also includes events with the + /// Variant of that only includes events with the /// - internal FrozenDictionary[] _compEventSubs = default!; + private FrozenDictionary[] _compEventSubs = default!; // pre-freeze event subscription data - internal Dictionary?[] _eventSubsUnfrozen = - Array.Empty>(); + private Dictionary?[] _eventSubsUnfrozen = []; + private Dictionary?[] _compEventSubsUnfrozen = []; /// /// Inverse of , mapping event types to sets of components. diff --git a/Robust.Shared/GameObjects/EntityEventBus.Directed.cs b/Robust.Shared/GameObjects/EntityEventBus.Directed.cs index e20e72603..f9e97bcd2 100644 --- a/Robust.Shared/GameObjects/EntityEventBus.Directed.cs +++ b/Robust.Shared/GameObjects/EntityEventBus.Directed.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Frozen; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Robust.Shared.Collections; @@ -101,9 +101,6 @@ namespace Robust.Shared.GameObjects { internal delegate void DirectedEventHandler(EntityUid uid, IComponent comp, ref Unit args); - private delegate void DirectedEventHandler(EntityUid uid, IComponent comp, ref TEvent args) - where TEvent : notnull; - /// /// Max size of a components event subscription linked list. /// Used to limit the stackalloc in @@ -118,7 +115,7 @@ namespace Robust.Shared.GameObjects /// /// The entity manager to watch for entity/component events. /// The reflection manager to use when finding derived types. - public EntityEventBus(IEntityManager entMan, IReflectionManager reflection) + public EntityEventBus(EntityManager entMan, IReflectionManager reflection) { _entMan = entMan; _comFac = entMan.ComponentFactory; @@ -176,13 +173,8 @@ namespace Robust.Shared.GameObjects public void RaiseComponentEvent(EntityUid uid, IComponent component, CompIdx type, ref TEvent args) where TEvent : notnull { - ref var unitRef = ref Unsafe.As(ref args); - - DispatchComponent( - uid, - component, - type, - ref unitRef); + if (_compEventSubs[type.Value].TryGetValue(typeof(TEvent), out var handler)) + handler(uid, component, ref Unsafe.As(ref args)); } public void OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare() @@ -249,15 +241,13 @@ namespace Robust.Shared.GameObjects where TComp : IComponent where TEvent : notnull { - void EventHandler(EntityUid uid, IComponent comp, ref TEvent args) - => handler(uid, (TComp)comp, args); + void EventHandler(EntityUid uid, IComponent comp, ref Unit ev) + { + ref var tev = ref Unsafe.As(ref ev); + handler(uid, (TComp) comp, tev); + } - EntSubscribe( - CompIdx.Index(), - typeof(TComp), - typeof(TEvent), - EventHandler, - null); + EntAddSubscription(CompIdx.Index(), typeof(TComp), typeof(TEvent), EventHandler); } public void SubscribeLocalEvent( @@ -268,71 +258,51 @@ namespace Robust.Shared.GameObjects where TComp : IComponent where TEvent : notnull { - void EventHandler(EntityUid uid, IComponent comp, ref TEvent args) - => handler(uid, (TComp)comp, args); + void EventHandler(EntityUid uid, IComponent comp, ref Unit ev) + { + ref var tev = ref Unsafe.As(ref ev); + handler(uid, (TComp) comp, tev); + } - var orderData = CreateOrderingData(orderType, before, after); - - EntSubscribe( - CompIdx.Index(), - typeof(TComp), - typeof(TEvent), - EventHandler, - orderData); - - RegisterCommon(typeof(TEvent), orderData, out _); + EntAddSubscription(CompIdx.Index(), typeof(TComp), typeof(TEvent), EventHandler, orderType, before, after); } public void SubscribeLocalEvent(ComponentEventRefHandler handler) where TComp : IComponent where TEvent : notnull { - void EventHandler(EntityUid uid, IComponent comp, ref TEvent args) - => handler(uid, (TComp)comp, ref args); + void EventHandler(EntityUid uid, IComponent comp, ref Unit ev) + { + ref var tev = ref Unsafe.As(ref ev); + handler(uid, (TComp) comp, ref tev); + } - EntSubscribe( - CompIdx.Index(), - typeof(TComp), - typeof(TEvent), - EventHandler, - null); + EntAddSubscription(CompIdx.Index(), typeof(TComp), typeof(TEvent), EventHandler); } public void SubscribeLocalEvent(ComponentEventRefHandler handler, Type orderType, Type[]? before = null, Type[]? after = null) where TComp : IComponent where TEvent : notnull { - void EventHandler(EntityUid uid, IComponent comp, ref TEvent args) - => handler(uid, (TComp)comp, ref args); + void EventHandler(EntityUid uid, IComponent comp, ref Unit ev) + { + ref var tev = ref Unsafe.As(ref ev); + handler(uid, (TComp) comp, ref tev); + } - var orderData = CreateOrderingData(orderType, before, after); - - EntSubscribe( - CompIdx.Index(), - typeof(TComp), - typeof(TEvent), - EventHandler, - orderData); - - RegisterCommon(typeof(TEvent), orderData, out _); + EntAddSubscription(CompIdx.Index(), typeof(TComp), typeof(TEvent), EventHandler, orderType, before, after); } public void SubscribeLocalEvent(EntityEventRefHandler handler, Type orderType, Type[]? before = null, Type[]? after = null) where TComp : IComponent where TEvent : notnull { - void EventHandler(EntityUid uid, IComponent comp, ref TEvent args) - => handler(new Entity(uid, (TComp) comp), ref args); + void EventHandler(EntityUid uid, IComponent comp, ref Unit ev) + { + ref var tev = ref Unsafe.As(ref ev); + handler(new Entity(uid, (TComp) comp), ref tev); + } - var orderData = CreateOrderingData(orderType, before, after); - - EntSubscribe( - CompIdx.Index(), - typeof(TComp), - typeof(TEvent), - EventHandler, - orderData); - - RegisterCommon(typeof(TEvent), orderData, out _); + EntAddSubscription(CompIdx.Index(), typeof(TComp), typeof(TEvent), EventHandler, orderType, before, after); } /// @@ -340,7 +310,24 @@ namespace Robust.Shared.GameObjects where TComp : IComponent where TEvent : notnull { - EntUnsubscribe(CompIdx.Index(), typeof(TEvent)); + if (!_comFac.TryGetRegistration(typeof(TComp), out _)) + { + if (!IgnoreUnregisteredComponents) + throw new InvalidOperationException($"Component is not a valid reference type: {typeof(TComp).Name}"); + + return; + } + + if (_subscriptionLock) + throw new InvalidOperationException("Subscription locked."); + + var i = CompIdx.ArrayIndex(); + + _eventSubsUnfrozen[i]!.Remove(typeof(TEvent)); + _compEventSubsUnfrozen[i]!.Remove(typeof(TEvent)); + + if (_eventSubsInv.TryGetValue(typeof(TEvent), out var t)) + t.Remove(CompIdx.Index()); } private void ComFacOnComponentsAdded(ComponentRegistration[] regs) @@ -351,6 +338,7 @@ namespace Robust.Shared.GameObjects foreach (var reg in regs) { CompIdx.RefArray(ref _eventSubsUnfrozen, reg.Idx) ??= new(); + CompIdx.RefArray(ref _compEventSubsUnfrozen, reg.Idx) ??= new(); } } @@ -374,40 +362,17 @@ namespace Robust.Shared.GameObjects _subscriptionLock = true; _eventData = _eventDataUnfrozen.ToFrozenDictionary(); - // Find last non-null entry. - var last = 0; - for (var i = 0; i < _eventSubsUnfrozen.Length; i++) - { - var entry = _eventSubsUnfrozen[i]; - if (entry != null) - last = i; - } - - // TODO PERFORMANCE - // make this only contain events that actually use comp-events - // Assuming it makes the frozen dictionaries more specialized and thus faster. - // AFAIK currently only MapInit is both a comp-event and a general event. - // It should probably be changed to just be a comp event. - _compEventSubs = _eventSubsUnfrozen - .Take(last+1) + _eventSubs = TrimNull(_eventSubsUnfrozen) .Select(dict => dict?.ToFrozenDictionary()!) .ToArray(); - _eventSubs = _eventSubsUnfrozen - .Take(last+1) - .Select(dict => dict?.Where(x => !IsComponentEvent(x.Key)).ToFrozenDictionary()!) + _compEventSubs = TrimNull(_compEventSubsUnfrozen) + .Select(dict => dict?.ToFrozenDictionary()!) .ToArray(); CalcOrdering(); } - private bool IsComponentEvent(Type t) - { - var isCompEv = _eventData[t].ComponentEvent; - DebugTools.Assert(isCompEv == t.HasCustomAttribute()); - return isCompEv; - } - public void OnComponentRemoved(in RemovedComponentEventArgs e) { EntRemoveComponent(e.BaseArgs.Owner, e.Idx); @@ -417,13 +382,15 @@ namespace Robust.Shared.GameObjects CompIdx compType, Type compTypeObj, Type eventType, - DirectedRegistration registration) + DirectedEventHandler handler, + Type? orderType = null, + Type[]? before = null, + Type[]? after = null) { if (_subscriptionLock) throw new InvalidOperationException("Subscription locked."); - if (compType.Value >= _eventSubsUnfrozen.Length - || _eventSubsUnfrozen[compType.Value] is not { } compSubs) + if (!_comFac.TryGetRegistration(compTypeObj, out _)) { if (IgnoreUnregisteredComponents) return; @@ -431,53 +398,25 @@ namespace Robust.Shared.GameObjects throw new InvalidOperationException($"Component is not a valid reference type: {compTypeObj.Name}"); } - if (compSubs.ContainsKey(eventType)) + if (eventType.GetCustomAttribute() is { } attr) { - throw new InvalidOperationException( - $"Duplicate Subscriptions for comp={compTypeObj}, event={eventType.Name}"); - } + if (!_compEventSubsUnfrozen[compType.Value]!.TryAdd(eventType, handler)) + throw new InvalidOperationException($"Duplicate Subscriptions for comp={compTypeObj}, event={eventType.Name}"); - compSubs.Add(eventType, registration); - - RegisterCommon(eventType, registration.Ordering, out var data); - data.ComponentEvent = eventType.HasCustomAttribute(); - if (!data.ComponentEvent) - _eventSubsInv.GetOrNew(eventType).Add(compType); - } - - private void EntSubscribe( - CompIdx compType, - Type compTypeObj, - Type eventType, - DirectedEventHandler handler, - OrderingData? order) - where TEvent : notnull - { - EntAddSubscription(compType, compTypeObj, eventType, new DirectedRegistration(handler, order, - (EntityUid uid, IComponent comp, ref Unit ev) => - { - ref var tev = ref Unsafe.As(ref ev); - handler(uid, comp, ref tev); - })); - } - - private void EntUnsubscribe(CompIdx compType, Type eventType) - { - if (_subscriptionLock) - throw new InvalidOperationException("Subscription locked."); - - if (compType.Value >= _eventSubsUnfrozen.Length - || _eventSubsUnfrozen[compType.Value] is not { } compSubs) - { - if (IgnoreUnregisteredComponents) + // An exclusive component-event is only raised via RaiseComponentEvent, hence it don't need a normal + // directed event subscription + if (attr.Exclusive) return; - - throw new InvalidOperationException("Trying to unsubscribe from unregistered component!"); } - var removed = compSubs.Remove(eventType); - if (removed) - _eventSubsInv[eventType].Remove(compType); + var orderData = orderType == null ? null : CreateOrderingData(orderType, before, after); + var reg = new DirectedRegistration(orderData, handler); + + if (!_eventSubsUnfrozen[compType.Value]!.TryAdd(eventType, reg)) + throw new InvalidOperationException($"Duplicate Subscriptions for comp={compTypeObj}, event={eventType.Name}"); + + RegisterCommon(eventType, reg.Ordering, out _); + _eventSubsInv.GetOrNew(eventType).Add(compType); } private void EntAddEntity(EntityUid euid) @@ -501,8 +440,6 @@ namespace Robust.Shared.GameObjects foreach (var evType in compSubs.Keys) { - DebugTools.Assert(!_eventData[evType].ComponentEvent); - if (eventTable.Free < 0) GrowEventTable(eventTable); @@ -563,7 +500,6 @@ namespace Robust.Shared.GameObjects foreach (var evType in compSubs.Keys) { - DebugTools.Assert(!_eventData[evType].ComponentEvent); ref var indices = ref CollectionsMarshal.GetValueRefOrNullRef(eventTable.EventIndices, evType); if (Unsafe.IsNullRef(ref indices)) { @@ -667,17 +603,6 @@ namespace Robust.Shared.GameObjects } } - private void DispatchComponent( - EntityUid euid, - IComponent component, - CompIdx baseType, - ref Unit args) - where TEvent : notnull - { - if (_compEventSubs[baseType.Value].TryGetValue(typeof(TEvent), out var reg)) - reg.Handler(euid, component, ref args); - } - public void ClearSubscriptions() { _subscriptionLock = false; @@ -691,6 +616,10 @@ namespace Robust.Shared.GameObjects { sub?.Clear(); } + foreach (var sub in _compEventSubsUnfrozen) + { + sub?.Clear(); + } } public void Dispose() @@ -705,22 +634,14 @@ namespace Robust.Shared.GameObjects _compEventSubs = null!; _eventSubs = null!; _eventSubsUnfrozen = null!; + _compEventSubsUnfrozen = null!; _eventSubsInv = null!; } - internal sealed class DirectedRegistration : OrderedRegistration + internal sealed class DirectedRegistration(OrderingData? ordering, DirectedEventHandler handler) + : OrderedRegistration(ordering) { - public readonly Delegate Original; - public readonly DirectedEventHandler Handler; - - public DirectedRegistration( - Delegate original, - OrderingData? ordering, - DirectedEventHandler handler) : base(ordering) - { - Original = original; - Handler = handler; - } + public readonly DirectedEventHandler Handler = handler; public void SetOrder(int order) { @@ -753,6 +674,48 @@ namespace Robust.Shared.GameObjects public int Next; public CompIdx Component; } + + /// + /// Return a new array with any trailing null entries removed. + /// + public static T[] TrimNull(T[] input) + { + // Find last non-null entry. + var last = 0; + for (var i = 0; i < input.Length; i++) + { + var entry = input[i]; + if (entry != null) + last = i; + } + + return input[..(last + 1)]; + } + + /// + /// Get an array of event handlers for a given component event, indexed by the component's net-id. + /// + /// + /// For most events, this will generally be a pretty sparse array, with most entries being null. However, for + /// the get and handle state events, this array will be relatively dense and helps save PVS a lot of save a + /// FrozenDictionary lookups. + /// + internal DirectedEventHandler?[] GetNetCompEventHandlers() + { + DebugTools.Assert(_subscriptionLock); + DebugTools.Assert(typeof(TEvent).HasCustomAttribute()); + + var netComps = _comFac.NetworkedComponents!; + var result = new DirectedEventHandler?[netComps.Count]; + + for (var i = 0; i < netComps.Count; i++) + { + var reg = netComps[i]; + result[i] = _compEventSubs[reg.Idx.Value].GetValueOrDefault(typeof(TEvent)); + } + + return result; + } } /// diff --git a/Robust.Shared/GameObjects/EntityManager.Components.cs b/Robust.Shared/GameObjects/EntityManager.Components.cs index bb0dfe520..3d7323717 100644 --- a/Robust.Shared/GameObjects/EntityManager.Components.cs +++ b/Robust.Shared/GameObjects/EntityManager.Components.cs @@ -406,7 +406,7 @@ namespace Robust.Shared.GameObjects var eventArgs = new AddedComponentEventArgs(new ComponentEventArgs(component, uid), reg); ComponentAdded?.Invoke(eventArgs); - _eventBus.OnComponentAdded(eventArgs); + EventBusInternal.OnComponentAdded(eventArgs); LifeAddToEntity(uid, component, reg.Idx); @@ -427,7 +427,7 @@ namespace Robust.Shared.GameObjects LifeStartup(uid, component, reg.Idx); if (metadata.EntityLifeStage >= EntityLifeStage.MapInitialized) - EventBus.RaiseComponentEvent(uid, component, reg.Idx, MapInitEventInstance); + EventBusInternal.RaiseComponentEvent(uid, component, reg.Idx, MapInitEventInstance); } /// @@ -717,7 +717,7 @@ namespace Robust.Shared.GameObjects var eventArgs = new RemovedComponentEventArgs(new ComponentEventArgs(component, entityUid), false, metadata, idx); ComponentRemoved?.Invoke(eventArgs); - _eventBus.OnComponentRemoved(eventArgs); + EventBusInternal.OnComponentRemoved(eventArgs); if (!terminating) { @@ -1708,9 +1708,12 @@ namespace Robust.Shared.GameObjects } public bool CanGetComponentState(IEventBus eventBus, IComponent component, ICommonSession player) + => CanGetComponentState(component, player); + + public bool CanGetComponentState(IComponent component, ICommonSession player) { var attempt = new ComponentGetStateAttemptEvent(player); - eventBus.RaiseComponentEvent(component.Owner, component, ref attempt); + EventBusInternal.RaiseComponentEvent(component.Owner, component, ref attempt); return !attempt.Cancelled; } diff --git a/Robust.Shared/GameObjects/EntityManager.LifeCycle.cs b/Robust.Shared/GameObjects/EntityManager.LifeCycle.cs index 84ed16f03..f569636f0 100644 --- a/Robust.Shared/GameObjects/EntityManager.LifeCycle.cs +++ b/Robust.Shared/GameObjects/EntityManager.LifeCycle.cs @@ -24,7 +24,7 @@ public partial class EntityManager component.CreationTick = CurrentTick; // networked components are assumed to be dirty when added to entities. See also: ClearTicks() component.LastModifiedTick = CurrentTick; - EventBus.RaiseComponentEvent(uid, component, idx, CompAddInstance); + EventBusInternal.RaiseComponentEvent(uid, component, idx, CompAddInstance); component.LifeStage = ComponentLifeStage.Added; #pragma warning restore CS0618 // Type or member is obsolete } @@ -39,7 +39,7 @@ public partial class EntityManager DebugTools.Assert(component.LifeStage == ComponentLifeStage.Added); component.LifeStage = ComponentLifeStage.Initializing; - EventBus.RaiseComponentEvent(uid, component, idx, CompInitInstance); + EventBusInternal.RaiseComponentEvent(uid, component, idx, CompInitInstance); component.LifeStage = ComponentLifeStage.Initialized; } @@ -53,7 +53,7 @@ public partial class EntityManager DebugTools.Assert(component.LifeStage == ComponentLifeStage.Initialized); component.LifeStage = ComponentLifeStage.Starting; - EventBus.RaiseComponentEvent(uid, component, idx, CompStartupInstance); + EventBusInternal.RaiseComponentEvent(uid, component, idx, CompStartupInstance); component.LifeStage = ComponentLifeStage.Running; } @@ -76,7 +76,7 @@ public partial class EntityManager } component.LifeStage = ComponentLifeStage.Stopping; - EventBus.RaiseComponentEvent(uid, component, idx, CompShutdownInstance); + EventBusInternal.RaiseComponentEvent(uid, component, idx, CompShutdownInstance); component.LifeStage = ComponentLifeStage.Stopped; } @@ -90,7 +90,7 @@ public partial class EntityManager DebugTools.Assert(component.LifeStage != ComponentLifeStage.PreAdd); component.LifeStage = ComponentLifeStage.Removing; - EventBus.RaiseComponentEvent(uid, component, idx, CompRemoveInstance); + EventBusInternal.RaiseComponentEvent(uid, component, idx, CompRemoveInstance); component.LifeStage = ComponentLifeStage.Deleted; } diff --git a/Robust.Shared/GameObjects/EntityManager.cs b/Robust.Shared/GameObjects/EntityManager.cs index bf2d802bb..680d24ef5 100644 --- a/Robust.Shared/GameObjects/EntityManager.cs +++ b/Robust.Shared/GameObjects/EntityManager.cs @@ -81,14 +81,14 @@ namespace Robust.Shared.GameObjects /// protected readonly HashSet Entities = new(); - private EntityEventBus _eventBus = null!; + internal EntityEventBus EventBusInternal = null!; protected int NextEntityUid = (int) EntityUid.FirstUid; protected int NextNetworkId = (int) NetEntity.First; /// - public IEventBus EventBus => _eventBus; + public IEventBus EventBus => EventBusInternal; public event Action>? EntityAdded; public event Action>? EntityInitialized; @@ -142,7 +142,7 @@ namespace Robust.Shared.GameObjects if (Initialized) throw new InvalidOperationException("Initialize() called multiple times"); - _eventBus = new EntityEventBus(this, _reflection); + EventBusInternal = new EntityEventBus(this, _reflection); InitializeComponents(); _metaReg = _componentFactory.GetRegistration(typeof(MetaDataComponent)); @@ -233,7 +233,7 @@ namespace Robust.Shared.GameObjects // TODO: Probably better to call this on its own given it's so infrequent. _entitySystemManager.Initialize(); Started = true; - _eventBus.LockSubscriptions(); + EventBusInternal.LockSubscriptions(); _mapSystem = System(); _xforms = System(); _containers = System(); @@ -248,7 +248,7 @@ namespace Robust.Shared.GameObjects { ShuttingDown = true; FlushEntities(); - _eventBus.ClearSubscriptions(); + EventBusInternal.ClearSubscriptions(); _entitySystemManager.Shutdown(); ClearComponents(); ShuttingDown = false; @@ -262,8 +262,8 @@ namespace Robust.Shared.GameObjects ShuttingDown = true; FlushEntities(); _entitySystemManager.Clear(); - _eventBus.Dispose(); - _eventBus = null!; + EventBusInternal.Dispose(); + EventBusInternal = null!; ClearComponents(); ShuttingDown = false; @@ -282,7 +282,7 @@ namespace Robust.Shared.GameObjects using (histogram?.WithLabels("EntityEventBus").NewTimer()) using (_prof.Group("Events")) { - _eventBus.ProcessEventQueue(); + EventBusInternal.ProcessEventQueue(); } using (histogram?.WithLabels("QueuedDeletion").NewTimer()) @@ -604,7 +604,7 @@ namespace Robust.Shared.GameObjects { var ev = new EntityTerminatingEvent((uid, metadata)); BeforeEntityTerminating?.Invoke(ref ev); - EventBus.RaiseLocalEvent(uid, ref ev, true); + EventBusInternal.RaiseLocalEvent(uid, ref ev, true); } catch (Exception e) { @@ -698,7 +698,7 @@ namespace Robust.Shared.GameObjects #endif } - _eventBus.OnEntityDeleted(uid); + EventBusInternal.OnEntityDeleted(uid); Entities.Remove(uid); // Need to get the ID above before MetadataComponent shutdown but only remove it after everything else is done. NetEntityLookup.Remove(metadata.NetEntity); @@ -932,7 +932,7 @@ namespace Robust.Shared.GameObjects // we want this called before adding components EntityAdded?.Invoke((uid, metadata)); - _eventBus.OnEntityAdded(uid); + EventBusInternal.OnEntityAdded(uid); Entities.Add(uid); // add the required MetaDataComponent directly. @@ -1049,7 +1049,7 @@ namespace Robust.Shared.GameObjects DebugTools.Assert(meta.EntityLifeStage == EntityLifeStage.Initialized, $"Expected entity {ToPrettyString(entity)} to be initialized, was {meta.EntityLifeStage}"); SetLifeStage(meta, EntityLifeStage.MapInitialized); - EventBus.RaiseLocalEvent(entity, MapInitEventInstance); + EventBusInternal.RaiseLocalEvent(entity, MapInitEventInstance); } /// diff --git a/Robust.Shared/GameObjects/EntitySystem.Subscriptions.cs b/Robust.Shared/GameObjects/EntitySystem.Subscriptions.cs index 35764cb80..a38259d92 100644 --- a/Robust.Shared/GameObjects/EntitySystem.Subscriptions.cs +++ b/Robust.Shared/GameObjects/EntitySystem.Subscriptions.cs @@ -148,7 +148,7 @@ namespace Robust.Shared.GameObjects { foreach (var sub in _subscriptions) { - sub.Unsubscribe(this, EntityManager.EventBus); + sub.Unsubscribe(this, EntityManager.EventBusInternal); } _subscriptions = default; diff --git a/Robust.Shared/GameObjects/EntitySystem.cs b/Robust.Shared/GameObjects/EntitySystem.cs index c923cddd9..12aaa8ce0 100644 --- a/Robust.Shared/GameObjects/EntitySystem.cs +++ b/Robust.Shared/GameObjects/EntitySystem.cs @@ -99,22 +99,22 @@ namespace Robust.Shared.GameObjects protected void RaiseLocalEvent(T message) where T : notnull { - EntityManager.EventBus.RaiseEvent(EventSource.Local, message); + EntityManager.EventBusInternal.RaiseEvent(EventSource.Local, message); } protected void RaiseLocalEvent(ref T message) where T : notnull { - EntityManager.EventBus.RaiseEvent(EventSource.Local, ref message); + EntityManager.EventBusInternal.RaiseEvent(EventSource.Local, ref message); } protected void RaiseLocalEvent(object message) { - EntityManager.EventBus.RaiseEvent(EventSource.Local, message); + EntityManager.EventBusInternal.RaiseEvent(EventSource.Local, message); } protected void QueueLocalEvent(EntityEventArgs message) { - EntityManager.EventBus.QueueEvent(EventSource.Local, message); + EntityManager.EventBusInternal.QueueEvent(EventSource.Local, message); } protected void RaiseNetworkEvent(EntityEventArgs message) @@ -158,23 +158,36 @@ namespace Robust.Shared.GameObjects protected void RaiseLocalEvent(EntityUid uid, TEvent args, bool broadcast = false) where TEvent : notnull { - EntityManager.EventBus.RaiseLocalEvent(uid, args, broadcast); + EntityManager.EventBusInternal.RaiseLocalEvent(uid, args, broadcast); } protected void RaiseLocalEvent(EntityUid uid, object args, bool broadcast = false) { - EntityManager.EventBus.RaiseLocalEvent(uid, args, broadcast); + EntityManager.EventBusInternal.RaiseLocalEvent(uid, args, broadcast); } protected void RaiseLocalEvent(EntityUid uid, ref TEvent args, bool broadcast = false) where TEvent : notnull { - EntityManager.EventBus.RaiseLocalEvent(uid, ref args, broadcast); + EntityManager.EventBusInternal.RaiseLocalEvent(uid, ref args, broadcast); } protected void RaiseLocalEvent(EntityUid uid, ref object args, bool broadcast = false) { - EntityManager.EventBus.RaiseLocalEvent(uid, ref args, broadcast); + EntityManager.EventBusInternal.RaiseLocalEvent(uid, ref args, broadcast); + } + + protected void RaiseComponentEvent(EntityUid uid, TComp comp, ref TEvent args) + where TEvent : notnull + where TComp : IComponent + { + EntityManager.EventBusInternal.RaiseComponentEvent(uid, comp, ref args); + } + + public void RaiseComponentEvent(EntityUid uid, IComponent component, ref TEvent args) + where TEvent : notnull + { + EntityManager.EventBusInternal.RaiseComponentEvent(uid, component, ref args); } #endregion diff --git a/Robust.Shared/GameObjects/EventBusAttributes.cs b/Robust.Shared/GameObjects/EventBusAttributes.cs index 54848a892..13aa55d30 100644 --- a/Robust.Shared/GameObjects/EventBusAttributes.cs +++ b/Robust.Shared/GameObjects/EventBusAttributes.cs @@ -3,15 +3,17 @@ using System; namespace Robust.Shared.GameObjects; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] -public sealed class ByRefEventAttribute : Attribute -{ -} +public sealed class ByRefEventAttribute : Attribute; /// -/// Indicates that an eventbus event should only ever be raised through . -/// This allows extra optimizations. +/// This attribute enables an event to be raised as a "component event" via . /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] internal sealed class ComponentEventAttribute : Attribute { + /// + /// If true, this event may **only** be raised via as a "component event", as any subscriptions will not be + /// included in the normal event tables. + /// + public bool Exclusive = true; } diff --git a/Robust.Shared/GameObjects/IMapInit.cs b/Robust.Shared/GameObjects/IMapInit.cs index 194535788..8762ae83f 100644 --- a/Robust.Shared/GameObjects/IMapInit.cs +++ b/Robust.Shared/GameObjects/IMapInit.cs @@ -3,6 +3,7 @@ namespace Robust.Shared.GameObjects /// /// Raised directed on an entity when the map is initialized. /// + [ComponentEvent(Exclusive = false)] public sealed class MapInitEvent : EntityEventArgs { } diff --git a/Robust.UnitTesting/Server/GameStates/PvsPauseTest.cs b/Robust.UnitTesting/Server/GameStates/PvsPauseTest.cs index 4bd6432b9..12e4d6759 100644 --- a/Robust.UnitTesting/Server/GameStates/PvsPauseTest.cs +++ b/Robust.UnitTesting/Server/GameStates/PvsPauseTest.cs @@ -166,9 +166,8 @@ public sealed class PvsPauseTest : RobustIntegrationTest AssertEnt(paused: true, detached: false, clientPaused: true); } - await client.WaitPost(() => netMan.ClientDisconnect("")); - await server.WaitRunTicks(5); - await client.WaitRunTicks(5); + client.Post(() => netMan.ClientDisconnect("")); + await RunTicks(); } } diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs index 1a76bc500..5ce870211 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.ComponentEvent.cs @@ -2,11 +2,8 @@ using System.Collections.Generic; using Moq; using NUnit.Framework; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Log; using Robust.Shared.Reflection; -using Robust.Shared.Serialization.Manager; -using Robust.UnitTesting.Shared.Reflection; +using Robust.UnitTesting.Server; namespace Robust.UnitTesting.Shared.GameObjects { @@ -15,30 +12,8 @@ namespace Robust.UnitTesting.Shared.GameObjects [Test] public void SubscribeCompEvent() { - var compFactory = new ComponentFactory(new DynamicTypeFactory(), new ReflectionManagerTest(), new SerializationManager(), new LogManager()); - - // Arrange - var entUid = new EntityUid(7); - var compInstance = new MetaDataComponent(); - - var entManMock = new Mock(); - var reflectMock = new Mock(); - - compFactory.RegisterClass(); - entManMock.Setup(m => m.ComponentFactory).Returns(compFactory); - - IComponent? outIComponent = compInstance; - entManMock.Setup(m => m.TryGetComponent(entUid, CompIdx.Index(), out outIComponent)) - .Returns(true); - - entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index())) - .Returns(compInstance); - - entManMock.Setup(m => m.GetComponentInternal(entUid, CompIdx.Index())) - .Returns(compInstance); - - var bus = new EntityEventBus(entManMock.Object, reflectMock.Object); - bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); + var (bus, sim, entUid, compInstance) = EntFactory(); + var compFactory = sim.Resolve(); // Subscribe int calledCount = 0; @@ -70,34 +45,8 @@ namespace Robust.UnitTesting.Shared.GameObjects [Test] public void UnsubscribeCompEvent() { - // Arrange - var entUid = new EntityUid(7); - var compInstance = new MetaDataComponent(); - - var entManMock = new Mock(); - - var compRegistration = new ComponentRegistration( - "MetaData", - typeof(MetaDataComponent), - CompIdx.Index()); - - var compFacMock = new Mock(); - var reflectMock = new Mock(); - - compFacMock.Setup(m => m.GetRegistration(CompIdx.Index())).Returns(compRegistration); - compFacMock.Setup(m => m.GetAllRegistrations()).Returns(new[] { compRegistration }); - compFacMock.Setup(m => m.GetIndex(typeof(MetaDataComponent))).Returns(CompIdx.Index()); - entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object); - - IComponent? outIComponent = compInstance; - entManMock.Setup(m => m.TryGetComponent(entUid, typeof(MetaDataComponent), out outIComponent)) - .Returns(true); - - entManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent))) - .Returns(compInstance); - - var bus = new EntityEventBus(entManMock.Object, reflectMock.Object); - bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); + var (bus, sim, entUid, compInstance) = EntFactory(); + var compFactory = sim.Resolve(); // Subscribe int calledCount = 0; @@ -108,7 +57,7 @@ namespace Robust.UnitTesting.Shared.GameObjects // add a component to the system bus.OnEntityAdded(entUid); - var reg = compFacMock.Object.GetRegistration(CompIdx.Index()); + var reg = compFactory.GetRegistration(CompIdx.Index()); bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg)); // Raise @@ -121,58 +70,22 @@ namespace Robust.UnitTesting.Shared.GameObjects { calledCount++; } - } [Test] public void SubscribeCompLifeEvent() { - // Arrange - var entUid = new EntityUid(7); - var compInstance = new MetaDataComponent(); - - var entManMock = new Mock(); - -#pragma warning disable CS0618 // Type or member is obsolete - compInstance.Owner = entUid; -#pragma warning restore CS0618 // Type or member is obsolete - - var compRegistration = new ComponentRegistration( - "MetaData", - typeof(MetaDataComponent), - CompIdx.Index()); - - var compFacMock = new Mock(); - var reflectMock = new Mock(); - - compFacMock.Setup(m => m.GetRegistration(CompIdx.Index())).Returns(compRegistration); - compFacMock.Setup(m => m.GetAllRegistrations()).Returns(new[] { compRegistration }); - compFacMock.Setup(m => m.GetIndex(typeof(MetaDataComponent))).Returns(CompIdx.Index()); - entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object); - - IComponent? outIComponent = compInstance; - entManMock.Setup(m => m.TryGetComponent(entUid, typeof(MetaDataComponent), out outIComponent)) - .Returns(true); - - entManMock.Setup(m => m.GetComponent(entUid, typeof(MetaDataComponent))) - .Returns(compInstance); - - var bus = new EntityEventBus(entManMock.Object, reflectMock.Object); - bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); + var (bus, sim, entUid, compInstance) = EntFactory(); + var entMan = sim.Resolve(); + var fact = sim.Resolve(); // Subscribe int calledCount = 0; bus.SubscribeLocalEvent(HandleTestEvent); bus.LockSubscriptions(); - // add a component to the system - entManMock.Raise(m => m.EntityAdded += null, entUid); - - var reg = compFacMock.Object.GetRegistration(); - entManMock.Raise(m => m.ComponentAdded += null, new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg)); - // Raise - ((IEventBus)bus).RaiseComponentEvent(entUid, compInstance, new ComponentInit()); + bus.RaiseComponentEvent(entUid, compInstance, new ComponentInit()); // Assert Assert.That(calledCount, Is.EqualTo(1)); @@ -187,39 +100,25 @@ namespace Robust.UnitTesting.Shared.GameObjects [Test] public void CompEventOrdered() { - // Arrange - var entUid = new EntityUid(7); + var sim = RobustServerSimulation + .NewSimulation() + .RegisterComponents(f => + { + f.RegisterClass(); + f.RegisterClass(); + f.RegisterClass(); + }) + .InitializeInstance(); - var entManMock = new Mock(); - var compFacMock = new Mock(); - var reflectMock = new Mock(); + var entMan = sim.Resolve(); + var entUid = entMan.Spawn(); + var instA = entMan.AddComponent(entUid); + var instB = entMan.AddComponent(entUid); + var instC = entMan.AddComponent(entUid); + var bus = entMan.EventBusInternal; + bus.ClearSubscriptions(); - List allRefTypes = new(); - void Setup(out T instance) where T : IComponent, new() - { - IComponent? inst = instance = new T(); - var reg = new ComponentRegistration( - typeof(T).Name, - typeof(T), - CompIdx.Index()); - - compFacMock.Setup(m => m.GetRegistration(CompIdx.Index())).Returns(reg); - compFacMock.Setup(m => m.GetIndex(typeof(T))).Returns(CompIdx.Index()); - entManMock.Setup(m => m.TryGetComponent(entUid, CompIdx.Index(), out inst)).Returns(true); - entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index())).Returns(inst); - entManMock.Setup(m => m.GetComponentInternal(entUid, CompIdx.Index())).Returns(inst); - allRefTypes.Add(reg); - } - - Setup(out var instA); - Setup(out var instB); - Setup(out var instC); - - compFacMock.Setup(m => m.GetAllRegistrations()).Returns(allRefTypes.ToArray()); - - entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object); - var bus = new EntityEventBus(entManMock.Object, reflectMock.Object); - bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); + var fact = sim.Resolve(); // Subscribe var a = false; @@ -250,9 +149,9 @@ namespace Robust.UnitTesting.Shared.GameObjects // add a component to the system bus.OnEntityAdded(entUid); - var regA = compFacMock.Object.GetRegistration(CompIdx.Index()); - var regB = compFacMock.Object.GetRegistration(CompIdx.Index()); - var regC = compFacMock.Object.GetRegistration(CompIdx.Index()); + var regA = fact.GetRegistration(CompIdx.Index()); + var regB = fact.GetRegistration(CompIdx.Index()); + var regC = fact.GetRegistration(CompIdx.Index()); bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA)); bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB)); @@ -271,40 +170,25 @@ namespace Robust.UnitTesting.Shared.GameObjects [Test] public void CompEventLoop() { - var entUid = new EntityUid(7); + var sim = RobustServerSimulation + .NewSimulation() + .RegisterComponents(f => + { + f.RegisterClass(); + f.RegisterClass(); + }) + .InitializeInstance(); - var entManMock = new Mock(); - var compFacMock = new Mock(); - var reflectMock = new Mock(); + var entMan = sim.Resolve(); + var entUid = entMan.Spawn(); + var instA = entMan.AddComponent(entUid); + var instB = entMan.AddComponent(entUid); + var bus = entMan.EventBusInternal; + bus.ClearSubscriptions(); - List allRefTypes = new(); - void Setup(out T instance) where T : IComponent, new() - { - IComponent? inst = instance = new T(); - var reg = new ComponentRegistration( - typeof(T).Name, - typeof(T), - CompIdx.Index()); - - compFacMock.Setup(m => m.GetRegistration(CompIdx.Index())).Returns(reg); - compFacMock.Setup(m => m.GetIndex(typeof(T))).Returns(CompIdx.Index()); - entManMock.Setup(m => m.TryGetComponent(entUid, CompIdx.Index(), out inst)).Returns(true); - entManMock.Setup(m => m.GetComponent(entUid, CompIdx.Index())).Returns(inst); - entManMock.Setup(m => m.GetComponentInternal(entUid, CompIdx.Index())).Returns(inst); - allRefTypes.Add(reg); - } - - Setup(out var instA); - Setup(out var instB); - - compFacMock.Setup(m => m.GetAllRegistrations()).Returns(allRefTypes.ToArray()); - - entManMock.Setup(m => m.ComponentFactory).Returns(compFacMock.Object); - var bus = new EntityEventBus(entManMock.Object, reflectMock.Object); - bus.OnlyCallOnRobustUnitTestISwearToGodPleaseSomebodyKillThisNightmare(); - - var regA = compFacMock.Object.GetRegistration(CompIdx.Index()); - var regB = compFacMock.Object.GetRegistration(CompIdx.Index()); + var fact = sim.Resolve(); + var regA = fact.GetRegistration(CompIdx.Index()); + var regB = fact.GetRegistration(CompIdx.Index()); var handlerACount = 0; void HandlerA(EntityUid uid, Component comp, TestEvent ev) diff --git a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs index 6dd43e6fe..eb21d0bcf 100644 --- a/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs +++ b/Robust.UnitTesting/Shared/GameObjects/EntityEventBusTests.SystemEvent.cs @@ -1,22 +1,27 @@ using System; -using Moq; using NUnit.Framework; using Robust.Shared.GameObjects; -using Robust.Shared.Reflection; +using Robust.UnitTesting.Server; namespace Robust.UnitTesting.Shared.GameObjects { [TestFixture, Parallelizable, TestOf(typeof(EntityEventBus))] public partial class EntityEventBusTests { + private static (EntityEventBus Bus, ISimulation Sim, EntityUid Uid, MetaDataComponent Comp) EntFactory() + { + var sim = RobustServerSimulation.NewSimulation().InitializeInstance(); + var entMan = sim.Resolve(); + var uid = entMan.Spawn(); + var comp = entMan.MetaQuery.Comp(uid); + var bus = entMan.EventBusInternal; + bus.ClearSubscriptions(); + return (bus, sim, uid, comp); + } + private static EntityEventBus BusFactory() { - var compFacMock = new Mock(); - var entManMock = new Mock(); - var reflectMock = new Mock(); - entManMock.SetupGet(e => e.ComponentFactory).Returns(compFacMock.Object); - var bus = new EntityEventBus(entManMock.Object, reflectMock.Object); - return bus; + return EntFactory().Bus; } ///