Files
RobustToolbox/Robust.Shared/GameObjects/EntityEventBus.Ordering.cs
T
e5013d9e3f Add support for struct directed events, add by-ref directed/broadcast events. (#1931)
* Add support for struct directed events.
- Turn transform events into readonly structs

* TransformComponent events have readonly fields.

* Reduce boxing allocations, add DirectedRegistration readonly struct.

* Use typeof in a few places instead of GetType

* Add overloads to allow passing directed events by ref.

* Raise transform events by ref.

* Fix occluder AnchorStateChangedEvent subscription

* Fix broadphase subscriptions

* Fix bug oops

* Fix transform system raising moveevents by value

* Don't reflect the test entity systems.

* Directed EventBus uses ref everywhere, internally
Deduplicates a bunch of code. Whoo!

* Add broadcast by-ref events

* Fix by-ref events bug using Unsafe, add new tests for sorted by-ref events.

* Port broadcast events to by-ref.

* Speed, more correctness, reduce generics bloat.

* Clean up subscription code some.

* Remove bad test.

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2021-08-21 11:37:02 +02:00

99 lines
3.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Utility;
namespace Robust.Shared.GameObjects
{
internal partial class EntityEventBus
{
// TODO: Topological sort is currently done every time an event is emitted.
// This should be fine for low-volume stuff like interactions, but definitely not for anything high volume.
// Not sure if we could pre-cache the topological sort, here.
// Ordered event raising is slow so if this event has any ordering dependencies we use a slower path.
private readonly HashSet<Type> _orderedEvents = new();
private void ProcessSingleEventOrdered(EventSource source, ref Unit eventArgs, Type eventType, bool byRef)
{
var found = new List<(RefEventHandler, OrderingData?)>();
CollectBroadcastOrdered(source, eventType, found, byRef);
DispatchOrderedEvents(ref eventArgs, found);
}
private void CollectBroadcastOrdered(
EventSource source,
Type eventType,
List<(RefEventHandler, OrderingData?)> found,
bool byRef)
{
if (!_eventSubscriptions.TryGetValue(eventType, out var subs))
return;
foreach (var handler in subs)
{
if (handler.ReferenceEvent != byRef)
ThrowByRefMisMatch();
if ((handler.Mask & source) != 0)
found.Add((handler.Handler, handler.Ordering));
}
}
private void RaiseLocalOrdered(EntityUid uid,
Type eventType,
ref Unit unitRef,
bool broadcast, bool byRef)
{
var found = new List<(RefEventHandler, OrderingData?)>();
if (broadcast)
CollectBroadcastOrdered(EventSource.Local, eventType, found, byRef);
_eventTables.CollectOrdered(uid, eventType, found, byRef);
DispatchOrderedEvents(ref unitRef, found);
if (broadcast)
ProcessAwaitingMessages(EventSource.Local, ref unitRef, eventType);
}
private static void DispatchOrderedEvents(ref Unit eventArgs, List<(RefEventHandler, OrderingData?)> found)
{
var nodes = TopologicalSort.FromBeforeAfter(
found.Where(f => f.Item2 != null),
n => n.Item2!.OrderType,
n => n.Item1!,
n => n.Item2!.Before ?? Array.Empty<Type>(),
n => n.Item2!.After ?? Array.Empty<Type>(),
allowMissing: true);
foreach (var handler in TopologicalSort.Sort(nodes))
{
handler(ref eventArgs);
}
// Go over all handlers that don't have ordering so weren't included in the sort.
foreach (var (handler, orderData) in found)
{
if (orderData == null)
handler(ref eventArgs);
}
}
private void HandleOrderRegistration(Type eventType, OrderingData? data)
{
if (data == null)
return;
if (data.Before != null || data.After != null)
_orderedEvents.Add(eventType);
}
private sealed record OrderingData(Type OrderType, Type[]? Before, Type[]? After);
}
}