Handling systems completely reworked
This commit is contained in:
parent
35ebc910ed
commit
77956be694
61 changed files with 965 additions and 671 deletions
|
@ -1,4 +1,98 @@
|
||||||
[*.cs]
|
[*.cs]
|
||||||
|
|
||||||
# IDE0290: Use primary constructor
|
# 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
|
||||||
|
|
16
GameServer/Controllers/AchievementController.cs
Normal file
16
GameServer/Controllers/AchievementController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
14
GameServer/Controllers/Attributes/GameEventAttribute.cs
Normal file
14
GameServer/Controllers/Attributes/GameEventAttribute.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
14
GameServer/Controllers/Attributes/NetEventAttribute.cs
Normal file
14
GameServer/Controllers/Attributes/NetEventAttribute.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
21
GameServer/Controllers/Controller.cs
Normal file
21
GameServer/Controllers/Controller.cs
Normal file
|
@ -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<TProtoBuf>(MessageId messageId, TProtoBuf protoBuf) where TProtoBuf : IMessage<TProtoBuf> => new()
|
||||||
|
{
|
||||||
|
MessageId = messageId,
|
||||||
|
Payload = protoBuf.ToByteArray()
|
||||||
|
};
|
||||||
|
}
|
19
GameServer/Controllers/DailyActivityController.cs
Normal file
19
GameServer/Controllers/DailyActivityController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
58
GameServer/Controllers/Event/EventSystem.cs
Normal file
58
GameServer/Controllers/Event/EventSystem.cs
Normal file
|
@ -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<GameEventType, Func<ModelManager, Task>> s_modelManagerEventHandlers = RegisterModelManagerEvents();
|
||||||
|
|
||||||
|
private readonly ModelManager _modelManager;
|
||||||
|
private readonly ControllerManager _controllerManager;
|
||||||
|
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public EventSystem(ModelManager modelManager, ControllerManager controllerManager, ILogger<EventSystem> 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<GameEventType, Func<ModelManager, Task>> RegisterModelManagerEvents()
|
||||||
|
{
|
||||||
|
var builder = ImmutableDictionary.CreateBuilder<GameEventType, Func<ModelManager, Task>>();
|
||||||
|
|
||||||
|
foreach (MethodInfo method in typeof(ModelManager).GetMethods())
|
||||||
|
{
|
||||||
|
GameEventAttribute? attribute = method.GetCustomAttribute<GameEventAttribute>();
|
||||||
|
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<Func<ModelManager, Task>> lambda = Expression.Lambda<Func<ModelManager, Task>>(exp, modelManagerParam);
|
||||||
|
|
||||||
|
builder.Add(attribute.Type, lambda.Compile());
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToImmutable();
|
||||||
|
}
|
||||||
|
}
|
6
GameServer/Controllers/Event/GameEventType.cs
Normal file
6
GameServer/Controllers/Event/GameEventType.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
namespace GameServer.Controllers.Event;
|
||||||
|
internal enum GameEventType
|
||||||
|
{
|
||||||
|
Login = 1,
|
||||||
|
EnterGame
|
||||||
|
}
|
16
GameServer/Controllers/ExchangeRewardController.cs
Normal file
16
GameServer/Controllers/ExchangeRewardController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
178
GameServer/Controllers/Factory/EventHandlerFactory.cs
Normal file
178
GameServer/Controllers/Factory/EventHandlerFactory.cs
Normal file
|
@ -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<MessageId, RpcHandler> s_rpcHandlers;
|
||||||
|
private readonly ImmutableDictionary<MessageId, PushHandler> s_pushHandlers;
|
||||||
|
private readonly ImmutableDictionary<GameEventType, List<GameEventHandler>> s_eventHandlers;
|
||||||
|
|
||||||
|
public EventHandlerFactory(ILogger<EventHandlerFactory> logger)
|
||||||
|
{
|
||||||
|
IEnumerable<Type> 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<GameEventHandler> GetEventHandlers(GameEventType eventType)
|
||||||
|
{
|
||||||
|
if (!s_eventHandlers.TryGetValue(eventType, out List<GameEventHandler>? handlers))
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableDictionary<GameEventType, List<GameEventHandler>> RegisterEventHandlers(IEnumerable<Type> controllerTypes)
|
||||||
|
{
|
||||||
|
var builder = ImmutableDictionary.CreateBuilder<GameEventType, List<GameEventHandler>>();
|
||||||
|
|
||||||
|
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<MethodInfo> methods = type.GetMethods()
|
||||||
|
.Where(method => method.GetCustomAttribute<GameEventAttribute>() != null
|
||||||
|
&& (method.ReturnType == typeof(Task) || method.ReturnType == typeof(void)));
|
||||||
|
|
||||||
|
foreach (MethodInfo method in methods)
|
||||||
|
{
|
||||||
|
GameEventAttribute attribute = method.GetCustomAttribute<GameEventAttribute>()!;
|
||||||
|
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<GameEventHandler> lambda = Expression.Lambda<GameEventHandler>(handlerCall, serviceProviderParam);
|
||||||
|
|
||||||
|
if (!builder.TryGetKey(attribute.Type, out _))
|
||||||
|
builder.Add(attribute.Type, []);
|
||||||
|
|
||||||
|
builder[attribute.Type].Add(lambda.Compile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableDictionary<MessageId, RpcHandler> RegisterRpcHandlers(IEnumerable<Type> controllerTypes)
|
||||||
|
{
|
||||||
|
var builder = ImmutableDictionary.CreateBuilder<MessageId, RpcHandler>();
|
||||||
|
|
||||||
|
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<MethodInfo> methods = type.GetMethods()
|
||||||
|
.Where(method => method.GetCustomAttribute<NetEventAttribute>() != null
|
||||||
|
&& (method.ReturnType == typeof(Task<ResponseMessage>) || method.ReturnType == typeof(ResponseMessage)));
|
||||||
|
|
||||||
|
foreach (MethodInfo method in methods)
|
||||||
|
{
|
||||||
|
NetEventAttribute attribute = method.GetCustomAttribute<NetEventAttribute>()!;
|
||||||
|
|
||||||
|
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
|
||||||
|
ParameterExpression dataParam = Expression.Parameter(typeof(ReadOnlySpan<byte>));
|
||||||
|
|
||||||
|
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<RpcHandler> lambda = Expression.Lambda<RpcHandler>(handlerCall, serviceProviderParam, dataParam);
|
||||||
|
|
||||||
|
builder.Add(attribute.MessageId, lambda.Compile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableDictionary<MessageId, PushHandler> RegisterPushHandlers(IEnumerable<Type> controllerTypes)
|
||||||
|
{
|
||||||
|
var builder = ImmutableDictionary.CreateBuilder<MessageId, PushHandler>();
|
||||||
|
|
||||||
|
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<MethodInfo> methods = type.GetMethods()
|
||||||
|
.Where(method => method.GetCustomAttribute<NetEventAttribute>() != null
|
||||||
|
&& (method.ReturnType == typeof(Task) || method.ReturnType == typeof(void)));
|
||||||
|
|
||||||
|
foreach (MethodInfo method in methods)
|
||||||
|
{
|
||||||
|
NetEventAttribute attribute = method.GetCustomAttribute<NetEventAttribute>()!;
|
||||||
|
|
||||||
|
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
|
||||||
|
ParameterExpression dataParam = Expression.Parameter(typeof(ReadOnlySpan<byte>));
|
||||||
|
|
||||||
|
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<PushHandler> lambda = Expression.Lambda<PushHandler>(handlerCall, serviceProviderParam, dataParam);
|
||||||
|
|
||||||
|
builder.Add(attribute.MessageId, lambda.Compile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Expression> FetchArgumentsForMethod(MethodInfo method, Expression serviceProviderParam, MethodInfo getServiceMethod, Expression? dataParam = null)
|
||||||
|
{
|
||||||
|
List<Expression> 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<byte>)])!;
|
||||||
|
|
||||||
|
arguments.Add(Expression.Call(Expression.Constant(parser.GetValue(null)), parseFrom, dataParam));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
arguments.Add(Expression.Call(getServiceMethod.MakeGenericMethod(param.ParameterType), serviceProviderParam));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
}
|
31
GameServer/Controllers/FormationController.cs
Normal file
31
GameServer/Controllers/FormationController.cs
Normal file
|
@ -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 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
16
GameServer/Controllers/FriendSystemController.cs
Normal file
16
GameServer/Controllers/FriendSystemController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
32
GameServer/Controllers/FunctionalController.cs
Normal file
32
GameServer/Controllers/FunctionalController.cs
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
16
GameServer/Controllers/GachaController.cs
Normal file
16
GameServer/Controllers/GachaController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
16
GameServer/Controllers/InfluenceReputationController.cs
Normal file
16
GameServer/Controllers/InfluenceReputationController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
25
GameServer/Controllers/InventoryController.cs
Normal file
25
GameServer/Controllers/InventoryController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
42
GameServer/Controllers/LoginController.cs
Normal file
42
GameServer/Controllers/LoginController.cs
Normal file
|
@ -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<ResponseMessage> 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<ResponseMessage> OnEnterGameRequest(EnterGameRequest request, ILogger<LoginController> 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());
|
||||||
|
}
|
16
GameServer/Controllers/LordGymController.cs
Normal file
16
GameServer/Controllers/LordGymController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
32
GameServer/Controllers/Manager/ControllerManager.cs
Normal file
32
GameServer/Controllers/Manager/ControllerManager.cs
Normal file
|
@ -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<GameEventHandler> handlers = _eventHandlerFactory.GetEventHandlers(eventType);
|
||||||
|
|
||||||
|
foreach (GameEventHandler handler in handlers)
|
||||||
|
{
|
||||||
|
await handler(_serviceProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TController Get<TController>() where TController : Controller
|
||||||
|
{
|
||||||
|
return _serviceProvider.GetRequiredService<TController>();
|
||||||
|
}
|
||||||
|
}
|
50
GameServer/Controllers/PlayerInfoController.cs
Normal file
50
GameServer/Controllers/PlayerInfoController.cs
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
16
GameServer/Controllers/RoguelikeController.cs
Normal file
16
GameServer/Controllers/RoguelikeController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
36
GameServer/Controllers/RoleController.cs
Normal file
36
GameServer/Controllers/RoleController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
16
GameServer/Controllers/ShopController.cs
Normal file
16
GameServer/Controllers/ShopController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
19
GameServer/Controllers/TowerController.cs
Normal file
19
GameServer/Controllers/TowerController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
|
@ -1,17 +1,18 @@
|
||||||
using GameServer.Handlers.Attributes;
|
using GameServer.Controllers.Attributes;
|
||||||
using GameServer.Network;
|
using GameServer.Network;
|
||||||
|
using GameServer.Network.Messages;
|
||||||
using Protocol;
|
using Protocol;
|
||||||
|
|
||||||
namespace GameServer.Handlers;
|
namespace GameServer.Controllers;
|
||||||
internal class TutorialMessageHandler : MessageHandlerBase
|
internal class TutorialController : Controller
|
||||||
{
|
{
|
||||||
public TutorialMessageHandler(PlayerSession session) : base(session)
|
public TutorialController(PlayerSession session) : base(session)
|
||||||
{
|
{
|
||||||
// TutorialMessageHandler.
|
// TutorialMessageHandler.
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessageHandler(MessageId.TutorialInfoRequest)]
|
[NetEvent(MessageId.TutorialInfoRequest)]
|
||||||
public async Task OnTutorialInfoRequest(ReadOnlyMemory<byte> data)
|
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];
|
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();
|
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)]
|
[NetEvent(MessageId.GetDetectionLabelInfoRequest)]
|
||||||
public async Task OnGetDetectionLabelInfoRequest(ReadOnlyMemory<byte> _)
|
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[] 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];
|
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.UnlockedGuideIds.AddRange(guides);
|
||||||
rsp.UnlockLabelInfo.UnlockedDetectionTextIds.AddRange(detectionTexts);
|
rsp.UnlockLabelInfo.UnlockedDetectionTextIds.AddRange(detectionTexts);
|
||||||
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.GetDetectionLabelInfoResponse, rsp);
|
return Response(MessageId.GetDetectionLabelInfoResponse, rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessageHandler(MessageId.GuideInfoRequest)]
|
[NetEvent(MessageId.GuideInfoRequest)]
|
||||||
public async Task OnGuideInfoRequest(ReadOnlyMemory<byte> _)
|
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 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
|
@ -1,107 +1,24 @@
|
||||||
using GameServer.Handlers.Attributes;
|
using GameServer.Controllers.Attributes;
|
||||||
|
using GameServer.Controllers.Event;
|
||||||
using GameServer.Models;
|
using GameServer.Models;
|
||||||
using GameServer.Network;
|
using GameServer.Network;
|
||||||
|
using GameServer.Network.Messages;
|
||||||
using Protocol;
|
using Protocol;
|
||||||
|
|
||||||
namespace GameServer.Handlers;
|
namespace GameServer.Controllers;
|
||||||
internal class AuthMessageHandler : MessageHandlerBase
|
internal class WorldController : Controller
|
||||||
{
|
{
|
||||||
private readonly ModelManager _modelManager;
|
public WorldController(PlayerSession session) : base(session)
|
||||||
|
|
||||||
public AuthMessageHandler(PlayerSession session, ModelManager modelManager) : base(session)
|
|
||||||
{
|
{
|
||||||
_modelManager = modelManager;
|
// WorldMessageHandler.
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessageHandler(MessageId.LoginRequest)]
|
[GameEvent(GameEventType.EnterGame)]
|
||||||
public async Task OnLoginRequest(ReadOnlyMemory<byte> _)
|
public async Task OnEnterGame(ModelManager modelManager)
|
||||||
{
|
{
|
||||||
_modelManager.OnLogin();
|
PlayerModel player = modelManager.Player;
|
||||||
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.LoginResponse, new LoginResponse
|
await Session.Push(MessageId.JoinSceneNotify, new JoinSceneNotify
|
||||||
{
|
|
||||||
Code = 0,
|
|
||||||
Platform = "PC",
|
|
||||||
Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.EnterGameRequest)]
|
|
||||||
public async Task OnEnterGameRequest(ReadOnlyMemory<byte> 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
|
|
||||||
{
|
{
|
||||||
MaxEntityId = 2,
|
MaxEntityId = 2,
|
||||||
TransitionOption = new TransitionOptionPb
|
TransitionOption = new TransitionOptionPb
|
||||||
|
@ -110,7 +27,7 @@ internal class AuthMessageHandler : MessageHandlerBase
|
||||||
},
|
},
|
||||||
SceneInfo = new SceneInformation
|
SceneInfo = new SceneInformation
|
||||||
{
|
{
|
||||||
OwnerId = 1337,
|
OwnerId = player.Id,
|
||||||
Mode = (int)SceneMode.Single,
|
Mode = (int)SceneMode.Single,
|
||||||
InstanceId = 8,
|
InstanceId = 8,
|
||||||
AoiData = new PlayerSceneAoiData
|
AoiData = new PlayerSceneAoiData
|
||||||
|
@ -122,9 +39,9 @@ internal class AuthMessageHandler : MessageHandlerBase
|
||||||
{
|
{
|
||||||
EntityState = (int)EntityState.Born,
|
EntityState = (int)EntityState.Born,
|
||||||
EntityType = (int)EEntityType.Player,
|
EntityType = (int)EEntityType.Player,
|
||||||
PlayerId = _modelManager.Player.Id,
|
PlayerId = player.Id,
|
||||||
LivingStatus = (int)LivingStatus.Alive,
|
LivingStatus = (int)LivingStatus.Alive,
|
||||||
ConfigId = _modelManager.Player.CharacterId,
|
ConfigId = player.CharacterId,
|
||||||
ConfigType = (int)EntityConfigType.Character,
|
ConfigType = (int)EntityConfigType.Character,
|
||||||
Id = 1,
|
Id = 1,
|
||||||
IsVisible = true,
|
IsVisible = true,
|
||||||
|
@ -248,7 +165,7 @@ internal class AuthMessageHandler : MessageHandlerBase
|
||||||
{
|
{
|
||||||
new ScenePlayerInformation
|
new ScenePlayerInformation
|
||||||
{
|
{
|
||||||
PlayerId = _modelManager.Player.Id,
|
PlayerId = player.Id,
|
||||||
Level = 1,
|
Level = 1,
|
||||||
IsOffline = false,
|
IsOffline = false,
|
||||||
Location = new()
|
Location = new()
|
||||||
|
@ -265,25 +182,33 @@ internal class AuthMessageHandler : MessageHandlerBase
|
||||||
CurHp = 1000,
|
CurHp = 1000,
|
||||||
MaxHp = 1000,
|
MaxHp = 1000,
|
||||||
IsControl = true,
|
IsControl = true,
|
||||||
RoleId = _modelManager.Player.CharacterId,
|
RoleId = player.CharacterId,
|
||||||
RoleLevel = 1,
|
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)]
|
[NetEvent(MessageId.EntityActiveRequest)]
|
||||||
public async Task OnHeartbeatRequest(ReadOnlyMemory<byte> _)
|
public ResponseMessage OnEntityActiveRequest() => Response(MessageId.EntityActiveResponse, new EntityActiveResponse());
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.HeartbeatResponse, new HeartbeatResponse());
|
[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());
|
||||||
}
|
}
|
16
GameServer/Controllers/WorldMapController.cs
Normal file
16
GameServer/Controllers/WorldMapController.cs
Normal file
|
@ -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());
|
||||||
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using GameServer.Handlers;
|
using GameServer.Controllers;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace GameServer.Extensions;
|
namespace GameServer.Extensions;
|
||||||
internal static class ServiceCollectionExtensions
|
internal static class ServiceCollectionExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddHandlers(this IServiceCollection services)
|
public static IServiceCollection AddControllers(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
IEnumerable<Type> handlerTypes = Assembly.GetExecutingAssembly().GetTypes()
|
IEnumerable<Type> controllerTypes = Assembly.GetExecutingAssembly().GetTypes()
|
||||||
.Where(t => t.IsAssignableTo(typeof(MessageHandlerBase)) && !t.IsAbstract);
|
.Where(t => t.IsAssignableTo(typeof(Controller)) && !t.IsAbstract);
|
||||||
|
|
||||||
foreach (Type type in handlerTypes)
|
foreach (Type type in controllerTypes)
|
||||||
{
|
{
|
||||||
services.AddScoped(type);
|
services.AddScoped(type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.AchievementInfoResponse, new AchievementInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.ActivityResponse, new ActivityResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.LivenessRequest)]
|
|
||||||
public async Task OnLivenessRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.LivenessResponse, new LivenessResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.ExchangeRewardInfoResponse, new ExchangeRewardInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<MessageId, MessageHandler> s_messageHandlers;
|
|
||||||
|
|
||||||
public MessageHandlerFactory(ILogger<MessageHandlerFactory> logger)
|
|
||||||
{
|
|
||||||
IEnumerable<Type> 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<MessageId, MessageHandler> GenerateHandlerMethods(IEnumerable<Type> handlerTypes)
|
|
||||||
{
|
|
||||||
var builder = ImmutableDictionary.CreateBuilder<MessageId, MessageHandler>();
|
|
||||||
|
|
||||||
MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", [typeof(IServiceProvider)])!;
|
|
||||||
foreach (Type type in handlerTypes)
|
|
||||||
{
|
|
||||||
IEnumerable<MethodInfo> methods = type.GetMethods()
|
|
||||||
.Where(method => method.GetCustomAttribute<MessageHandlerAttribute>() != null);
|
|
||||||
|
|
||||||
foreach (MethodInfo method in methods)
|
|
||||||
{
|
|
||||||
MessageHandlerAttribute attribute = method.GetCustomAttribute<MessageHandlerAttribute>()!;
|
|
||||||
|
|
||||||
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
|
|
||||||
ParameterExpression dataParam = Expression.Parameter(typeof(ReadOnlyMemory<byte>));
|
|
||||||
|
|
||||||
MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam);
|
|
||||||
MethodCallExpression handlerCall = Expression.Call(getServiceCall, method, dataParam);
|
|
||||||
|
|
||||||
Expression<MessageHandler> lambda = Expression.Lambda<MessageHandler>(handlerCall, serviceProviderParam, dataParam);
|
|
||||||
builder.Add(attribute.MessageId, lambda.Compile());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.ToImmutable();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.GetFormationDataResponse, new GetFormationDataResponse
|
|
||||||
{
|
|
||||||
Formations =
|
|
||||||
{
|
|
||||||
new FightFormation
|
|
||||||
{
|
|
||||||
CurRole = _modelManager.Player.CharacterId,
|
|
||||||
FormationId = 1,
|
|
||||||
IsCurrent = true,
|
|
||||||
RoleIds = { _modelManager.Player.CharacterId },
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.FriendAllResponse, new FriendAllResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.GachaInfoResponse, new GachaInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.InfluenceInfoResponse, new InfluenceInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.NormalItemResponse, new NormalItemResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.WeaponItemRequest)]
|
|
||||||
public async Task OnWeaponItemRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.WeaponItemResponse, new WeaponItemResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.PhantomItemRequest)]
|
|
||||||
public async Task OnPhantomItemRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.PhantomItemResponse, new PhantomItemResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.ItemExchangeInfoRequest)]
|
|
||||||
public async Task OnItemExchangeInfoRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.ItemExchangeInfoResponse, new ItemExchangeInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.LordGymInfoResponse, new LordGymInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
using GameServer.Network;
|
|
||||||
|
|
||||||
namespace GameServer.Handlers;
|
|
||||||
internal abstract class MessageHandlerBase
|
|
||||||
{
|
|
||||||
protected PlayerSession Session { get; }
|
|
||||||
|
|
||||||
public MessageHandlerBase(PlayerSession session)
|
|
||||||
{
|
|
||||||
Session = session;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> 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<bool> ProcessMessage(MessageId messageId, ReadOnlyMemory<byte> data)
|
|
||||||
{
|
|
||||||
MessageHandler? handler = _handlerFactory.GetHandler(messageId);
|
|
||||||
if (handler != null)
|
|
||||||
{
|
|
||||||
await handler(_serviceProvider, data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void EncodeMessage(Memory<byte> buffer, BaseMessage message)
|
|
||||||
{
|
|
||||||
buffer.Span[0] = (byte)message.Type;
|
|
||||||
message.Encode(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BaseMessage DecodeMessage(ReadOnlyMemory<byte> 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.RoguelikeSeasonDataResponse, new RoguelikeSeasonDataResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.RoleFavorListResponse, new RoleFavorListResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.PayShopInfoResponse, new PayShopInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.TowerChallengeResponse, new TowerChallengeResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.CycleTowerChallengeRequest)]
|
|
||||||
public async Task OnCycleTowerChallengeRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.CycleTowerChallengeResponse, new CycleTowerChallengeResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.MapTraceInfoResponse, new MapTraceInfoResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.EntityActiveResponse, new EntityActiveResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.EntityOnLandedRequest)]
|
|
||||||
public async Task OnEntityOnLandedRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.EntityOnLandedResponse, new EntityOnLandedResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.PlayerMotionRequest)]
|
|
||||||
public async Task OnPlayerMotionRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.PlayerMotionResponse, new PlayerMotionResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.EntityLoadCompleteRequest)]
|
|
||||||
public async Task OnEntityLoadCompleteRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.EntityLoadCompleteResponse, new EntityLoadCompleteResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.SceneLoadingFinishRequest)]
|
|
||||||
public async Task OnSceneLoadingFinishRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.SceneLoadingFinishResponse, new SceneLoadingFinishResponse());
|
|
||||||
}
|
|
||||||
|
|
||||||
[MessageHandler(MessageId.UpdateSceneDateRequest)]
|
|
||||||
public async Task OnUpdateSceneDateRequest(ReadOnlyMemory<byte> _)
|
|
||||||
{
|
|
||||||
await Session.Rpc.ReturnAsync(MessageId.UpdateSceneDateResponse, new UpdateSceneDateResponse());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,12 @@
|
||||||
namespace GameServer.Models;
|
using GameServer.Controllers.Attributes;
|
||||||
|
using GameServer.Controllers.Event;
|
||||||
|
|
||||||
|
namespace GameServer.Models;
|
||||||
internal class ModelManager
|
internal class ModelManager
|
||||||
{
|
{
|
||||||
private PlayerModel? _playerModel;
|
private PlayerModel? _playerModel;
|
||||||
|
|
||||||
|
[GameEvent(GameEventType.Login)]
|
||||||
public void OnLogin()
|
public void OnLogin()
|
||||||
{
|
{
|
||||||
_playerModel = PlayerModel.CreateDefaultPlayer();
|
_playerModel = PlayerModel.CreateDefaultPlayer();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using GameServer.Network.Packets;
|
using GameServer.Network.Messages;
|
||||||
|
|
||||||
namespace GameServer.Network;
|
namespace GameServer.Network;
|
||||||
internal interface IConnection : ISessionActionListener
|
internal interface IConnection : ISessionActionListener
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using GameServer.Network.Packets;
|
using GameServer.Network.Messages;
|
||||||
|
|
||||||
namespace GameServer.Network;
|
namespace GameServer.Network;
|
||||||
internal interface ISessionActionListener
|
internal interface ISessionActionListener
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
using GameServer.Extensions;
|
using GameServer.Extensions;
|
||||||
using GameServer.Handlers;
|
|
||||||
using GameServer.Network.Packets;
|
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using KcpSharp;
|
using KcpSharp;
|
||||||
|
using GameServer.Network.Messages;
|
||||||
|
|
||||||
namespace GameServer.Network.Kcp;
|
namespace GameServer.Network.Kcp;
|
||||||
internal class KcpConnection : IConnection
|
internal class KcpConnection : IConnection
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
|
|
||||||
namespace GameServer.Network.Packets;
|
namespace GameServer.Network.Messages;
|
||||||
internal abstract class BaseMessage
|
internal abstract class BaseMessage
|
||||||
{
|
{
|
||||||
public const int LengthFieldSize = 3;
|
public const int LengthFieldSize = 3;
|
||||||
|
|
59
GameServer/Network/Messages/MessageManager.cs
Normal file
59
GameServer/Network/Messages/MessageManager.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
using GameServer.Controllers.Factory;
|
||||||
|
using Protocol;
|
||||||
|
|
||||||
|
namespace GameServer.Network.Messages;
|
||||||
|
|
||||||
|
internal delegate Task PushHandler(IServiceProvider serviceProvider, ReadOnlySpan<byte> data);
|
||||||
|
internal delegate Task<ResponseMessage> RpcHandler(IServiceProvider serviceProvider, ReadOnlySpan<byte> 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<ResponseMessage?> ExecuteRpc(MessageId messageId, ReadOnlyMemory<byte> data)
|
||||||
|
{
|
||||||
|
RpcHandler? handler = _handlerFactory.GetRpcHandler(messageId);
|
||||||
|
if (handler != null)
|
||||||
|
return await handler(_serviceProvider, data.Span);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> HandlePush(MessageId messageId, ReadOnlyMemory<byte> data)
|
||||||
|
{
|
||||||
|
PushHandler? handler = _handlerFactory.GetPushHandler(messageId);
|
||||||
|
if (handler == null) return false;
|
||||||
|
|
||||||
|
await handler(_serviceProvider, data.Span);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EncodeMessage(Memory<byte> buffer, BaseMessage message)
|
||||||
|
{
|
||||||
|
buffer.Span[0] = (byte)message.Type;
|
||||||
|
message.Encode(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BaseMessage DecodeMessage(ReadOnlyMemory<byte> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using GameServer.Network.Packets;
|
|
||||||
using Protocol;
|
using Protocol;
|
||||||
|
|
||||||
namespace GameServer.Network.Messages;
|
namespace GameServer.Network.Messages;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using GameServer.Network.Packets;
|
|
||||||
using Protocol;
|
using Protocol;
|
||||||
|
|
||||||
namespace GameServer.Network.Messages;
|
namespace GameServer.Network.Messages;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using GameServer.Network.Packets;
|
|
||||||
using Protocol;
|
using Protocol;
|
||||||
|
|
||||||
namespace GameServer.Network.Messages;
|
namespace GameServer.Network.Messages;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
using GameServer.Handlers;
|
using GameServer.Network.Messages;
|
||||||
using GameServer.Network.Messages;
|
|
||||||
using GameServer.Network.Packets;
|
|
||||||
using GameServer.Network.Rpc;
|
using GameServer.Network.Rpc;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
@ -27,17 +25,17 @@ internal class PlayerSession
|
||||||
switch (message)
|
switch (message)
|
||||||
{
|
{
|
||||||
case RequestMessage request:
|
case RequestMessage request:
|
||||||
await Rpc.HandleRpcRequest(request);
|
await Rpc.Execute(request);
|
||||||
break;
|
break;
|
||||||
case PushMessage push:
|
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);
|
_logger.LogWarning("Push message ({id}) was not handled", push.MessageId);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task PushMessage<TProtoBuf>(MessageId id, TProtoBuf data) where TProtoBuf : IMessage<TProtoBuf>
|
public Task Push<TProtoBuf>(MessageId id, TProtoBuf data) where TProtoBuf : IMessage<TProtoBuf>
|
||||||
{
|
{
|
||||||
return Listener?.OnServerMessageAvailable(new PushMessage
|
return Listener?.OnServerMessageAvailable(new PushMessage
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
using GameServer.Handlers;
|
using GameServer.Network.Messages;
|
||||||
using GameServer.Network.Messages;
|
|
||||||
using Google.Protobuf;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Protocol;
|
|
||||||
|
|
||||||
namespace GameServer.Network.Rpc;
|
namespace GameServer.Network.Rpc;
|
||||||
internal class RpcManager
|
internal class RpcManager
|
||||||
|
@ -10,7 +7,6 @@ internal class RpcManager
|
||||||
private readonly IRpcEndPoint _endPoint;
|
private readonly IRpcEndPoint _endPoint;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly MessageManager _messageManager;
|
private readonly MessageManager _messageManager;
|
||||||
private ushort _curId;
|
|
||||||
|
|
||||||
public RpcManager(MessageManager messageManager, IRpcEndPoint endPoint, ILogger<RpcManager> logger)
|
public RpcManager(MessageManager messageManager, IRpcEndPoint endPoint, ILogger<RpcManager> logger)
|
||||||
{
|
{
|
||||||
|
@ -19,32 +15,18 @@ internal class RpcManager
|
||||||
_messageManager = messageManager;
|
_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);
|
ResponseMessage? response = await _messageManager.ExecuteRpc(request.MessageId, request.Payload);
|
||||||
|
if (response == null)
|
||||||
_curId = request.RpcID;
|
|
||||||
_ = await _messageManager.ProcessMessage(request.MessageId, request.Payload);
|
|
||||||
|
|
||||||
if (_curId != 0)
|
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Rpc was not handled properly (message: {msg_id}, id: {rpc_id})", request.MessageId, request.RpcID);
|
_logger.LogWarning("Rpc was not handled properly (message: {msg_id}, id: {rpc_id})", request.MessageId, request.RpcID);
|
||||||
_curId = 0;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ReturnAsync<TProtoBuf>(MessageId messageId, TProtoBuf data) where TProtoBuf : IMessage<TProtoBuf>
|
response.RpcID = request.RpcID;
|
||||||
{
|
await _endPoint.SendRpcResult(response);
|
||||||
if (_curId == 0) throw new InvalidOperationException("RpcManager::ReturnAsync called - no rpc being processed!");
|
|
||||||
|
|
||||||
await _endPoint.SendRpcResult(new ResponseMessage
|
_logger.LogInformation("Rpc with id {rpc_id} was handled, return message: {msg_id}", request.RpcID, response.MessageId);
|
||||||
{
|
|
||||||
MessageId = messageId,
|
|
||||||
RpcID = _curId,
|
|
||||||
Payload = data.ToByteArray()
|
|
||||||
});
|
|
||||||
|
|
||||||
_logger.LogInformation("Rpc with id {rpc_id} was handled, return message: {msg_id}", _curId, messageId);
|
|
||||||
_curId = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using GameServer.Network.Kcp;
|
using GameServer.Network.Kcp;
|
||||||
using GameServer.Network.Packets;
|
using GameServer.Network.Messages;
|
||||||
using KcpSharp;
|
using KcpSharp;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using GameServer.Extensions;
|
using GameServer.Controllers.Event;
|
||||||
using GameServer.Handlers;
|
using GameServer.Controllers.Factory;
|
||||||
using GameServer.Handlers.Factory;
|
using GameServer.Controllers.Manager;
|
||||||
|
using GameServer.Extensions;
|
||||||
using GameServer.Models;
|
using GameServer.Models;
|
||||||
using GameServer.Network;
|
using GameServer.Network;
|
||||||
using GameServer.Network.Kcp;
|
using GameServer.Network.Kcp;
|
||||||
|
using GameServer.Network.Messages;
|
||||||
using GameServer.Network.Rpc;
|
using GameServer.Network.Rpc;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
@ -18,12 +20,12 @@ internal static class Program
|
||||||
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
|
||||||
builder.Logging.AddConsole();
|
builder.Logging.AddConsole();
|
||||||
|
|
||||||
builder.Services.AddHandlers()
|
builder.Services.AddControllers()
|
||||||
.AddSingleton<KcpGateway>().AddScoped<PlayerSession>()
|
.AddSingleton<KcpGateway>().AddScoped<PlayerSession>()
|
||||||
.AddScoped<MessageManager>().AddSingleton<MessageHandlerFactory>()
|
.AddScoped<MessageManager>().AddSingleton<EventHandlerFactory>()
|
||||||
.AddScoped<RpcManager>().AddScoped<IRpcEndPoint, RpcSessionEndPoint>()
|
.AddScoped<RpcManager>().AddScoped<IRpcEndPoint, RpcSessionEndPoint>()
|
||||||
.AddSingleton<SessionManager>()
|
.AddSingleton<SessionManager>()
|
||||||
.AddScoped<ModelManager>()
|
.AddScoped<EventSystem>().AddScoped<ModelManager>().AddScoped<ControllerManager>()
|
||||||
.AddHostedService<WWGameServer>();
|
.AddHostedService<WWGameServer>();
|
||||||
|
|
||||||
await builder.Build().RunAsync();
|
await builder.Build().RunAsync();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using GameServer.Handlers.Factory;
|
using GameServer.Controllers.Factory;
|
||||||
using GameServer.Network.Kcp;
|
using GameServer.Network.Kcp;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ internal class WWGameServer : IHostedService
|
||||||
{
|
{
|
||||||
private readonly KcpGateway _gateway;
|
private readonly KcpGateway _gateway;
|
||||||
|
|
||||||
public WWGameServer(KcpGateway gateway, MessageHandlerFactory messageHandlerFactory)
|
public WWGameServer(KcpGateway gateway, EventHandlerFactory messageHandlerFactory)
|
||||||
{
|
{
|
||||||
_ = messageHandlerFactory;
|
_ = messageHandlerFactory;
|
||||||
_gateway = gateway;
|
_gateway = gateway;
|
||||||
|
@ -20,8 +20,5 @@ internal class WWGameServer : IHostedService
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue