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:
@@ -1,9 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using ShrinkEventBus;
|
||||
@@ -16,38 +14,27 @@ namespace ShrinkNetwork.Integration
|
||||
private sealed class ServiceRegistration
|
||||
{
|
||||
public ShrinkNetworkService Service = null!;
|
||||
public Func<ShrinkNetworkSession, EventBase, bool>? SessionFilter;
|
||||
public Func<ShrinkNetworkSession, IShrinkEvent, bool>? SessionFilter;
|
||||
public Dictionary<string, long> AppliedDeltaVersions { get; } = new();
|
||||
}
|
||||
|
||||
private static readonly object SyncRoot = new();
|
||||
private static readonly Dictionary<ShrinkNetworkService, ServiceRegistration> RegisteredServices = new();
|
||||
private static readonly AsyncLocal<int> SuppressForwardDepth = new();
|
||||
private static readonly ConcurrentDictionary<Type, Func<EventBase, UniTask>> IncomingDispatchers = new();
|
||||
private static readonly ConcurrentDictionary<Type, Func<ShrinkNetworkContext, EventBase, UniTask<object?>>> IncomingRequestDispatchers = new();
|
||||
private static readonly MethodInfo DispatchIncomingGenericMethod =
|
||||
typeof(ShrinkNetworkEventBusBridge).GetMethod(nameof(DispatchIncomingGenericAsync),
|
||||
BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
private static readonly MethodInfo DispatchIncomingRequestGenericMethod =
|
||||
typeof(ShrinkNetworkEventBusBridge).GetMethod(nameof(DispatchIncomingRequestGenericAsync),
|
||||
BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
private static Dictionary<Type, ShrinkNetworkMessageAttribute> _networkEventTypes = new();
|
||||
private static Dictionary<Type, ShrinkNetworkEventRegistration> _networkEventTypes = new();
|
||||
private static bool _initialized;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
private static void ResetStaticState()
|
||||
{
|
||||
EventBus.OnEventTriggered -= HandleLocalEventTriggered;
|
||||
EventBus.Posted -= HandleLocalEventPosted;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
RegisteredServices.Clear();
|
||||
FallbackDeltaVersions.Clear();
|
||||
}
|
||||
|
||||
IncomingDispatchers.Clear();
|
||||
IncomingRequestDispatchers.Clear();
|
||||
_networkEventTypes = new Dictionary<Type, ShrinkNetworkMessageAttribute>();
|
||||
_networkEventTypes = new Dictionary<Type, ShrinkNetworkEventRegistration>();
|
||||
_initialized = false;
|
||||
}
|
||||
|
||||
@@ -58,11 +45,11 @@ namespace ShrinkNetwork.Integration
|
||||
|
||||
_initialized = true;
|
||||
RefreshNetworkEventTypes();
|
||||
EventBus.OnEventTriggered += HandleLocalEventTriggered;
|
||||
EventBus.Posted += HandleLocalEventPosted;
|
||||
}
|
||||
|
||||
public static void RegisterService(ShrinkNetworkService service,
|
||||
Func<ShrinkNetworkSession, EventBase, bool>? sessionFilter = null)
|
||||
Func<ShrinkNetworkSession, IShrinkEvent, bool>? sessionFilter = null)
|
||||
{
|
||||
if (service == null)
|
||||
throw new ArgumentNullException(nameof(service));
|
||||
@@ -116,7 +103,7 @@ namespace ShrinkNetwork.Integration
|
||||
}
|
||||
|
||||
public static UniTask PublishAsync<TEvent>(ShrinkNetworkSession session, TEvent eventArgs, string? route = null)
|
||||
where TEvent : EventBase, IShrinkNetworkMessage
|
||||
where TEvent : IShrinkEvent, IShrinkNetworkMessage
|
||||
{
|
||||
if (session == null)
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
@@ -130,7 +117,7 @@ namespace ShrinkNetwork.Integration
|
||||
|
||||
public static async UniTask PublishAsync<TEvent>(IEnumerable<ShrinkNetworkSession> sessions, TEvent eventArgs,
|
||||
string? route = null)
|
||||
where TEvent : EventBase, IShrinkNetworkMessage
|
||||
where TEvent : IShrinkEvent, IShrinkNetworkMessage
|
||||
{
|
||||
if (sessions == null)
|
||||
throw new ArgumentNullException(nameof(sessions));
|
||||
@@ -159,7 +146,7 @@ namespace ShrinkNetwork.Integration
|
||||
|
||||
public static async UniTask<ShrinkNetworkEventResultResponse> RequestResultAsync<TEvent>(ShrinkNetworkSession session,
|
||||
TEvent eventArgs, ShrinkRpcCallOptions? options = null)
|
||||
where TEvent : EventBase, IShrinkNetworkRequest
|
||||
where TEvent : IShrinkResultEvent<EventResult>, IShrinkNetworkRequest
|
||||
{
|
||||
if (session == null)
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
@@ -178,14 +165,14 @@ namespace ShrinkNetwork.Integration
|
||||
if (response.IsSuccess)
|
||||
{
|
||||
eventArgs.SetResult(response.Result);
|
||||
if (eventArgs.IsCancelable)
|
||||
eventArgs.SetCanceled(response.IsCanceled);
|
||||
if (eventArgs is IShrinkCancelableEvent cancelable)
|
||||
cancelable.SetCanceled(response.IsCanceled);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
private static void HandleLocalEventTriggered(EventBase eventArgs, Type eventType)
|
||||
private static void HandleLocalEventPosted(IShrinkEvent eventArgs, Type eventType, ShrinkBusKey busKey)
|
||||
{
|
||||
if (eventArgs == null || eventType == null)
|
||||
return;
|
||||
@@ -201,7 +188,7 @@ namespace ShrinkNetwork.Integration
|
||||
ForwardEventAsync(eventArgs, eventType, networkMessage).Forget();
|
||||
}
|
||||
|
||||
private static async UniTaskVoid ForwardEventAsync(EventBase eventArgs, Type eventType,
|
||||
private static async UniTaskVoid ForwardEventAsync(IShrinkEvent eventArgs, Type eventType,
|
||||
IShrinkNetworkMessage networkMessage)
|
||||
{
|
||||
List<ServiceRegistration> registrations;
|
||||
@@ -234,9 +221,8 @@ namespace ShrinkNetwork.Integration
|
||||
continue;
|
||||
}
|
||||
|
||||
// Complete every serializer pass before the first await. EventBase
|
||||
// instances may be returned to EventPool immediately after the
|
||||
// synchronous trigger returns.
|
||||
// Complete every serializer pass before the first await so mutable
|
||||
// event payloads cannot change between target sessions.
|
||||
batches.Add((registration, sessions, payload));
|
||||
}
|
||||
|
||||
@@ -269,13 +255,8 @@ namespace ShrinkNetwork.Integration
|
||||
|
||||
if (!service.MessageRegistry.TryGetMeta(typeof(ShrinkNetworkEventResultResponse), out _))
|
||||
{
|
||||
var responseAttribute =
|
||||
typeof(ShrinkNetworkEventResultResponse).GetCustomAttribute<ShrinkNetworkMessageAttribute>(false);
|
||||
if (responseAttribute != null)
|
||||
{
|
||||
service.RegisterMessage(typeof(ShrinkNetworkEventResultResponse), responseAttribute.Opcode,
|
||||
responseAttribute.Route);
|
||||
}
|
||||
service.RegisterMessage(typeof(ShrinkNetworkEventResultResponse), -300001,
|
||||
"__integration/event_result_response");
|
||||
}
|
||||
|
||||
var isRequestEvent = typeof(IShrinkNetworkRequest).IsAssignableFrom(pair.Key);
|
||||
@@ -301,43 +282,37 @@ namespace ShrinkNetwork.Integration
|
||||
|
||||
private static UniTask DispatchIncomingAsync(ShrinkNetworkContext context, object message)
|
||||
{
|
||||
if (message is not EventBase eventArgs)
|
||||
if (message is not IShrinkEvent eventArgs)
|
||||
return UniTask.CompletedTask;
|
||||
|
||||
var dispatcher = IncomingDispatchers.GetOrAdd(eventArgs.GetType(), CreateIncomingDispatcher);
|
||||
return dispatcher(eventArgs);
|
||||
}
|
||||
|
||||
private static Func<EventBase, UniTask> CreateIncomingDispatcher(Type eventType)
|
||||
{
|
||||
var closedMethod = DispatchIncomingGenericMethod.MakeGenericMethod(eventType);
|
||||
return (Func<EventBase, UniTask>)closedMethod.CreateDelegate(typeof(Func<EventBase, UniTask>));
|
||||
return _networkEventTypes.TryGetValue(eventArgs.GetType(), out var registration)
|
||||
? registration.Dispatch(eventArgs)
|
||||
: UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
private static UniTask<object?> DispatchIncomingRequestAsync(ShrinkNetworkContext context, object message)
|
||||
{
|
||||
if (message is not EventBase eventArgs)
|
||||
if (message is not IShrinkEvent eventArgs)
|
||||
{
|
||||
return UniTask.FromResult<object?>(new ShrinkNetworkEventResultResponse
|
||||
{
|
||||
ErrorCode = ShrinkRpcErrorCode.InvalidResponse,
|
||||
ErrorMessage = "Incoming network event request is not an EventBase."
|
||||
ErrorMessage = "Incoming network event request does not implement IShrinkEvent."
|
||||
});
|
||||
}
|
||||
|
||||
var dispatcher = IncomingRequestDispatchers.GetOrAdd(eventArgs.GetType(), CreateIncomingRequestDispatcher);
|
||||
return dispatcher(context, eventArgs);
|
||||
return _networkEventTypes.TryGetValue(eventArgs.GetType(), out var registration) &&
|
||||
registration.RequestDispatch != null
|
||||
? registration.RequestDispatch(context, eventArgs)
|
||||
: UniTask.FromResult<object?>(new ShrinkNetworkEventResultResponse
|
||||
{
|
||||
ErrorCode = ShrinkRpcErrorCode.InvalidResponse,
|
||||
ErrorMessage = $"No generated network event request binding for {eventArgs.GetType().FullName}."
|
||||
});
|
||||
}
|
||||
|
||||
private static Func<ShrinkNetworkContext, EventBase, UniTask<object?>> CreateIncomingRequestDispatcher(Type eventType)
|
||||
{
|
||||
var closedMethod = DispatchIncomingRequestGenericMethod.MakeGenericMethod(eventType);
|
||||
return (Func<ShrinkNetworkContext, EventBase, UniTask<object?>>)closedMethod.CreateDelegate(
|
||||
typeof(Func<ShrinkNetworkContext, EventBase, UniTask<object?>>));
|
||||
}
|
||||
|
||||
private static async UniTask DispatchIncomingGenericAsync<TEvent>(EventBase eventArgs)
|
||||
where TEvent : EventBase
|
||||
internal static async UniTask DispatchGeneratedAsync<TEvent>(TEvent eventArgs)
|
||||
where TEvent : IShrinkEvent
|
||||
{
|
||||
SuppressForwardDepth.Value++;
|
||||
try
|
||||
@@ -348,7 +323,7 @@ namespace ShrinkNetwork.Integration
|
||||
return;
|
||||
}
|
||||
|
||||
await EventBus.TriggerEventAsync((TEvent)eventArgs);
|
||||
await EventBus.PostAsync(eventArgs);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -356,23 +331,21 @@ namespace ShrinkNetwork.Integration
|
||||
}
|
||||
}
|
||||
|
||||
private static async UniTask<object?> DispatchIncomingRequestGenericAsync<TEvent>(ShrinkNetworkContext context,
|
||||
EventBase eventArgs)
|
||||
where TEvent : EventBase
|
||||
internal static async UniTask<object?> DispatchGeneratedRequestAsync<TEvent>(
|
||||
ShrinkNetworkContext context, TEvent eventArgs)
|
||||
where TEvent : IShrinkEvent
|
||||
{
|
||||
var typedEvent = (TEvent)eventArgs;
|
||||
|
||||
SuppressForwardDepth.Value++;
|
||||
try
|
||||
{
|
||||
if (TryGetDeltaEvent(typedEvent, typeof(TEvent), out var deltaEvent) &&
|
||||
if (TryGetDeltaEvent(eventArgs, typeof(TEvent), out var deltaEvent) &&
|
||||
!TryMarkIncomingDelta(typeof(TEvent), deltaEvent, context))
|
||||
{
|
||||
return BuildResultResponse(typedEvent);
|
||||
return BuildResultResponse(eventArgs);
|
||||
}
|
||||
|
||||
await EventBus.TriggerEventAsync(typedEvent);
|
||||
return BuildResultResponse(typedEvent);
|
||||
await EventBus.PostAsync(eventArgs);
|
||||
return BuildResultResponse(eventArgs);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -381,8 +354,10 @@ namespace ShrinkNetwork.Integration
|
||||
{
|
||||
ErrorCode = ShrinkRpcErrorCode.HandlerException,
|
||||
ErrorMessage = ex.Message,
|
||||
Result = typedEvent.HasResult ? typedEvent.Result : EventResult.DEFAULT,
|
||||
IsCanceled = typedEvent.IsCancelable && typedEvent.IsCanceled
|
||||
Result = eventArgs is IShrinkResultEvent<EventResult> resultEvent
|
||||
? resultEvent.Result
|
||||
: EventResult.DEFAULT,
|
||||
IsCanceled = eventArgs is IShrinkCancelableEvent cancelable && cancelable.IsCanceled
|
||||
};
|
||||
}
|
||||
finally
|
||||
@@ -393,62 +368,30 @@ namespace ShrinkNetwork.Integration
|
||||
|
||||
private static void RefreshNetworkEventTypes()
|
||||
{
|
||||
var eventTypes = new Dictionary<Type, ShrinkNetworkMessageAttribute>();
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
Type[] types;
|
||||
try
|
||||
{
|
||||
types = assembly.GetTypes();
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type == null || type.IsAbstract)
|
||||
continue;
|
||||
if (!typeof(EventBase).IsAssignableFrom(type))
|
||||
continue;
|
||||
if (!typeof(IShrinkNetworkMessage).IsAssignableFrom(type))
|
||||
continue;
|
||||
if (type.GetCustomAttribute<ShrinkNetworkEventAttribute>(false) == null)
|
||||
continue;
|
||||
|
||||
var messageAttr = type.GetCustomAttribute<ShrinkNetworkMessageAttribute>(false);
|
||||
if (messageAttr == null)
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"[ShrinkNetwork.Integration] {type.FullName} 标记了 [ShrinkNetworkEvent],但缺少 [ShrinkNetworkMessage],已忽略。");
|
||||
continue;
|
||||
}
|
||||
|
||||
eventTypes[type] = messageAttr;
|
||||
}
|
||||
}
|
||||
var eventTypes = new Dictionary<Type, ShrinkNetworkEventRegistration>();
|
||||
foreach (var registration in ShrinkNetworkEventRegistry.Snapshot())
|
||||
eventTypes[registration.EventType] = registration;
|
||||
|
||||
_networkEventTypes = eventTypes;
|
||||
}
|
||||
|
||||
private static bool TryGetNetworkEventMeta(Type eventType, out ShrinkNetworkMessageAttribute? messageAttribute)
|
||||
private static bool TryGetNetworkEventMeta(Type eventType,
|
||||
out ShrinkNetworkEventRegistration registration)
|
||||
{
|
||||
if (eventType == null)
|
||||
{
|
||||
messageAttribute = null;
|
||||
registration = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return _networkEventTypes.TryGetValue(eventType, out messageAttribute);
|
||||
return _networkEventTypes.TryGetValue(eventType, out registration);
|
||||
}
|
||||
|
||||
private static void EnsureNetworkEventType(Type eventType)
|
||||
{
|
||||
if (!TryGetNetworkEventMeta(eventType, out _))
|
||||
throw new InvalidOperationException(
|
||||
$"Event type {eventType.FullName} must implement EventBase + IShrinkNetworkMessage and declare [ShrinkNetworkEvent] + [ShrinkNetworkMessage].");
|
||||
$"Event type {eventType.FullName} must implement IShrinkEvent + IShrinkNetworkMessage and declare [ShrinkNetworkEvent] + [ShrinkNetworkMessage].");
|
||||
}
|
||||
|
||||
private static void EnsurePublishableEventType(Type eventType)
|
||||
@@ -465,21 +408,23 @@ namespace ShrinkNetwork.Integration
|
||||
if (!typeof(IShrinkNetworkRequest).IsAssignableFrom(eventType))
|
||||
throw new InvalidOperationException(
|
||||
$"Event type {eventType.FullName} must implement IShrinkNetworkRequest to use RequestResultAsync.");
|
||||
if (eventType.GetCustomAttribute<HasResultAttribute>(false) == null)
|
||||
if (!typeof(IShrinkResultEvent<EventResult>).IsAssignableFrom(eventType))
|
||||
throw new InvalidOperationException(
|
||||
$"Event type {eventType.FullName} must declare [HasResult] to use RequestResultAsync.");
|
||||
$"Event type {eventType.FullName} must implement IShrinkResultEvent<EventResult> to use RequestResultAsync.");
|
||||
}
|
||||
|
||||
private static ShrinkNetworkEventResultResponse BuildResultResponse(EventBase eventArgs)
|
||||
private static ShrinkNetworkEventResultResponse BuildResultResponse(IShrinkEvent eventArgs)
|
||||
{
|
||||
return new ShrinkNetworkEventResultResponse
|
||||
{
|
||||
Result = eventArgs.HasResult ? eventArgs.Result : EventResult.DEFAULT,
|
||||
IsCanceled = eventArgs.IsCancelable && eventArgs.IsCanceled
|
||||
Result = eventArgs is IShrinkResultEvent<EventResult> resultEvent
|
||||
? resultEvent.Result
|
||||
: EventResult.DEFAULT,
|
||||
IsCanceled = eventArgs is IShrinkCancelableEvent cancelable && cancelable.IsCanceled
|
||||
};
|
||||
}
|
||||
|
||||
private static bool TryGetDeltaEvent(EventBase eventArgs, Type eventType, out IShrinkNetworkDeltaEvent deltaEvent)
|
||||
private static bool TryGetDeltaEvent(IShrinkEvent eventArgs, Type eventType, out IShrinkNetworkDeltaEvent deltaEvent)
|
||||
{
|
||||
if (eventArgs is IShrinkNetworkDeltaEvent eventDelta)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user