feat(sdk): migrate to EventBus 2.0

Replace the legacy EventBase runtime with generated multi-bus bindings and explicit scheduling. Migrate app, data, network, demo, and mod consumers; add generated network-event registration and owner-scoped mod content overrides.
This commit is contained in:
2026-08-26 01:15:48 +08:00
parent 67d32795c4
commit ad5a7b68a3
129 changed files with 5071 additions and 5584 deletions
+163 -96
View File
@@ -1,129 +1,196 @@
#nullable enable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
using Cysharp.Threading.Tasks;
namespace ShrinkEventBus
{
internal readonly struct ShrinkEventTrace
{
public ShrinkEventTrace(DateTime timestampUtc, Type eventType, ShrinkBusKey busKey,
ShrinkBusOptions options, int threadId, long elapsedTimestampTicks,
ShrinkPostResult result, bool isAsync)
{
TimestampUtc = timestampUtc;
EventType = eventType;
BusKey = busKey;
Scheduler = options.Scheduler;
DispatchMode = options.DispatchMode;
ThreadId = threadId;
ElapsedTimestampTicks = elapsedTimestampTicks;
Result = result;
IsAsync = isAsync;
}
public DateTime TimestampUtc { get; }
public Type EventType { get; }
public ShrinkBusKey BusKey { get; }
public ShrinkBusSchedulerKind Scheduler { get; }
public ShrinkDispatchMode DispatchMode { get; }
public int ThreadId { get; }
public long ElapsedTimestampTicks { get; }
public ShrinkPostResult Result { get; }
public bool IsAsync { get; }
}
public static class EventBus
{
private sealed class GlobalBusResolver : IShrinkBusResolver
{
public IShrinkEventBus GetBus(ShrinkBusKey key)
{
if (TryGetBus(key, out var bus))
return bus;
throw new KeyNotFoundException($"Bus '{key}' is not registered.");
}
public bool TryGetBus(ShrinkBusKey key, out IShrinkEventBus bus) =>
EventBus.TryGetBus(key, out bus);
}
private static readonly ConcurrentDictionary<ShrinkBusKey, IShrinkEventBus> Buses = new();
internal static readonly IShrinkBusResolver Resolver = new GlobalBusResolver();
private static readonly IShrinkEventBus DefaultBus = CreateDefaultBus();
private static Action<IShrinkEvent, Type, ShrinkBusKey>? _posted;
private static Action<ShrinkEventTrace>? _detailedPosted;
internal static event Action<IShrinkEvent, Type, ShrinkBusKey> Posted
{
add
{
_posted += value;
}
remove
{
_posted -= value;
}
}
internal static bool HasPostedObservers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Volatile.Read(ref _posted) != null;
}
internal static event Action<ShrinkEventTrace> DetailedPosted
{
add => _detailedPosted += value;
remove => _detailedPosted -= value;
}
internal static bool HasDetailedPostedObservers
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Volatile.Read(ref _detailedPosted) != null;
}
public static IShrinkEventBus Default => DefaultBus;
public static event Action<EventBase, Type> OnEventTriggered
public static IShrinkEventBus CreateBus(ShrinkBusKey key, ShrinkBusOptions options)
{
add => DefaultBus.OnEventTriggered += value;
remove => DefaultBus.OnEventTriggered -= value;
if (options == null)
throw new ArgumentNullException(nameof(options));
var bus = BuildBus(key, options);
if (!Buses.TryAdd(key, bus))
{
bus.Dispose();
throw new InvalidOperationException($"Bus '{key}' is already registered.");
}
ShrinkStaticBindingRegistry.AttachForBus(key, Resolver);
return bus;
}
#if UNITY_EDITOR
public static bool EnableDebugRecord
public static IShrinkEventBus GetOrCreateBus(ShrinkBusKey key, ShrinkBusOptions options)
{
get => DefaultBus.EnableDebugRecord;
set => DefaultBus.EnableDebugRecord = value;
if (options == null)
throw new ArgumentNullException(nameof(options));
var bus = Buses.GetOrAdd(key, busKey => BuildBus(busKey, options));
ShrinkStaticBindingRegistry.AttachForBus(key, Resolver);
return bus;
}
public static event Action<EventBase, string, string, EventHandlerInfo[]> OnEventTriggeredForEditor
{
add => DefaultBus.OnEventTriggeredForEditor += value;
remove => DefaultBus.OnEventTriggeredForEditor -= value;
}
#endif
public static bool TryGetBus(ShrinkBusKey key, out IShrinkEventBus bus) =>
Buses.TryGetValue(key, out bus!);
public static ShrinkEventBusBuilder Builder() => new();
public static IShrinkEventBus CreateBus(Action<ShrinkEventBusBuilder>? configure = null)
public static bool RemoveBus(ShrinkBusKey key)
{
var builder = new ShrinkEventBusBuilder();
configure?.Invoke(builder);
return builder.Build();
if (key == ShrinkBusKey.Game || !Buses.TryRemove(key, out var bus))
return false;
ShrinkStaticBindingRegistry.DetachBus(key);
bus.Dispose();
return true;
}
public static void Start() => DefaultBus.Start();
public static void AutoRegister(object target) => DefaultBus.AutoRegister(target);
public static void Register(object target) => DefaultBus.Register(target);
public static void Unregister(object target) => DefaultBus.Unregister(target);
public static ShrinkPostResult Post<TEvent>(in TEvent eventData)
where TEvent : IShrinkEvent => DefaultBus.Post(in eventData);
public static void RegisterEvent<TEvent>(Func<TEvent, UniTask> handler, int priority)
where TEvent : EventBase => DefaultBus.RegisterEvent(handler, priority);
public static UniTask<ShrinkPostResult> PostAsync<TEvent>(TEvent eventData,
CancellationToken cancellationToken = default)
where TEvent : IShrinkEvent => DefaultBus.PostAsync(eventData, cancellationToken);
public static void RegisterEvent<TEvent>(Action<TEvent> handler,
EventPriority priority = EventPriority.NORMAL, bool receiveCanceled = false)
where TEvent : EventBase => DefaultBus.RegisterEvent(handler, priority, receiveCanceled);
public static IDisposable Attach(object target, ShrinkBusKey? defaultBus = null)
{
if (target == null)
throw new ArgumentNullException(nameof(target));
if (target is not IShrinkGeneratedSubscriber generated)
throw new InvalidOperationException(
$"Type {target.GetType().FullName} has no generated [ShrinkSubscribe] binding.");
return generated.AttachGenerated(Resolver, defaultBus ?? ShrinkBusKey.Game);
}
public static void RegisterEvent<TEvent>(Action<TEvent> handler, int priority)
where TEvent : EventBase => DefaultBus.RegisterEvent(handler, priority);
public static void RegisterEvent<TEvent>(Func<TEvent, UniTask> handler,
EventPriority priority = EventPriority.NORMAL, bool receiveCanceled = false)
where TEvent : EventBase => DefaultBus.RegisterEvent(handler, priority, receiveCanceled);
public static IShrinkEventSubscription SubscribeEvent<TEvent>(Action<TEvent> handler,
EventPriority priority = EventPriority.NORMAL, bool receiveCanceled = false)
where TEvent : EventBase => DefaultBus.SubscribeEvent(handler, priority, receiveCanceled);
public static IShrinkEventSubscription SubscribeEvent<TEvent>(Action<TEvent> handler, int priority)
where TEvent : EventBase => DefaultBus.SubscribeEvent(handler, priority);
public static IShrinkEventSubscription SubscribeEvent<TEvent>(Func<TEvent, UniTask> handler,
EventPriority priority = EventPriority.NORMAL, bool receiveCanceled = false)
where TEvent : EventBase => DefaultBus.SubscribeEvent(handler, priority, receiveCanceled);
public static IShrinkEventSubscription SubscribeEvent<TEvent>(Func<TEvent, UniTask> handler, int priority)
where TEvent : EventBase => DefaultBus.SubscribeEvent(handler, priority);
public static void UnregisterEvent<TEvent>(Func<TEvent, UniTask> handler) where TEvent : EventBase =>
DefaultBus.UnregisterEvent(handler);
public static void UnregisterEvent<TEvent>(Action<TEvent> handler) where TEvent : EventBase =>
DefaultBus.UnregisterEvent(handler);
public static void ClearAllSubscribersForEvent<TEvent>() where TEvent : EventBase =>
DefaultBus.ClearAllSubscribersForEvent<TEvent>();
public static void UnregisterAllEventsForObject(object targetObject) =>
DefaultBus.UnregisterAllEventsForObject(targetObject);
public static void UnregisterInstance(object targetObject) => DefaultBus.Unregister(targetObject);
public static void UnregisterAllEvents() => DefaultBus.UnregisterAllEvents();
public static UniTask<bool> TriggerEventAsync<TEvent>(TEvent eventArgs) where TEvent : EventBase =>
DefaultBus.TriggerEventAsync(eventArgs);
public static UniTask<bool> TriggerEventAsync<TEvent>(EventPriority phase, TEvent eventArgs)
where TEvent : EventBase => DefaultBus.TriggerEventAsync(phase, eventArgs);
public static bool TriggerEvent<TEvent>(TEvent eventArgs) where TEvent : EventBase =>
DefaultBus.TriggerEvent(eventArgs);
public static bool TriggerEvent<TEvent>(EventPriority phase, TEvent eventArgs) where TEvent : EventBase =>
DefaultBus.TriggerEvent(phase, eventArgs);
public static EventHandlerInfo[] GetEventSubscribers<TEvent>() where TEvent : EventBase =>
DefaultBus.GetEventSubscribers<TEvent>();
public static ListenerList GetListenerList<TEvent>() where TEvent : EventBase =>
DefaultBus.GetListenerList<TEvent>();
public static IReadOnlyDictionary<Type, EventHandlerInfo[]> GetAllSubscribersSnapshot() =>
DefaultBus.GetAllSubscribersSnapshot();
public static IReadOnlyList<ShrinkEventSubscriptionSnapshot> GetActiveSubscriptionsSnapshot() =>
DefaultBus.GetActiveSubscriptionsSnapshot();
public static bool IsInstanceRegistered(object target) => DefaultBus.IsInstanceRegistered(target);
public static int GetRegisteredInstanceCount() => DefaultBus.GetRegisteredInstanceCount();
public static int GetRegisteredEventTypeCount() => DefaultBus.GetRegisteredEventTypeCount();
internal static IReadOnlyList<(ShrinkBusKey Key, ShrinkBusOptions Options, int Subscribers)> Snapshot()
{
var snapshot = new List<(ShrinkBusKey, ShrinkBusOptions, int)>(Buses.Count);
foreach (var pair in Buses)
{
var subscribers = pair.Value is ShrinkEventBusInstance instance
? instance.SubscriberCount
: 0;
snapshot.Add((pair.Key, pair.Value.Options, subscribers));
}
return snapshot;
}
private static IShrinkEventBus CreateDefaultBus()
{
var bus = (ShrinkEventBusInstance)new ShrinkEventBusBuilder()
.SetExceptionHandlingMode(ShrinkEventExceptionHandlingMode.LogAndContinue)
.AllowPerPhaseDispatch()
.Build();
EventBusRegHelper.RegStaticEventHandler(bus);
var bus = BuildBus(ShrinkBusKey.Game, ShrinkBusOptions.MainThread());
Buses[ShrinkBusKey.Game] = bus;
ShrinkStaticBindingRegistry.AttachForBus(ShrinkBusKey.Game, Resolver);
return bus;
}
private static IShrinkEventBus BuildBus(ShrinkBusKey key, ShrinkBusOptions options) =>
new ShrinkEventBusBuilder()
.WithKey(key)
.WithOptions(options)
.WithPostObserver(NotifyPostedObject)
.Build();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void NotifyPostedObject(IShrinkEvent eventData, Type eventType, ShrinkBusKey key)
{
_posted?.Invoke(eventData, eventType, key);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void NotifyDetailedPosted(ShrinkEventTrace trace)
{
var observers = Volatile.Read(ref _detailedPosted);
if (observers == null)
return;
try
{
observers(trace);
}
catch (Exception exception)
{
UnityEngine.Debug.LogException(exception);
}
}
}
}