#nullable enable using System; using System.Collections.Generic; using System.Reflection; using Cysharp.Threading.Tasks; namespace ShrinkNetwork { public static class ShrinkNetworkRegHelper { private static readonly MethodInfo AwaitUniTaskResponseMethod = typeof(ShrinkNetworkRegHelper).GetMethod(nameof(AwaitUniTaskResponse), BindingFlags.NonPublic | BindingFlags.Static)!; public static void RegisterAttributedMessages(ShrinkNetworkService service) { RegisterAttributedMessages(service, ShrinkNetworkGeneratedRegistry.GetAttributedMessageTypes()); } public static void RegisterAttributedMessages(ShrinkNetworkService service, IEnumerable messageTypes) { if (service == null) throw new ArgumentNullException(nameof(service)); if (messageTypes == null) throw new ArgumentNullException(nameof(messageTypes)); foreach (var type in messageTypes) { if (type == null || !typeof(IShrinkNetworkMessage).IsAssignableFrom(type)) continue; var attr = type.GetCustomAttribute(false); if (attr == null || service.MessageRegistry.TryGetMeta(type, out _)) continue; service.RegisterMessage(type, attr.Opcode, attr.Route); } } public static void RegisterStaticHandlers(ShrinkNetworkService service) => RegisterStaticHandlers(service, ShrinkNetworkGeneratedRegistry.GetStaticSubscriberTypes()); public static void RegisterStaticHandlers(ShrinkNetworkService service, IEnumerable subscriberTypes) { if (service == null) throw new ArgumentNullException(nameof(service)); if (subscriberTypes == null) throw new ArgumentNullException(nameof(subscriberTypes)); foreach (var type in subscriberTypes) { if (type == null || type.GetCustomAttribute(false) == null) continue; ScanMethodsAndRegister(service, null, type, type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)); } } public static void RegisterHandlers(ShrinkNetworkService service, object target) => RegisterHandlersInternal(service, target); private static void RegisterHandlersInternal(ShrinkNetworkService service, object? target) { if (target == null) return; var ownerType = target.GetType(); if (ownerType.GetCustomAttribute(false) == null) return; ScanMethodsAndRegister(service, target, ownerType, ownerType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)); } private static void ScanMethodsAndRegister(ShrinkNetworkService service, object? target, Type ownerType, MethodInfo[] methods) { foreach (var method in methods) { if (target != null && method.IsStatic) continue; var attributes = method.GetCustomAttributes(typeof(ShrinkNetworkSubscribeAttribute), false); if (attributes.Length == 0) continue; var subscribeAttr = (ShrinkNetworkSubscribeAttribute)attributes[0]; var requirement = new ShrinkNetworkPermissionRequirement(subscribeAttr.Authority, subscribeAttr.Permission); if (!TryParseHandlerSignature(method, out var hasContext, out var messageType, out var responseType)) { ShrinkNetworkLogger.Warn($"[ShrinkNetwork] Invalid handler signature: {ownerType.FullName}.{method.Name}"); continue; } var resolvedMessageType = messageType!; EnsureMessageRegistered(service, resolvedMessageType); if (responseType != null) EnsureMessageRegistered(service, responseType); if (responseType == null) { service.RegisterHandler(resolvedMessageType, (context, message) => InvokeMessageHandler(target, method, hasContext, context, message), requirement); continue; } var resolvedResponseType = responseType!; service.RegisterRequestHandler(resolvedMessageType, resolvedResponseType, (context, request) => InvokeRequestHandler(target, method, hasContext, resolvedResponseType, context, request), requirement); } } private static bool TryParseHandlerSignature(MethodInfo method, out bool hasContext, out Type? messageType, out Type? responseType) { hasContext = false; messageType = null; responseType = null; var parameters = method.GetParameters(); if (parameters.Length == 1) { messageType = parameters[0].ParameterType; } else if (parameters.Length == 2 && parameters[0].ParameterType == typeof(ShrinkNetworkContext)) { hasContext = true; messageType = parameters[1].ParameterType; } else { return false; } if (!typeof(IShrinkNetworkMessage).IsAssignableFrom(messageType)) return false; var returnType = method.ReturnType; if (returnType == typeof(void) || returnType == typeof(UniTask)) return true; if (typeof(IShrinkNetworkResponse).IsAssignableFrom(returnType)) { responseType = returnType; return true; } if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(UniTask<>)) { var resultType = returnType.GetGenericArguments()[0]; if (typeof(IShrinkNetworkResponse).IsAssignableFrom(resultType)) { responseType = resultType; return true; } } return false; } private static void EnsureMessageRegistered(ShrinkNetworkService service, Type messageType) { if (service.MessageRegistry.TryGetMeta(messageType, out _)) return; var attr = messageType.GetCustomAttribute(false); if (attr == null) throw new InvalidOperationException($"Message type {messageType.FullName} must declare [ShrinkNetworkMessage]."); service.RegisterMessage(messageType, attr.Opcode, attr.Route); } private static async UniTask InvokeMessageHandler(object? target, MethodInfo method, bool hasContext, ShrinkNetworkContext context, object message) { var args = hasContext ? new object?[] { context, message } : new object?[] { message }; var result = method.Invoke(target, args); if (method.ReturnType == typeof(UniTask)) await (UniTask)(result ?? throw new InvalidOperationException($"Handler returned null UniTask: {method.DeclaringType?.FullName}.{method.Name}")); } private static async UniTask InvokeRequestHandler(object? target, MethodInfo method, bool hasContext, Type responseType, ShrinkNetworkContext context, object request) { var args = hasContext ? new object?[] { context, request } : new object?[] { request }; var result = method.Invoke(target, args); if (result == null) return null; if (responseType.IsInstanceOfType(result)) return result; if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(UniTask<>)) return await AwaitUniTaskResponseObject(result, responseType); return result; } private static UniTask AwaitUniTaskResponseObject(object taskObject, Type responseType) { return (UniTask)AwaitUniTaskResponseMethod .MakeGenericMethod(responseType) .Invoke(null, new[] { taskObject })!; } private static async UniTask AwaitUniTaskResponse(UniTask task) where TResponse : class, IShrinkNetworkResponse { return await task; } } }