diff --git a/.editorconfig b/.editorconfig index 75e7a15..e11d987 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,98 @@ [*.cs] # IDE0290: Use primary constructor -csharp_style_prefer_primary_constructors = false +csharp_style_prefer_primary_constructors = false:suggestion + +# CA1822: Mark members as static +dotnet_diagnostic.CA1822.severity = none + +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = true:silent +csharp_style_namespace_declarations = file_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_prefer_static_local_function = false:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent +csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent + +[*.{cs,vb}] +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_allow_statement_immediately_after_block_experimental = true:silent diff --git a/GameServer/Controllers/AchievementController.cs b/GameServer/Controllers/AchievementController.cs new file mode 100644 index 0000000..e951478 --- /dev/null +++ b/GameServer/Controllers/AchievementController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class AchievementController : Controller +{ + public AchievementController(PlayerSession session) : base(session) + { + // AchievementMessageHandler. + } + + [NetEvent(MessageId.AchievementInfoRequest)] + public ResponseMessage OnAchievementInfoRequest() => Response(MessageId.AchievementInfoResponse, new AchievementInfoResponse()); +} diff --git a/GameServer/Controllers/Attributes/GameEventAttribute.cs b/GameServer/Controllers/Attributes/GameEventAttribute.cs new file mode 100644 index 0000000..4ba6ff3 --- /dev/null +++ b/GameServer/Controllers/Attributes/GameEventAttribute.cs @@ -0,0 +1,14 @@ +using GameServer.Controllers.Event; + +namespace GameServer.Controllers.Attributes; + +[AttributeUsage(AttributeTargets.Method)] +internal class GameEventAttribute : Attribute +{ + public GameEventType Type { get; } + + public GameEventAttribute(GameEventType type) + { + Type = type; + } +} diff --git a/GameServer/Controllers/Attributes/NetEventAttribute.cs b/GameServer/Controllers/Attributes/NetEventAttribute.cs new file mode 100644 index 0000000..5b619e1 --- /dev/null +++ b/GameServer/Controllers/Attributes/NetEventAttribute.cs @@ -0,0 +1,14 @@ +using Protocol; + +namespace GameServer.Controllers.Attributes; + +[AttributeUsage(AttributeTargets.Method)] +internal class NetEventAttribute : Attribute +{ + public MessageId MessageId { get; } + + public NetEventAttribute(MessageId netMessageId) + { + MessageId = netMessageId; + } +} diff --git a/GameServer/Controllers/Controller.cs b/GameServer/Controllers/Controller.cs new file mode 100644 index 0000000..d32ab43 --- /dev/null +++ b/GameServer/Controllers/Controller.cs @@ -0,0 +1,21 @@ +using GameServer.Network; +using GameServer.Network.Messages; +using Google.Protobuf; +using Protocol; + +namespace GameServer.Controllers; +internal abstract class Controller +{ + protected PlayerSession Session { get; } + + public Controller(PlayerSession session) + { + Session = session; + } + + protected static ResponseMessage Response(MessageId messageId, TProtoBuf protoBuf) where TProtoBuf : IMessage => new() + { + MessageId = messageId, + Payload = protoBuf.ToByteArray() + }; +} diff --git a/GameServer/Controllers/DailyActivityController.cs b/GameServer/Controllers/DailyActivityController.cs new file mode 100644 index 0000000..24f5226 --- /dev/null +++ b/GameServer/Controllers/DailyActivityController.cs @@ -0,0 +1,19 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class DailyActivityController : Controller +{ + public DailyActivityController(PlayerSession session) : base(session) + { + // DailyActivityMessageHandler. + } + + [NetEvent(MessageId.ActivityRequest)] + public ResponseMessage OnActivityRequest() => Response(MessageId.ActivityResponse, new ActivityResponse()); + + [NetEvent(MessageId.LivenessRequest)] + public ResponseMessage OnLivenessRequest() => Response(MessageId.LivenessResponse, new LivenessResponse()); +} diff --git a/GameServer/Controllers/Event/EventSystem.cs b/GameServer/Controllers/Event/EventSystem.cs new file mode 100644 index 0000000..262684e --- /dev/null +++ b/GameServer/Controllers/Event/EventSystem.cs @@ -0,0 +1,58 @@ +using System.Collections.Immutable; +using System.Linq.Expressions; +using System.Reflection; +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Manager; +using GameServer.Models; +using Microsoft.Extensions.Logging; + +namespace GameServer.Controllers.Event; +internal class EventSystem +{ + private static readonly ImmutableDictionary> s_modelManagerEventHandlers = RegisterModelManagerEvents(); + + private readonly ModelManager _modelManager; + private readonly ControllerManager _controllerManager; + + private readonly ILogger _logger; + + public EventSystem(ModelManager modelManager, ControllerManager controllerManager, ILogger logger) + { + _modelManager = modelManager; + _controllerManager = controllerManager; + + _logger = logger; + } + + public async Task Emit(GameEventType eventType) + { + if (s_modelManagerEventHandlers.TryGetValue(eventType, out var handler)) + await handler(_modelManager); + + await _controllerManager.OnEvent(eventType); + + _logger.LogInformation("Event {type} emitted", eventType); + } + + public static ImmutableDictionary> RegisterModelManagerEvents() + { + var builder = ImmutableDictionary.CreateBuilder>(); + + foreach (MethodInfo method in typeof(ModelManager).GetMethods()) + { + GameEventAttribute? attribute = method.GetCustomAttribute(); + if (attribute == null) continue; + + ParameterExpression modelManagerParam = Expression.Parameter(typeof(ModelManager)); + Expression exp = Expression.Call(modelManagerParam, method); + if (method.ReturnType == typeof(void)) + exp = Expression.Block(exp, Expression.Constant(Task.CompletedTask)); + + Expression> lambda = Expression.Lambda>(exp, modelManagerParam); + + builder.Add(attribute.Type, lambda.Compile()); + } + + return builder.ToImmutable(); + } +} diff --git a/GameServer/Controllers/Event/GameEventType.cs b/GameServer/Controllers/Event/GameEventType.cs new file mode 100644 index 0000000..63594c6 --- /dev/null +++ b/GameServer/Controllers/Event/GameEventType.cs @@ -0,0 +1,6 @@ +namespace GameServer.Controllers.Event; +internal enum GameEventType +{ + Login = 1, + EnterGame +} diff --git a/GameServer/Controllers/ExchangeRewardController.cs b/GameServer/Controllers/ExchangeRewardController.cs new file mode 100644 index 0000000..6aae076 --- /dev/null +++ b/GameServer/Controllers/ExchangeRewardController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class ExchangeRewardController : Controller +{ + public ExchangeRewardController(PlayerSession session) : base(session) + { + // ExchangeRewardMessageHandler. + } + + [NetEvent(MessageId.ExchangeRewardInfoRequest)] + public ResponseMessage OnExchangeRewardInfoRequest() => Response(MessageId.ExchangeRewardInfoResponse, new ExchangeRewardInfoResponse()); +} diff --git a/GameServer/Controllers/Factory/EventHandlerFactory.cs b/GameServer/Controllers/Factory/EventHandlerFactory.cs new file mode 100644 index 0000000..4e35ae6 --- /dev/null +++ b/GameServer/Controllers/Factory/EventHandlerFactory.cs @@ -0,0 +1,178 @@ +using System.Collections.Immutable; +using System.Linq.Expressions; +using System.Reflection; +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; +using GameServer.Network.Messages; +using Google.Protobuf; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Protocol; + +namespace GameServer.Controllers.Factory; + +internal delegate Task GameEventHandler(IServiceProvider serviceProvider); +internal class EventHandlerFactory +{ + private readonly ImmutableDictionary s_rpcHandlers; + private readonly ImmutableDictionary s_pushHandlers; + private readonly ImmutableDictionary> s_eventHandlers; + + public EventHandlerFactory(ILogger logger) + { + IEnumerable controllerTypes = Assembly.GetExecutingAssembly().GetTypes() + .Where(t => t.IsAssignableTo(typeof(Controller)) && !t.IsAbstract); + + s_rpcHandlers = RegisterRpcHandlers(controllerTypes); + s_pushHandlers = RegisterPushHandlers(controllerTypes); + s_eventHandlers = RegisterEventHandlers(controllerTypes); + logger.LogInformation("Registered {rpc_count} rpc handlers, {push_count} push handlers", s_rpcHandlers.Count, s_pushHandlers.Count); + } + + public RpcHandler? GetRpcHandler(MessageId messageId) + { + s_rpcHandlers.TryGetValue(messageId, out RpcHandler? handler); + return handler; + } + + public PushHandler? GetPushHandler(MessageId messageId) + { + s_pushHandlers.TryGetValue(messageId, out PushHandler? handler); + return handler; + } + + public IEnumerable GetEventHandlers(GameEventType eventType) + { + if (!s_eventHandlers.TryGetValue(eventType, out List? handlers)) + return []; + + return handlers; + } + + private static ImmutableDictionary> RegisterEventHandlers(IEnumerable controllerTypes) + { + var builder = ImmutableDictionary.CreateBuilder>(); + + MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), [typeof(IServiceProvider)])!; + MethodInfo taskFromResultMethod = typeof(Task).GetMethod(nameof(Task.FromResult))!.MakeGenericMethod(typeof(ResponseMessage)); + + foreach (Type type in controllerTypes) + { + IEnumerable methods = type.GetMethods() + .Where(method => method.GetCustomAttribute() != null + && (method.ReturnType == typeof(Task) || method.ReturnType == typeof(void))); + + foreach (MethodInfo method in methods) + { + GameEventAttribute attribute = method.GetCustomAttribute()!; + ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider)); + + MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam); + Expression handlerCall = Expression.Call(getServiceCall, method, FetchArgumentsForMethod(method, serviceProviderParam, getServiceMethod)); + + if (method.ReturnType == typeof(void)) // Allow non-async methods as well + handlerCall = Expression.Block(handlerCall, Expression.Constant(Task.CompletedTask)); + + Expression lambda = Expression.Lambda(handlerCall, serviceProviderParam); + + if (!builder.TryGetKey(attribute.Type, out _)) + builder.Add(attribute.Type, []); + + builder[attribute.Type].Add(lambda.Compile()); + } + } + + return builder.ToImmutable(); + } + + private static ImmutableDictionary RegisterRpcHandlers(IEnumerable controllerTypes) + { + var builder = ImmutableDictionary.CreateBuilder(); + + MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), [typeof(IServiceProvider)])!; + MethodInfo taskFromResultMethod = typeof(Task).GetMethod(nameof(Task.FromResult))!.MakeGenericMethod(typeof(ResponseMessage)); + + foreach (Type type in controllerTypes) + { + IEnumerable methods = type.GetMethods() + .Where(method => method.GetCustomAttribute() != null + && (method.ReturnType == typeof(Task) || method.ReturnType == typeof(ResponseMessage))); + + foreach (MethodInfo method in methods) + { + NetEventAttribute attribute = method.GetCustomAttribute()!; + + ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider)); + ParameterExpression dataParam = Expression.Parameter(typeof(ReadOnlySpan)); + + MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam); + Expression handlerCall = Expression.Call(getServiceCall, method, FetchArgumentsForMethod(method, serviceProviderParam, getServiceMethod, dataParam)); + + if (method.ReturnType == typeof(ResponseMessage)) // Allow non-async methods as well + handlerCall = Expression.Call(taskFromResultMethod, handlerCall); + + Expression lambda = Expression.Lambda(handlerCall, serviceProviderParam, dataParam); + + builder.Add(attribute.MessageId, lambda.Compile()); + } + } + + return builder.ToImmutable(); + } + + private static ImmutableDictionary RegisterPushHandlers(IEnumerable controllerTypes) + { + var builder = ImmutableDictionary.CreateBuilder(); + + MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod(nameof(ServiceProviderServiceExtensions.GetRequiredService), [typeof(IServiceProvider)])!; + MethodInfo taskFromResultMethod = typeof(Task).GetMethod(nameof(Task.FromResult))!.MakeGenericMethod(typeof(ResponseMessage)); + + foreach (Type type in controllerTypes) + { + IEnumerable methods = type.GetMethods() + .Where(method => method.GetCustomAttribute() != null + && (method.ReturnType == typeof(Task) || method.ReturnType == typeof(void))); + + foreach (MethodInfo method in methods) + { + NetEventAttribute attribute = method.GetCustomAttribute()!; + + ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider)); + ParameterExpression dataParam = Expression.Parameter(typeof(ReadOnlySpan)); + + MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam); + Expression handlerCall = Expression.Call(getServiceCall, method, FetchArgumentsForMethod(method, serviceProviderParam, getServiceMethod, dataParam)); + + if (method.ReturnType == typeof(void)) // Allow non-async methods as well + handlerCall = Expression.Block(handlerCall, Expression.Constant(Task.CompletedTask)); + + Expression lambda = Expression.Lambda(handlerCall, serviceProviderParam, dataParam); + + builder.Add(attribute.MessageId, lambda.Compile()); + } + } + + return builder.ToImmutable(); + } + + private static List FetchArgumentsForMethod(MethodInfo method, Expression serviceProviderParam, MethodInfo getServiceMethod, Expression? dataParam = null) + { + List arguments = []; + foreach (ParameterInfo param in method.GetParameters()) + { + if (dataParam != null && param.ParameterType.IsAssignableTo(typeof(IMessage))) + { + PropertyInfo parser = (param.ParameterType.GetMember("Parser", BindingFlags.Static | BindingFlags.Public).Single() as PropertyInfo)!; + MethodInfo parseFrom = parser.PropertyType.GetMethod(nameof(MessageParser.ParseFrom), [typeof(ReadOnlySpan)])!; + + arguments.Add(Expression.Call(Expression.Constant(parser.GetValue(null)), parseFrom, dataParam)); + } + else + { + arguments.Add(Expression.Call(getServiceMethod.MakeGenericMethod(param.ParameterType), serviceProviderParam)); + } + } + + return arguments; + } +} diff --git a/GameServer/Controllers/FormationController.cs b/GameServer/Controllers/FormationController.cs new file mode 100644 index 0000000..d264269 --- /dev/null +++ b/GameServer/Controllers/FormationController.cs @@ -0,0 +1,31 @@ +using GameServer.Controllers.Attributes; +using GameServer.Models; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class FormationController : Controller +{ + private readonly ModelManager _modelManager; + + public FormationController(PlayerSession session, ModelManager modelManager) : base(session) + { + _modelManager = modelManager; + } + + [NetEvent(MessageId.GetFormationDataRequest)] + public ResponseMessage OnGetFormationDataRequest() => Response(MessageId.GetFormationDataResponse, new GetFormationDataResponse + { + Formations = + { + new FightFormation + { + CurRole = _modelManager.Player.CharacterId, + FormationId = 1, + IsCurrent = true, + RoleIds = { _modelManager.Player.CharacterId }, + } + }, + }); +} diff --git a/GameServer/Controllers/FriendSystemController.cs b/GameServer/Controllers/FriendSystemController.cs new file mode 100644 index 0000000..cc6c0a4 --- /dev/null +++ b/GameServer/Controllers/FriendSystemController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class FriendSystemController : Controller +{ + public FriendSystemController(PlayerSession session) : base(session) + { + // FriendMessageHandler. + } + + [NetEvent(MessageId.FriendAllRequest)] + public ResponseMessage OnFriendAllRequest() => Response(MessageId.FriendAllResponse, new FriendAllResponse()); +} diff --git a/GameServer/Controllers/FunctionalController.cs b/GameServer/Controllers/FunctionalController.cs new file mode 100644 index 0000000..2174bc3 --- /dev/null +++ b/GameServer/Controllers/FunctionalController.cs @@ -0,0 +1,32 @@ +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; +using GameServer.Network; +using Protocol; + +namespace GameServer.Controllers; +internal class FunctionalController : Controller +{ + private static readonly int[] s_functions = [10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10017, 10018, 10019, 10020, 10021, 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029, 10030, 10031, 10033, 10034, 10035, 10036, 10041, 10042, 10043, 10046, 10047, 10048, 10049, 10050, 10052, 10023001, 10023002, 10023004, 10023005, 10053, 10054, 10001003, 10055, 10026001, 10026002, 10026003, 10026004, 10026005, 10026006, 10026008, 10056, 10026101, 110057, 10001004, 10037, 10057, 10059, 10058, 10023003, 10032, 110056, 110058, 10060, 10061]; + + public FunctionalController(PlayerSession session) : base(session) + { + // FunctionalController. + } + + [GameEvent(GameEventType.EnterGame)] + public async Task OnEnterGame() + { + FuncOpenNotify notify = new(); + + foreach (int id in s_functions) + { + notify.Func.Add(new Function + { + Id = id, + Flag = 2 + }); + } + + await Session.Push(MessageId.FuncOpenNotify, notify); + } +} diff --git a/GameServer/Controllers/GachaController.cs b/GameServer/Controllers/GachaController.cs new file mode 100644 index 0000000..74deaef --- /dev/null +++ b/GameServer/Controllers/GachaController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class GachaController : Controller +{ + public GachaController(PlayerSession session) : base(session) + { + // GachaMessageHandler. + } + + [NetEvent(MessageId.GachaInfoRequest)] + public ResponseMessage OnGachaInfoRequest() => Response(MessageId.GachaInfoResponse, new GachaInfoResponse()); +} diff --git a/GameServer/Controllers/InfluenceReputationController.cs b/GameServer/Controllers/InfluenceReputationController.cs new file mode 100644 index 0000000..d9bde5c --- /dev/null +++ b/GameServer/Controllers/InfluenceReputationController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class InfluenceReputationController : Controller +{ + public InfluenceReputationController(PlayerSession session) : base(session) + { + // InfluenceReputationMessageHandler. + } + + [NetEvent(MessageId.InfluenceInfoRequest)] + public ResponseMessage OnInfluenceInfoRequest() => Response(MessageId.InfluenceInfoResponse, new InfluenceInfoResponse()); +} diff --git a/GameServer/Controllers/InventoryController.cs b/GameServer/Controllers/InventoryController.cs new file mode 100644 index 0000000..4643637 --- /dev/null +++ b/GameServer/Controllers/InventoryController.cs @@ -0,0 +1,25 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class InventoryController : Controller +{ + public InventoryController(PlayerSession session) : base(session) + { + // InventoryMessageHandler. + } + + [NetEvent(MessageId.NormalItemRequest)] + public ResponseMessage OnNormalItemRequest() => Response(MessageId.NormalItemResponse, new NormalItemResponse()); + + [NetEvent(MessageId.WeaponItemRequest)] + public ResponseMessage OnWeaponItemRequest() => Response(MessageId.WeaponItemResponse, new WeaponItemResponse()); + + [NetEvent(MessageId.PhantomItemRequest)] + public ResponseMessage OnPhantomItemRequest() => Response(MessageId.PhantomItemResponse, new PhantomItemResponse()); + + [NetEvent(MessageId.ItemExchangeInfoRequest)] + public ResponseMessage OnItemExchangeInfoRequest() => Response(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse()); +} diff --git a/GameServer/Controllers/LoginController.cs b/GameServer/Controllers/LoginController.cs new file mode 100644 index 0000000..d4fcb4a --- /dev/null +++ b/GameServer/Controllers/LoginController.cs @@ -0,0 +1,42 @@ +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; +using GameServer.Network; +using GameServer.Network.Messages; +using Microsoft.Extensions.Logging; +using Protocol; + +namespace GameServer.Controllers; +internal class LoginController : Controller +{ + public LoginController(PlayerSession session) : base(session) + { + // LoginController. + } + + [NetEvent(MessageId.LoginRequest)] + public async Task OnLoginRequest(EventSystem eventSystem) + { + await eventSystem.Emit(GameEventType.Login); + + return Response(MessageId.LoginResponse, new LoginResponse + { + Code = 0, + Platform = "PC", + Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds() + }); + } + + [NetEvent(MessageId.EnterGameRequest)] + public async Task OnEnterGameRequest(EnterGameRequest request, ILogger logger, EventSystem eventSystem) + { + logger.LogInformation("Enter Game Request:\n{req}", request); + + await eventSystem.Emit(GameEventType.EnterGame); + await Session.Push(MessageId.PushDataCompleteNotify, new PushDataCompleteNotify()); + + return Response(MessageId.EnterGameResponse, new EnterGameResponse()); + } + + [NetEvent(MessageId.HeartbeatRequest)] + public ResponseMessage OnHeartbeatRequest() => Response(MessageId.HeartbeatResponse, new HeartbeatResponse()); +} \ No newline at end of file diff --git a/GameServer/Controllers/LordGymController.cs b/GameServer/Controllers/LordGymController.cs new file mode 100644 index 0000000..8f80d7d --- /dev/null +++ b/GameServer/Controllers/LordGymController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class LordGymController : Controller +{ + public LordGymController(PlayerSession session) : base(session) + { + // LordGymMessageHandler. + } + + [NetEvent(MessageId.LordGymInfoRequest)] + public ResponseMessage OnLordGymInfoRequest() => Response(MessageId.LordGymInfoResponse, new LordGymInfoResponse()); +} diff --git a/GameServer/Controllers/Manager/ControllerManager.cs b/GameServer/Controllers/Manager/ControllerManager.cs new file mode 100644 index 0000000..34ea41c --- /dev/null +++ b/GameServer/Controllers/Manager/ControllerManager.cs @@ -0,0 +1,32 @@ +using GameServer.Controllers.Event; +using GameServer.Controllers.Factory; +using GameServer.Network; +using Microsoft.Extensions.DependencyInjection; + +namespace GameServer.Controllers.Manager; +internal class ControllerManager +{ + private readonly IServiceProvider _serviceProvider; + private readonly EventHandlerFactory _eventHandlerFactory; + + public ControllerManager(IServiceProvider serviceProvider, EventHandlerFactory handlerFactory) + { + _serviceProvider = serviceProvider; + _eventHandlerFactory = handlerFactory; + } + + public async Task OnEvent(GameEventType eventType) + { + IEnumerable handlers = _eventHandlerFactory.GetEventHandlers(eventType); + + foreach (GameEventHandler handler in handlers) + { + await handler(_serviceProvider); + } + } + + public TController Get() where TController : Controller + { + return _serviceProvider.GetRequiredService(); + } +} diff --git a/GameServer/Controllers/PlayerInfoController.cs b/GameServer/Controllers/PlayerInfoController.cs new file mode 100644 index 0000000..7626377 --- /dev/null +++ b/GameServer/Controllers/PlayerInfoController.cs @@ -0,0 +1,50 @@ +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; +using GameServer.Models; +using GameServer.Network; +using Protocol; + +namespace GameServer.Controllers; +internal class PlayerInfoController : Controller +{ + public PlayerInfoController(PlayerSession session) : base(session) + { + // PlayerInfoController. + } + + [GameEvent(GameEventType.EnterGame)] + public async Task OnEnterGame(ModelManager modelManager) + { + PlayerModel player = modelManager.Player; + + await Session.Push(MessageId.BasicInfoNotify, new BasicInfoNotify + { + RandomSeed = 1337, + Id = player.Id, + Birthday = 0, + Attributes = + { + new PlayerAttr + { + Key = (int)PlayerAttrKey.Name, + ValueType = (int)PlayerAttrType.String, + StringValue = player.Name + }, + new PlayerAttr + { + Key = (int)PlayerAttrKey.Level, + ValueType = (int)PlayerAttrType.Int32, + Int32Value = 10 + } + }, + RoleShowList = + { + new RoleShowEntry + { + Level = 1, + RoleId = player.CharacterId + } + }, + }); + } +} diff --git a/GameServer/Controllers/RoguelikeController.cs b/GameServer/Controllers/RoguelikeController.cs new file mode 100644 index 0000000..aa00631 --- /dev/null +++ b/GameServer/Controllers/RoguelikeController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class RoguelikeController : Controller +{ + public RoguelikeController(PlayerSession session) : base(session) + { + // RoguelikeMessageHandler. + } + + [NetEvent(MessageId.RoguelikeSeasonDataRequest)] + public ResponseMessage OnRoguelikeSeasonDataRequest() => Response(MessageId.RoguelikeSeasonDataResponse, new RoguelikeSeasonDataResponse()); +} diff --git a/GameServer/Controllers/RoleController.cs b/GameServer/Controllers/RoleController.cs new file mode 100644 index 0000000..ac1e429 --- /dev/null +++ b/GameServer/Controllers/RoleController.cs @@ -0,0 +1,36 @@ +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; +using GameServer.Models; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class RoleController : Controller +{ + public RoleController(PlayerSession session) : base(session) + { + // RoleMessageHandler. + } + + [GameEvent(GameEventType.EnterGame)] + public async Task OnEnterGame(ModelManager modelManager) + { + PlayerModel player = modelManager.Player; + + await Session.Push(MessageId.PbGetRoleListNotify, new PbGetRoleListNotify + { + RoleList = + { + new roleInfo + { + RoleId = player.CharacterId, + Level = 1, + } + } + }); + } + + [NetEvent(MessageId.RoleFavorListRequest)] + public ResponseMessage OnRoleFavorListRequest() => Response(MessageId.RoleFavorListResponse, new RoleFavorListResponse()); +} diff --git a/GameServer/Controllers/ShopController.cs b/GameServer/Controllers/ShopController.cs new file mode 100644 index 0000000..5fbeaa2 --- /dev/null +++ b/GameServer/Controllers/ShopController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class ShopController : Controller +{ + public ShopController(PlayerSession session) : base(session) + { + // ShopMessageHandler. + } + + [NetEvent(MessageId.PayShopInfoRequest)] + public ResponseMessage OnPayShopInfoRequest() => Response(MessageId.PayShopInfoResponse, new PayShopInfoResponse()); +} diff --git a/GameServer/Controllers/TowerController.cs b/GameServer/Controllers/TowerController.cs new file mode 100644 index 0000000..435cbea --- /dev/null +++ b/GameServer/Controllers/TowerController.cs @@ -0,0 +1,19 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class TowerController : Controller +{ + public TowerController(PlayerSession session) : base(session) + { + // TowerMessageHandler. + } + + [NetEvent(MessageId.TowerChallengeRequest)] + public ResponseMessage OnTowerChallengeRequest() => Response(MessageId.TowerChallengeResponse, new TowerChallengeResponse()); + + [NetEvent(MessageId.CycleTowerChallengeRequest)] + public ResponseMessage OnCycleTowerChallengeRequest() => Response(MessageId.CycleTowerChallengeResponse, new CycleTowerChallengeResponse()); +} diff --git a/GameServer/Handlers/TutorialMessageHandler.cs b/GameServer/Controllers/TutorialController.cs similarity index 52% rename from GameServer/Handlers/TutorialMessageHandler.cs rename to GameServer/Controllers/TutorialController.cs index 6d1e39c..20496f7 100644 --- a/GameServer/Handlers/TutorialMessageHandler.cs +++ b/GameServer/Controllers/TutorialController.cs @@ -1,17 +1,18 @@ -using GameServer.Handlers.Attributes; +using GameServer.Controllers.Attributes; using GameServer.Network; +using GameServer.Network.Messages; using Protocol; -namespace GameServer.Handlers; -internal class TutorialMessageHandler : MessageHandlerBase +namespace GameServer.Controllers; +internal class TutorialController : Controller { - public TutorialMessageHandler(PlayerSession session) : base(session) + public TutorialController(PlayerSession session) : base(session) { // TutorialMessageHandler. } - [MessageHandler(MessageId.TutorialInfoRequest)] - public async Task OnTutorialInfoRequest(ReadOnlyMemory data) + [NetEvent(MessageId.TutorialInfoRequest)] + public ResponseMessage OnTutorialInfoRequest() { int[] tutorials = [30001, 30002, 30003, 30004, 30005, 30006, 30007, 30011, 30012, 30008, 30009, 30010, 30013, 30014, 30015, 30016, 30017, 30018, 30019, 30020, 30021, 30022, 30023, 30024, 40001, 30025, 30026, 30027, 30028, 30029, 30030, 30031, 30032, 30033, 30034, 30035, 30036, 50001, 50002, 50003, 50004, 50005, 50006, 50007, 50008, 50009, 50010, 50011, 33001, 34017, 34018, 32001, 32002, 32003, 32004, 32005, 32006, 32007, 32008, 32009, 32010, 32011, 32012, 32013, 32014, 32015, 32016, 32017, 32018, 32019, 32020, 32021, 33002, 33003, 33004, 33005, 34001, 34002, 34003, 34004, 34005, 34006, 34007, 34008, 34009, 34010, 34011, 34012, 34013, 34014, 34015, 34016, 34019, 34020, 34021, 34022, 34023, 34024, 34025, 34027, 34028, 34029, 34030, 34031, 34032, 34033]; TutorialInfoResponse rsp = new(); @@ -25,11 +26,11 @@ internal class TutorialMessageHandler : MessageHandlerBase }); } - await Session.Rpc.ReturnAsync(MessageId.TutorialInfoResponse, rsp); + return Response(MessageId.TutorialInfoResponse, rsp); } - [MessageHandler(MessageId.GetDetectionLabelInfoRequest)] - public async Task OnGetDetectionLabelInfoRequest(ReadOnlyMemory _) + [NetEvent(MessageId.GetDetectionLabelInfoRequest)] + public ResponseMessage OnGetDetectionLabelInfoRequest() { int[] guides = [0, 1, 2, 3, 14, 15, 16, 4, 21, 22, 7, 5, 18, 6, 61, 8, 9, 10, 11, 12, 13, 17, 19]; int[] detectionTexts = [1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 61]; @@ -38,15 +39,12 @@ internal class TutorialMessageHandler : MessageHandlerBase rsp.UnlockLabelInfo.UnlockedGuideIds.AddRange(guides); rsp.UnlockLabelInfo.UnlockedDetectionTextIds.AddRange(detectionTexts); - await Session.Rpc.ReturnAsync(MessageId.GetDetectionLabelInfoResponse, rsp); + return Response(MessageId.GetDetectionLabelInfoResponse, rsp); } - [MessageHandler(MessageId.GuideInfoRequest)] - public async Task OnGuideInfoRequest(ReadOnlyMemory _) + [NetEvent(MessageId.GuideInfoRequest)] + public ResponseMessage OnGuideInfoRequest() => Response(MessageId.GuideInfoResponse, new GuideInfoResponse() { - await Session.Rpc.ReturnAsync(MessageId.GuideInfoResponse, new GuideInfoResponse() - { - GuideGroupFinishList = { 60001, 60002, 60003, 60004, 60005, 60006, 60007, 60008, 60009, 60010, 60011, 60012, 60013, 60014, 60015, 60016, 60017, 60018, 60019, 60020, 60021, 60101, 60102, 60103, 62002, 62004, 62005, 62006, 62007, 62009, 62010, 62011, 62012, 62013, 62014, 62015, 62016, 62017, 62022, 62027, 62028, 62029, 62030, 62031, 62032, 62033, 62034, 62036, 65001, 67001, 67002, 67003, 67004, 67005, 67006, 67007, 67008, 67009, 67010, 67011, 67012, 67013, 67014, 67015, 67016, 67017, 67018, 67019, 67022, 62001, 62008, 62018, 62019, 62020, 62021, 62023, 62024, 62025, 62026, 62035, 65002, 65003, 65004, 65005 } - }); - } + GuideGroupFinishList = { 60001, 60002, 60003, 60004, 60005, 60006, 60007, 60008, 60009, 60010, 60011, 60012, 60013, 60014, 60015, 60016, 60017, 60018, 60019, 60020, 60021, 60101, 60102, 60103, 62002, 62004, 62005, 62006, 62007, 62009, 62010, 62011, 62012, 62013, 62014, 62015, 62016, 62017, 62022, 62027, 62028, 62029, 62030, 62031, 62032, 62033, 62034, 62036, 65001, 67001, 67002, 67003, 67004, 67005, 67006, 67007, 67008, 67009, 67010, 67011, 67012, 67013, 67014, 67015, 67016, 67017, 67018, 67019, 67022, 62001, 62008, 62018, 62019, 62020, 62021, 62023, 62024, 62025, 62026, 62035, 65002, 65003, 65004, 65005 } + }); } diff --git a/GameServer/Handlers/AuthMessageHandler.cs b/GameServer/Controllers/WorldController.cs similarity index 61% rename from GameServer/Handlers/AuthMessageHandler.cs rename to GameServer/Controllers/WorldController.cs index 980c9fc..433ad6b 100644 --- a/GameServer/Handlers/AuthMessageHandler.cs +++ b/GameServer/Controllers/WorldController.cs @@ -1,107 +1,24 @@ -using GameServer.Handlers.Attributes; +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; using GameServer.Models; using GameServer.Network; +using GameServer.Network.Messages; using Protocol; -namespace GameServer.Handlers; -internal class AuthMessageHandler : MessageHandlerBase +namespace GameServer.Controllers; +internal class WorldController : Controller { - private readonly ModelManager _modelManager; - - public AuthMessageHandler(PlayerSession session, ModelManager modelManager) : base(session) + public WorldController(PlayerSession session) : base(session) { - _modelManager = modelManager; + // WorldMessageHandler. } - [MessageHandler(MessageId.LoginRequest)] - public async Task OnLoginRequest(ReadOnlyMemory _) + [GameEvent(GameEventType.EnterGame)] + public async Task OnEnterGame(ModelManager modelManager) { - _modelManager.OnLogin(); + PlayerModel player = modelManager.Player; - await Session.Rpc.ReturnAsync(MessageId.LoginResponse, new LoginResponse - { - Code = 0, - Platform = "PC", - Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds() - }); - } - - [MessageHandler(MessageId.EnterGameRequest)] - public async Task OnEnterGameRequest(ReadOnlyMemory data) - { - EnterGameRequest request = EnterGameRequest.Parser.ParseFrom(data.Span); - - await Session.PushMessage(MessageId.BasicInfoNotify, new BasicInfoNotify - { - RandomSeed = 1337, - Id = _modelManager.Player.Id, - Birthday = 0, - Attributes = - { - new PlayerAttr - { - Key = (int)PlayerAttrKey.Name, - ValueType = (int)PlayerAttrType.String, - StringValue = _modelManager.Player.Name - }, - new PlayerAttr - { - Key = (int)PlayerAttrKey.Level, - ValueType = (int)PlayerAttrType.Int32, - Int32Value = 10 - } - }, - RoleShowList = - { - new RoleShowEntry - { - Level = 1, - RoleId = _modelManager.Player.CharacterId - } - }, - }); - - int[] quests = [10010001, 10010002, 20020010, 20017001, 10010003, 10010004, 10010005, 10010006, 10010007, 20010001, 20010002, 20040001, 10010008, 10010009, 10010010, 10010011, 20010003, 20040002, 20021501, 20020004, 20010004, 20040003, 20040004, 20030001, 20032004, 20010005, 20040005, 20040006, 20040007, 20160001, 20160002, 20010007, 20010008, 20012003, 20010006, 20042301, 20032005, 20032006, 20032007, 20032008, 20032009, 59990001, 59990002, 59990003, 59990004, 50010001, 50010002, 50010003, 50010004, 50010005, 50010006, 50010007, 50020001, 50020002, 50020003, 50020004, 50020005, 50020006, 50020007, 50030001, 50030002, 50030003, 50030004, 50030005, 50030006, 50030007, 50040001, 50040002, 50040003, 50040004, 50000001, 50000002, 50000003, 50000004, 50000005, 50000006, 50000007, 50000008, 50050001, 50050002, 50050003, 50050004, 50060001, 50060002, 50060003, 50060004]; - QuestListNotify questList = new(); - - foreach (int id in quests) - { - questList.Quests.Add(new QuestInfo - { - Status = 3, - QuestId = id, - }); - } - - int[] functions = [10001, 10002, 10003, 10004, 10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10017, 10018, 10019, 10020, 10021, 10022, 10023, 10024, 10025, 10026, 10027, 10028, 10029, 10030, 10031, 10033, 10034, 10035, 10036, 10041, 10042, 10043, 10046, 10047, 10048, 10049, 10050, 10052, 10023001, 10023002, 10023004, 10023005, 10053, 10054, 10001003, 10055, 10026001, 10026002, 10026003, 10026004, 10026005, 10026006, 10026008, 10056, 10026101, 110057, 10001004, 10037, 10057, 10059, 10058, 10023003, 10032, 110056, 110058, 10060, 10061]; - FuncOpenNotify funcOpen = new(); - - foreach (int id in functions) - { - funcOpen.Func.Add(new Function - { - Id = id, - Flag = 2 - }); - } - - await Session.PushMessage(MessageId.FuncOpenNotify, funcOpen); - - await Session.PushMessage(MessageId.QuestListNotify, questList); - - await Session.PushMessage(MessageId.PbGetRoleListNotify, new PbGetRoleListNotify - { - RoleList = - { - new roleInfo - { - RoleId = _modelManager.Player.CharacterId, - Level = 1, - } - } - }); - - await Session.PushMessage(MessageId.JoinSceneNotify, new JoinSceneNotify + await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify { MaxEntityId = 2, TransitionOption = new TransitionOptionPb @@ -110,21 +27,21 @@ internal class AuthMessageHandler : MessageHandlerBase }, SceneInfo = new SceneInformation { - OwnerId = 1337, + OwnerId = player.Id, Mode = (int)SceneMode.Single, InstanceId = 8, AoiData = new PlayerSceneAoiData { - GenIds = {1}, + GenIds = { 1 }, Entities = { new EntityPb { EntityState = (int)EntityState.Born, EntityType = (int)EEntityType.Player, - PlayerId = _modelManager.Player.Id, + PlayerId = player.Id, LivingStatus = (int)LivingStatus.Alive, - ConfigId = _modelManager.Player.CharacterId, + ConfigId = player.CharacterId, ConfigType = (int)EntityConfigType.Character, Id = 1, IsVisible = true, @@ -244,11 +161,11 @@ internal class AuthMessageHandler : MessageHandlerBase { Hour = 23 }, - PlayerInfos = + PlayerInfos = { new ScenePlayerInformation { - PlayerId = _modelManager.Player.Id, + PlayerId = player.Id, Level = 1, IsOffline = false, Location = new() @@ -265,25 +182,33 @@ internal class AuthMessageHandler : MessageHandlerBase CurHp = 1000, MaxHp = 1000, IsControl = true, - RoleId = _modelManager.Player.CharacterId, + RoleId = player.CharacterId, RoleLevel = 1, } }, - PlayerName = "ReversedRooms" + PlayerName = player.Name } }, - CurContextId = _modelManager.Player.Id + CurContextId = player.Id } }); - - await Session.PushMessage(MessageId.PushDataCompleteNotify, new PushDataCompleteNotify()); - - await Session.Rpc.ReturnAsync(MessageId.EnterGameResponse, new EnterGameResponse()); } - [MessageHandler(MessageId.HeartbeatRequest)] - public async Task OnHeartbeatRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.HeartbeatResponse, new HeartbeatResponse()); - } + [NetEvent(MessageId.EntityActiveRequest)] + public ResponseMessage OnEntityActiveRequest() => Response(MessageId.EntityActiveResponse, new EntityActiveResponse()); + + [NetEvent(MessageId.EntityOnLandedRequest)] + public ResponseMessage OnEntityOnLandedRequest() => Response(MessageId.EntityOnLandedResponse, new EntityOnLandedResponse()); + + [NetEvent(MessageId.PlayerMotionRequest)] + public ResponseMessage OnPlayerMotionRequest() => Response(MessageId.PlayerMotionResponse, new PlayerMotionResponse()); + + [NetEvent(MessageId.EntityLoadCompleteRequest)] + public ResponseMessage OnEntityLoadCompleteRequest() => Response(MessageId.EntityLoadCompleteResponse, new EntityLoadCompleteResponse()); + + [NetEvent(MessageId.SceneLoadingFinishRequest)] + public ResponseMessage OnSceneLoadingFinishRequest() => Response(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse()); + + [NetEvent(MessageId.UpdateSceneDateRequest)] + public ResponseMessage OnUpdateSceneDateRequest() => Response(MessageId.UpdateSceneDateResponse, new UpdateSceneDateResponse()); } diff --git a/GameServer/Controllers/WorldMapController.cs b/GameServer/Controllers/WorldMapController.cs new file mode 100644 index 0000000..7398260 --- /dev/null +++ b/GameServer/Controllers/WorldMapController.cs @@ -0,0 +1,16 @@ +using GameServer.Controllers.Attributes; +using GameServer.Network; +using GameServer.Network.Messages; +using Protocol; + +namespace GameServer.Controllers; +internal class WorldMapController : Controller +{ + public WorldMapController(PlayerSession session) : base(session) + { + // WorldMapMessageHandler. + } + + [NetEvent(MessageId.MapTraceInfoRequest)] + public ResponseMessage OnMapTraceInfoRequest() => Response(MessageId.MapTraceInfoResponse, new MapTraceInfoResponse()); +} diff --git a/GameServer/Extensions/ServiceCollectionExtensions.cs b/GameServer/Extensions/ServiceCollectionExtensions.cs index 2f9bde4..1904afc 100644 --- a/GameServer/Extensions/ServiceCollectionExtensions.cs +++ b/GameServer/Extensions/ServiceCollectionExtensions.cs @@ -1,16 +1,16 @@ using System.Reflection; -using GameServer.Handlers; +using GameServer.Controllers; using Microsoft.Extensions.DependencyInjection; namespace GameServer.Extensions; internal static class ServiceCollectionExtensions { - public static IServiceCollection AddHandlers(this IServiceCollection services) + public static IServiceCollection AddControllers(this IServiceCollection services) { - IEnumerable handlerTypes = Assembly.GetExecutingAssembly().GetTypes() - .Where(t => t.IsAssignableTo(typeof(MessageHandlerBase)) && !t.IsAbstract); + IEnumerable controllerTypes = Assembly.GetExecutingAssembly().GetTypes() + .Where(t => t.IsAssignableTo(typeof(Controller)) && !t.IsAbstract); - foreach (Type type in handlerTypes) + foreach (Type type in controllerTypes) { services.AddScoped(type); } diff --git a/GameServer/Handlers/AchievementMessageHandler.cs b/GameServer/Handlers/AchievementMessageHandler.cs deleted file mode 100644 index 3bbcb22..0000000 --- a/GameServer/Handlers/AchievementMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class AchievementMessageHandler : MessageHandlerBase -{ - public AchievementMessageHandler(PlayerSession session) : base(session) - { - // AchievementMessageHandler. - } - - [MessageHandler(MessageId.AchievementInfoRequest)] - public async Task OnAchievementInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.AchievementInfoResponse, new AchievementInfoResponse()); - } -} diff --git a/GameServer/Handlers/Attributes/MessageHandlerAttribute.cs b/GameServer/Handlers/Attributes/MessageHandlerAttribute.cs deleted file mode 100644 index 310ab9c..0000000 --- a/GameServer/Handlers/Attributes/MessageHandlerAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Protocol; - -namespace GameServer.Handlers.Attributes; - -[AttributeUsage(AttributeTargets.Method)] -internal class MessageHandlerAttribute : Attribute -{ - public MessageId MessageId { get; } - - public MessageHandlerAttribute(MessageId messageId) - { - MessageId = messageId; - } -} diff --git a/GameServer/Handlers/DailyActivityMessageHandler.cs b/GameServer/Handlers/DailyActivityMessageHandler.cs deleted file mode 100644 index 71bc86b..0000000 --- a/GameServer/Handlers/DailyActivityMessageHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class DailyActivityMessageHandler : MessageHandlerBase -{ - public DailyActivityMessageHandler(PlayerSession session) : base(session) - { - // DailyActivityMessageHandler. - } - - [MessageHandler(MessageId.ActivityRequest)] - public async Task OnActivityRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.ActivityResponse, new ActivityResponse()); - } - - [MessageHandler(MessageId.LivenessRequest)] - public async Task OnLivenessRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.LivenessResponse, new LivenessResponse()); - } -} diff --git a/GameServer/Handlers/ExchangeRewardMessageHandler.cs b/GameServer/Handlers/ExchangeRewardMessageHandler.cs deleted file mode 100644 index 9ac8700..0000000 --- a/GameServer/Handlers/ExchangeRewardMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class ExchangeRewardMessageHandler : MessageHandlerBase -{ - public ExchangeRewardMessageHandler(PlayerSession session) : base(session) - { - // ExchangeRewardMessageHandler. - } - - [MessageHandler(MessageId.ExchangeRewardInfoRequest)] - public async Task OnExchangeRewardInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.ExchangeRewardInfoResponse, new ExchangeRewardInfoResponse()); - } -} diff --git a/GameServer/Handlers/Factory/MessageHandlerFactory.cs b/GameServer/Handlers/Factory/MessageHandlerFactory.cs deleted file mode 100644 index 68fa46a..0000000 --- a/GameServer/Handlers/Factory/MessageHandlerFactory.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Immutable; -using System.Linq.Expressions; -using System.Reflection; -using GameServer.Handlers.Attributes; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Protocol; - -namespace GameServer.Handlers.Factory; -internal class MessageHandlerFactory -{ - private readonly ImmutableDictionary s_messageHandlers; - - public MessageHandlerFactory(ILogger logger) - { - IEnumerable handlerTypes = Assembly.GetExecutingAssembly().GetTypes() - .Where(t => t.IsAssignableTo(typeof(MessageHandlerBase)) && !t.IsAbstract); - - s_messageHandlers = GenerateHandlerMethods(handlerTypes); - logger.LogInformation("Registered {count} message handlers", s_messageHandlers.Count); - } - - public MessageHandler? GetHandler(MessageId messageId) - { - s_messageHandlers.TryGetValue(messageId, out MessageHandler? handler); - return handler; - } - - private static ImmutableDictionary GenerateHandlerMethods(IEnumerable handlerTypes) - { - var builder = ImmutableDictionary.CreateBuilder(); - - MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", [typeof(IServiceProvider)])!; - foreach (Type type in handlerTypes) - { - IEnumerable methods = type.GetMethods() - .Where(method => method.GetCustomAttribute() != null); - - foreach (MethodInfo method in methods) - { - MessageHandlerAttribute attribute = method.GetCustomAttribute()!; - - ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider)); - ParameterExpression dataParam = Expression.Parameter(typeof(ReadOnlyMemory)); - - MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam); - MethodCallExpression handlerCall = Expression.Call(getServiceCall, method, dataParam); - - Expression lambda = Expression.Lambda(handlerCall, serviceProviderParam, dataParam); - builder.Add(attribute.MessageId, lambda.Compile()); - } - } - - return builder.ToImmutable(); - } -} diff --git a/GameServer/Handlers/FormationMessageHandler.cs b/GameServer/Handlers/FormationMessageHandler.cs deleted file mode 100644 index b5f0829..0000000 --- a/GameServer/Handlers/FormationMessageHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Models; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class FormationMessageHandler : MessageHandlerBase -{ - private readonly ModelManager _modelManager; - - public FormationMessageHandler(PlayerSession session, ModelManager modelManager) : base(session) - { - _modelManager = modelManager; - } - - [MessageHandler(MessageId.GetFormationDataRequest)] - public async Task OnGetFormationDataRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.GetFormationDataResponse, new GetFormationDataResponse - { - Formations = - { - new FightFormation - { - CurRole = _modelManager.Player.CharacterId, - FormationId = 1, - IsCurrent = true, - RoleIds = { _modelManager.Player.CharacterId }, - } - }, - }); - } -} diff --git a/GameServer/Handlers/FriendMessageHandler.cs b/GameServer/Handlers/FriendMessageHandler.cs deleted file mode 100644 index 82b1402..0000000 --- a/GameServer/Handlers/FriendMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class FriendMessageHandler : MessageHandlerBase -{ - public FriendMessageHandler(PlayerSession session) : base(session) - { - // FriendMessageHandler. - } - - [MessageHandler(MessageId.FriendAllRequest)] - public async Task OnFriendAllRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.FriendAllResponse, new FriendAllResponse()); - } -} diff --git a/GameServer/Handlers/GachaMessageHandler.cs b/GameServer/Handlers/GachaMessageHandler.cs deleted file mode 100644 index 7d3a58a..0000000 --- a/GameServer/Handlers/GachaMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class GachaMessageHandler : MessageHandlerBase -{ - public GachaMessageHandler(PlayerSession session) : base(session) - { - // GachaMessageHandler. - } - - [MessageHandler(MessageId.GachaInfoRequest)] - public async Task OnGachaInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.GachaInfoResponse, new GachaInfoResponse()); - } -} diff --git a/GameServer/Handlers/InfluenceReputationMessageHandler.cs b/GameServer/Handlers/InfluenceReputationMessageHandler.cs deleted file mode 100644 index 4aeaf92..0000000 --- a/GameServer/Handlers/InfluenceReputationMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class InfluenceReputationMessageHandler : MessageHandlerBase -{ - public InfluenceReputationMessageHandler(PlayerSession session) : base(session) - { - // InfluenceReputationMessageHandler. - } - - [MessageHandler(MessageId.InfluenceInfoRequest)] - public async Task OnInfluenceInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.InfluenceInfoResponse, new InfluenceInfoResponse()); - } -} diff --git a/GameServer/Handlers/InventoryMessageHandler.cs b/GameServer/Handlers/InventoryMessageHandler.cs deleted file mode 100644 index e0a495c..0000000 --- a/GameServer/Handlers/InventoryMessageHandler.cs +++ /dev/null @@ -1,36 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class InventoryMessageHandler : MessageHandlerBase -{ - public InventoryMessageHandler(PlayerSession session) : base(session) - { - // InventoryMessageHandler. - } - - [MessageHandler(MessageId.NormalItemRequest)] - public async Task OnNormalItemRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.NormalItemResponse, new NormalItemResponse()); - } - - [MessageHandler(MessageId.WeaponItemRequest)] - public async Task OnWeaponItemRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.WeaponItemResponse, new WeaponItemResponse()); - } - - [MessageHandler(MessageId.PhantomItemRequest)] - public async Task OnPhantomItemRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.PhantomItemResponse, new PhantomItemResponse()); - } - - [MessageHandler(MessageId.ItemExchangeInfoRequest)] - public async Task OnItemExchangeInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse()); - } -} diff --git a/GameServer/Handlers/LordGymMessageHandler.cs b/GameServer/Handlers/LordGymMessageHandler.cs deleted file mode 100644 index b02896d..0000000 --- a/GameServer/Handlers/LordGymMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class LordGymMessageHandler : MessageHandlerBase -{ - public LordGymMessageHandler(PlayerSession session) : base(session) - { - // LordGymMessageHandler. - } - - [MessageHandler(MessageId.LordGymInfoRequest)] - public async Task OnLordGymInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.LordGymInfoResponse, new LordGymInfoResponse()); - } -} diff --git a/GameServer/Handlers/MessageHandlerBase.cs b/GameServer/Handlers/MessageHandlerBase.cs deleted file mode 100644 index 56bec7d..0000000 --- a/GameServer/Handlers/MessageHandlerBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using GameServer.Network; - -namespace GameServer.Handlers; -internal abstract class MessageHandlerBase -{ - protected PlayerSession Session { get; } - - public MessageHandlerBase(PlayerSession session) - { - Session = session; - } -} diff --git a/GameServer/Handlers/MessageManager.cs b/GameServer/Handlers/MessageManager.cs deleted file mode 100644 index 624a518..0000000 --- a/GameServer/Handlers/MessageManager.cs +++ /dev/null @@ -1,55 +0,0 @@ -using GameServer.Handlers.Factory; -using GameServer.Network; -using GameServer.Network.Messages; -using GameServer.Network.Packets; -using Protocol; - -namespace GameServer.Handlers; - -internal delegate Task MessageHandler(IServiceProvider serviceProvider, ReadOnlyMemory data); -internal class MessageManager -{ - private readonly MessageHandlerFactory _handlerFactory; - private readonly IServiceProvider _serviceProvider; - - public MessageManager(IServiceProvider serviceProvider, MessageHandlerFactory handlerFactory) - { - _handlerFactory = handlerFactory; - _serviceProvider = serviceProvider; - } - - public async Task ProcessMessage(MessageId messageId, ReadOnlyMemory data) - { - MessageHandler? handler = _handlerFactory.GetHandler(messageId); - if (handler != null) - { - await handler(_serviceProvider, data); - return true; - } - - return false; - } - - public static void EncodeMessage(Memory buffer, BaseMessage message) - { - buffer.Span[0] = (byte)message.Type; - message.Encode(buffer); - } - - public static BaseMessage DecodeMessage(ReadOnlyMemory buffer) - { - MessageType type = (MessageType)buffer.Span[0]; - - BaseMessage message = type switch - { - MessageType.Request => new RequestMessage(), - MessageType.Response => new ResponseMessage(), - MessageType.Push => new PushMessage(), - - _ => throw new NotSupportedException("Message type not implemented: " + type) - }; - - message.Decode(buffer); - return message; - } -} diff --git a/GameServer/Handlers/RoguelikeMessageHandler.cs b/GameServer/Handlers/RoguelikeMessageHandler.cs deleted file mode 100644 index 736ea12..0000000 --- a/GameServer/Handlers/RoguelikeMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class RoguelikeMessageHandler : MessageHandlerBase -{ - public RoguelikeMessageHandler(PlayerSession session) : base(session) - { - // RoguelikeMessageHandler. - } - - [MessageHandler(MessageId.RoguelikeSeasonDataRequest)] - public async Task OnRoguelikeSeasonDataRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.RoguelikeSeasonDataResponse, new RoguelikeSeasonDataResponse()); - } -} diff --git a/GameServer/Handlers/RoleMessageHandler.cs b/GameServer/Handlers/RoleMessageHandler.cs deleted file mode 100644 index 7259224..0000000 --- a/GameServer/Handlers/RoleMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class RoleMessageHandler : MessageHandlerBase -{ - public RoleMessageHandler(PlayerSession session) : base(session) - { - // RoleMessageHandler. - } - - [MessageHandler(MessageId.RoleFavorListRequest)] - public async Task OnRoleFavorListRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.RoleFavorListResponse, new RoleFavorListResponse()); - } -} diff --git a/GameServer/Handlers/ShopMessageHandler.cs b/GameServer/Handlers/ShopMessageHandler.cs deleted file mode 100644 index cefde20..0000000 --- a/GameServer/Handlers/ShopMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class ShopMessageHandler : MessageHandlerBase -{ - public ShopMessageHandler(PlayerSession session) : base(session) - { - // ShopMessageHandler. - } - - [MessageHandler(MessageId.PayShopInfoRequest)] - public async Task OnPayShopInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.PayShopInfoResponse, new PayShopInfoResponse()); - } -} diff --git a/GameServer/Handlers/TowerMessageHandler.cs b/GameServer/Handlers/TowerMessageHandler.cs deleted file mode 100644 index 49674c1..0000000 --- a/GameServer/Handlers/TowerMessageHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class TowerMessageHandler : MessageHandlerBase -{ - public TowerMessageHandler(PlayerSession session) : base(session) - { - // TowerMessageHandler. - } - - [MessageHandler(MessageId.TowerChallengeRequest)] - public async Task OnTowerChallengeRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.TowerChallengeResponse, new TowerChallengeResponse()); - } - - [MessageHandler(MessageId.CycleTowerChallengeRequest)] - public async Task OnCycleTowerChallengeRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.CycleTowerChallengeResponse, new CycleTowerChallengeResponse()); - } -} diff --git a/GameServer/Handlers/WorldMapMessageHandler.cs b/GameServer/Handlers/WorldMapMessageHandler.cs deleted file mode 100644 index 7bfdcc2..0000000 --- a/GameServer/Handlers/WorldMapMessageHandler.cs +++ /dev/null @@ -1,18 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class WorldMapMessageHandler : MessageHandlerBase -{ - public WorldMapMessageHandler(PlayerSession session) : base(session) - { - // WorldMapMessageHandler. - } - - [MessageHandler(MessageId.MapTraceInfoRequest)] - public async Task OnMapTraceInfoRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.MapTraceInfoResponse, new MapTraceInfoResponse()); - } -} diff --git a/GameServer/Handlers/WorldMessageHandler.cs b/GameServer/Handlers/WorldMessageHandler.cs deleted file mode 100644 index d0db7d0..0000000 --- a/GameServer/Handlers/WorldMessageHandler.cs +++ /dev/null @@ -1,48 +0,0 @@ -using GameServer.Handlers.Attributes; -using GameServer.Network; -using Protocol; - -namespace GameServer.Handlers; -internal class WorldMessageHandler : MessageHandlerBase -{ - public WorldMessageHandler(PlayerSession session) : base(session) - { - // WorldMessageHandler. - } - - [MessageHandler(MessageId.EntityActiveRequest)] - public async Task OnEntityActiveRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.EntityActiveResponse, new EntityActiveResponse()); - } - - [MessageHandler(MessageId.EntityOnLandedRequest)] - public async Task OnEntityOnLandedRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.EntityOnLandedResponse, new EntityOnLandedResponse()); - } - - [MessageHandler(MessageId.PlayerMotionRequest)] - public async Task OnPlayerMotionRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.PlayerMotionResponse, new PlayerMotionResponse()); - } - - [MessageHandler(MessageId.EntityLoadCompleteRequest)] - public async Task OnEntityLoadCompleteRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.EntityLoadCompleteResponse, new EntityLoadCompleteResponse()); - } - - [MessageHandler(MessageId.SceneLoadingFinishRequest)] - public async Task OnSceneLoadingFinishRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse()); - } - - [MessageHandler(MessageId.UpdateSceneDateRequest)] - public async Task OnUpdateSceneDateRequest(ReadOnlyMemory _) - { - await Session.Rpc.ReturnAsync(MessageId.UpdateSceneDateResponse, new UpdateSceneDateResponse()); - } -} diff --git a/GameServer/Models/ModelManager.cs b/GameServer/Models/ModelManager.cs index 2946d8f..ae87dd1 100644 --- a/GameServer/Models/ModelManager.cs +++ b/GameServer/Models/ModelManager.cs @@ -1,8 +1,12 @@ -namespace GameServer.Models; +using GameServer.Controllers.Attributes; +using GameServer.Controllers.Event; + +namespace GameServer.Models; internal class ModelManager { private PlayerModel? _playerModel; + [GameEvent(GameEventType.Login)] public void OnLogin() { _playerModel = PlayerModel.CreateDefaultPlayer(); diff --git a/GameServer/Network/IConnection.cs b/GameServer/Network/IConnection.cs index 15e8ac0..6754aa2 100644 --- a/GameServer/Network/IConnection.cs +++ b/GameServer/Network/IConnection.cs @@ -1,4 +1,4 @@ -using GameServer.Network.Packets; +using GameServer.Network.Messages; namespace GameServer.Network; internal interface IConnection : ISessionActionListener diff --git a/GameServer/Network/ISessionActionListener.cs b/GameServer/Network/ISessionActionListener.cs index 082d32c..9653cac 100644 --- a/GameServer/Network/ISessionActionListener.cs +++ b/GameServer/Network/ISessionActionListener.cs @@ -1,4 +1,4 @@ -using GameServer.Network.Packets; +using GameServer.Network.Messages; namespace GameServer.Network; internal interface ISessionActionListener diff --git a/GameServer/Network/Kcp/KcpConnection.cs b/GameServer/Network/Kcp/KcpConnection.cs index f579cc0..ebba63b 100644 --- a/GameServer/Network/Kcp/KcpConnection.cs +++ b/GameServer/Network/Kcp/KcpConnection.cs @@ -1,8 +1,7 @@ using GameServer.Extensions; -using GameServer.Handlers; -using GameServer.Network.Packets; using System.Buffers; using KcpSharp; +using GameServer.Network.Messages; namespace GameServer.Network.Kcp; internal class KcpConnection : IConnection diff --git a/GameServer/Network/Messages/BaseMessage.cs b/GameServer/Network/Messages/BaseMessage.cs index 768b337..b6a52b3 100644 --- a/GameServer/Network/Messages/BaseMessage.cs +++ b/GameServer/Network/Messages/BaseMessage.cs @@ -1,6 +1,6 @@ using System.Buffers.Binary; -namespace GameServer.Network.Packets; +namespace GameServer.Network.Messages; internal abstract class BaseMessage { public const int LengthFieldSize = 3; diff --git a/GameServer/Network/Messages/MessageManager.cs b/GameServer/Network/Messages/MessageManager.cs new file mode 100644 index 0000000..049c708 --- /dev/null +++ b/GameServer/Network/Messages/MessageManager.cs @@ -0,0 +1,59 @@ +using GameServer.Controllers.Factory; +using Protocol; + +namespace GameServer.Network.Messages; + +internal delegate Task PushHandler(IServiceProvider serviceProvider, ReadOnlySpan data); +internal delegate Task RpcHandler(IServiceProvider serviceProvider, ReadOnlySpan data); +internal class MessageManager +{ + private readonly EventHandlerFactory _handlerFactory; + private readonly IServiceProvider _serviceProvider; + + public MessageManager(IServiceProvider serviceProvider, EventHandlerFactory handlerFactory) + { + _handlerFactory = handlerFactory; + _serviceProvider = serviceProvider; + } + + public async Task ExecuteRpc(MessageId messageId, ReadOnlyMemory data) + { + RpcHandler? handler = _handlerFactory.GetRpcHandler(messageId); + if (handler != null) + return await handler(_serviceProvider, data.Span); + + return null; + } + + public async Task HandlePush(MessageId messageId, ReadOnlyMemory data) + { + PushHandler? handler = _handlerFactory.GetPushHandler(messageId); + if (handler == null) return false; + + await handler(_serviceProvider, data.Span); + return true; + } + + public static void EncodeMessage(Memory buffer, BaseMessage message) + { + buffer.Span[0] = (byte)message.Type; + message.Encode(buffer); + } + + public static BaseMessage DecodeMessage(ReadOnlyMemory buffer) + { + MessageType type = (MessageType)buffer.Span[0]; + + BaseMessage message = type switch + { + MessageType.Request => new RequestMessage(), + MessageType.Response => new ResponseMessage(), + MessageType.Push => new PushMessage(), + + _ => throw new NotSupportedException("Message type not implemented: " + type) + }; + + message.Decode(buffer); + return message; + } +} diff --git a/GameServer/Network/Messages/PushMessage.cs b/GameServer/Network/Messages/PushMessage.cs index a32bf25..99f6520 100644 --- a/GameServer/Network/Messages/PushMessage.cs +++ b/GameServer/Network/Messages/PushMessage.cs @@ -1,5 +1,4 @@ using System.Buffers.Binary; -using GameServer.Network.Packets; using Protocol; namespace GameServer.Network.Messages; diff --git a/GameServer/Network/Messages/RequestMessage.cs b/GameServer/Network/Messages/RequestMessage.cs index 738a43d..6c3fb5c 100644 --- a/GameServer/Network/Messages/RequestMessage.cs +++ b/GameServer/Network/Messages/RequestMessage.cs @@ -1,5 +1,4 @@ using System.Buffers.Binary; -using GameServer.Network.Packets; using Protocol; namespace GameServer.Network.Messages; diff --git a/GameServer/Network/Messages/ResponseMessage.cs b/GameServer/Network/Messages/ResponseMessage.cs index 4165925..913847a 100644 --- a/GameServer/Network/Messages/ResponseMessage.cs +++ b/GameServer/Network/Messages/ResponseMessage.cs @@ -1,5 +1,4 @@ using System.Buffers.Binary; -using GameServer.Network.Packets; using Protocol; namespace GameServer.Network.Messages; diff --git a/GameServer/Network/PlayerSession.cs b/GameServer/Network/PlayerSession.cs index 04ddda7..c998bc4 100644 --- a/GameServer/Network/PlayerSession.cs +++ b/GameServer/Network/PlayerSession.cs @@ -1,6 +1,4 @@ -using GameServer.Handlers; -using GameServer.Network.Messages; -using GameServer.Network.Packets; +using GameServer.Network.Messages; using GameServer.Network.Rpc; using Google.Protobuf; using Microsoft.Extensions.Logging; @@ -27,17 +25,17 @@ internal class PlayerSession switch (message) { case RequestMessage request: - await Rpc.HandleRpcRequest(request); + await Rpc.Execute(request); break; case PushMessage push: - if (!await _messageManager.ProcessMessage(push.MessageId, push.Payload)) + if (!await _messageManager.HandlePush(push.MessageId, push.Payload)) _logger.LogWarning("Push message ({id}) was not handled", push.MessageId); break; } } - public Task PushMessage(MessageId id, TProtoBuf data) where TProtoBuf : IMessage + public Task Push(MessageId id, TProtoBuf data) where TProtoBuf : IMessage { return Listener?.OnServerMessageAvailable(new PushMessage { diff --git a/GameServer/Network/Rpc/RpcManager.cs b/GameServer/Network/Rpc/RpcManager.cs index 6e64d3e..957c80b 100644 --- a/GameServer/Network/Rpc/RpcManager.cs +++ b/GameServer/Network/Rpc/RpcManager.cs @@ -1,8 +1,5 @@ -using GameServer.Handlers; -using GameServer.Network.Messages; -using Google.Protobuf; +using GameServer.Network.Messages; using Microsoft.Extensions.Logging; -using Protocol; namespace GameServer.Network.Rpc; internal class RpcManager @@ -10,7 +7,6 @@ internal class RpcManager private readonly IRpcEndPoint _endPoint; private readonly ILogger _logger; private readonly MessageManager _messageManager; - private ushort _curId; public RpcManager(MessageManager messageManager, IRpcEndPoint endPoint, ILogger logger) { @@ -19,32 +15,18 @@ internal class RpcManager _messageManager = messageManager; } - public async Task HandleRpcRequest(RequestMessage request) + public async Task Execute(RequestMessage request) { - _logger.LogInformation("Processing Rpc, message: {msg_id}, id: {rpc_id}", request.MessageId, request.RpcID); - - _curId = request.RpcID; - _ = await _messageManager.ProcessMessage(request.MessageId, request.Payload); - - if (_curId != 0) + ResponseMessage? response = await _messageManager.ExecuteRpc(request.MessageId, request.Payload); + if (response == null) { _logger.LogWarning("Rpc was not handled properly (message: {msg_id}, id: {rpc_id})", request.MessageId, request.RpcID); - _curId = 0; + return; } - } - public async Task ReturnAsync(MessageId messageId, TProtoBuf data) where TProtoBuf : IMessage - { - if (_curId == 0) throw new InvalidOperationException("RpcManager::ReturnAsync called - no rpc being processed!"); + response.RpcID = request.RpcID; + await _endPoint.SendRpcResult(response); - await _endPoint.SendRpcResult(new ResponseMessage - { - MessageId = messageId, - RpcID = _curId, - Payload = data.ToByteArray() - }); - - _logger.LogInformation("Rpc with id {rpc_id} was handled, return message: {msg_id}", _curId, messageId); - _curId = 0; + _logger.LogInformation("Rpc with id {rpc_id} was handled, return message: {msg_id}", request.RpcID, response.MessageId); } } diff --git a/GameServer/Network/SessionManager.cs b/GameServer/Network/SessionManager.cs index 945db70..e65555d 100644 --- a/GameServer/Network/SessionManager.cs +++ b/GameServer/Network/SessionManager.cs @@ -1,5 +1,5 @@ using GameServer.Network.Kcp; -using GameServer.Network.Packets; +using GameServer.Network.Messages; using KcpSharp; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; diff --git a/GameServer/Program.cs b/GameServer/Program.cs index e86211a..1ff65ed 100644 --- a/GameServer/Program.cs +++ b/GameServer/Program.cs @@ -1,9 +1,11 @@ -using GameServer.Extensions; -using GameServer.Handlers; -using GameServer.Handlers.Factory; +using GameServer.Controllers.Event; +using GameServer.Controllers.Factory; +using GameServer.Controllers.Manager; +using GameServer.Extensions; using GameServer.Models; using GameServer.Network; using GameServer.Network.Kcp; +using GameServer.Network.Messages; using GameServer.Network.Rpc; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -18,12 +20,12 @@ internal static class Program HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); builder.Logging.AddConsole(); - builder.Services.AddHandlers() + builder.Services.AddControllers() .AddSingleton().AddScoped() - .AddScoped().AddSingleton() + .AddScoped().AddSingleton() .AddScoped().AddScoped() .AddSingleton() - .AddScoped() + .AddScoped().AddScoped().AddScoped() .AddHostedService(); await builder.Build().RunAsync(); diff --git a/GameServer/WWGameServer.cs b/GameServer/WWGameServer.cs index d6f1ed8..3509093 100644 --- a/GameServer/WWGameServer.cs +++ b/GameServer/WWGameServer.cs @@ -1,4 +1,4 @@ -using GameServer.Handlers.Factory; +using GameServer.Controllers.Factory; using GameServer.Network.Kcp; using Microsoft.Extensions.Hosting; @@ -7,7 +7,7 @@ internal class WWGameServer : IHostedService { private readonly KcpGateway _gateway; - public WWGameServer(KcpGateway gateway, MessageHandlerFactory messageHandlerFactory) + public WWGameServer(KcpGateway gateway, EventHandlerFactory messageHandlerFactory) { _ = messageHandlerFactory; _gateway = gateway; @@ -20,8 +20,5 @@ internal class WWGameServer : IHostedService return Task.CompletedTask; } - public Task StopAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; }