Handling systems completely reworked

This commit is contained in:
xeon 2024-02-10 01:15:05 +03:00
parent 35ebc910ed
commit 77956be694
61 changed files with 965 additions and 671 deletions

View file

@ -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

View 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());
}

View 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;
}
}

View 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;
}
}

View 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()
};
}

View 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());
}

View 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();
}
}

View file

@ -0,0 +1,6 @@
namespace GameServer.Controllers.Event;
internal enum GameEventType
{
Login = 1,
EnterGame
}

View 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());
}

View 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;
}
}

View 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 },
}
},
});
}

View 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());
}

View 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);
}
}

View 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());
}

View 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());
}

View 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());
}

View 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());
}

View 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());
}

View 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>();
}
}

View 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
}
},
});
}
}

View 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());
}

View 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());
}

View 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());
}

View 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());
}

View file

@ -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 }
}); });
} }
}

View file

@ -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());
} }

View 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());
}

View file

@ -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);
} }

View file

@ -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());
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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();
}
}

View file

@ -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 },
}
},
});
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -1,12 +0,0 @@
using GameServer.Network;
namespace GameServer.Handlers;
internal abstract class MessageHandlerBase
{
protected PlayerSession Session { get; }
public MessageHandlerBase(PlayerSession session)
{
Session = session;
}
}

View file

@ -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;
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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());
}
}

View file

@ -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();

View file

@ -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

View file

@ -1,4 +1,4 @@
using GameServer.Network.Packets; using GameServer.Network.Messages;
namespace GameServer.Network; namespace GameServer.Network;
internal interface ISessionActionListener internal interface ISessionActionListener

View file

@ -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

View file

@ -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;

View 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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
{ {

View file

@ -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;
} }
} }

View file

@ -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;

View file

@ -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();

View file

@ -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;
}
} }