mirror of
https://github.com/MikuLeaks/MikuSB.git
synced 2026-06-04 05:23:59 +00:00
enter intro cutscene
This commit is contained in:
79
GameServer/Command/CommandArg.cs
Normal file
79
GameServer/Command/CommandArg.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using MikuSB.Database.Account;
|
||||
using MikuSB.GameServer.Server;
|
||||
using MikuSB.Internationalization;
|
||||
|
||||
namespace MikuSB.GameServer.Command;
|
||||
|
||||
public class CommandArg
|
||||
{
|
||||
public string RawArg { get; } = "";
|
||||
public List<string> Args { get; } = [];
|
||||
public List<string> Attributes { get; } = [];
|
||||
public ICommandSender Sender { get; }
|
||||
public int TargetUid { get; set; } = 0;
|
||||
public Connection? Target { get; set; }
|
||||
|
||||
public CommandArg(string rawArg, ICommandSender sender)
|
||||
{
|
||||
Sender = sender;
|
||||
RawArg = rawArg;
|
||||
foreach (var arg in rawArg.Split(' '))
|
||||
{
|
||||
if (string.IsNullOrEmpty(arg)) continue;
|
||||
Args.Add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask SendMsg(string msg)
|
||||
{
|
||||
await Sender.SendMsg(msg);
|
||||
}
|
||||
|
||||
public int GetInt(int index)
|
||||
{
|
||||
if (Args.Count <= index) return 0;
|
||||
if (int.TryParse(Args[index], out var res))
|
||||
return res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public async ValueTask<int?> GetOption(char pre, string def = "1")
|
||||
{
|
||||
var opStr = Args.FirstOrDefault(x => x[0] == pre)?[1..] ?? def;
|
||||
if (!int.TryParse(opStr, out var op))
|
||||
{
|
||||
await SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments"));
|
||||
return null;
|
||||
}
|
||||
return op;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> CheckArgCnt(int start, int? end = null)
|
||||
{
|
||||
end ??= start;
|
||||
if (Args.Count >= start && Args.Count <= end) return true;
|
||||
await SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments"));
|
||||
return false;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> CheckTarget()
|
||||
{
|
||||
if (AccountData.GetAccountByUid(TargetUid) == null)
|
||||
{
|
||||
await SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public async ValueTask<bool> CheckOnlineTarget(bool sendMsg = true)
|
||||
{
|
||||
if (Target == null)
|
||||
{
|
||||
if (sendMsg)
|
||||
await SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
25
GameServer/Command/CommandAttribute.cs
Normal file
25
GameServer/Command/CommandAttribute.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using MikuSB.Enums.Player;
|
||||
|
||||
namespace MikuSB.GameServer.Command;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class CommandInfoAttribute(
|
||||
string name, string desc, string usage, string[] alias, PermEnum[] perm) : Attribute
|
||||
{
|
||||
public string Name { get; } = name;
|
||||
public string Description { get; } = desc;
|
||||
public string Usage { get; } = usage;
|
||||
public PermEnum[] Perm { get; } = perm;
|
||||
public string[] Alias { get; } = alias;
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class CommandDefaultAttribute : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class CommandMethodAttribute(string method) : Attribute
|
||||
{
|
||||
public string MethodName { get; } = method;
|
||||
}
|
||||
19
GameServer/Command/CommandExecutor.cs
Normal file
19
GameServer/Command/CommandExecutor.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
namespace MikuSB.GameServer.Command;
|
||||
|
||||
public static class CommandExecutor
|
||||
{
|
||||
public delegate void RunCommand(ICommandSender sender, string cmd);
|
||||
|
||||
public static event RunCommand? OnRunCommand;
|
||||
|
||||
public static void ExecuteCommand(ICommandSender sender, string cmd)
|
||||
{
|
||||
OnRunCommand?.Invoke(sender, cmd);
|
||||
}
|
||||
|
||||
public static void ConsoleExcuteCommand(string input)
|
||||
{
|
||||
CommandManager.HandleCommand(input, new ConsoleCommandSender(CommandManager.Logger));
|
||||
}
|
||||
}
|
||||
3
GameServer/Command/CommandInterface.cs
Normal file
3
GameServer/Command/CommandInterface.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace MikuSB.GameServer.Command;
|
||||
|
||||
public interface ICommands;
|
||||
126
GameServer/Command/CommandManager.cs
Normal file
126
GameServer/Command/CommandManager.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using MikuSB.Database.Account;
|
||||
using MikuSB.Enums.Player;
|
||||
using MikuSB.GameServer.Server;
|
||||
using MikuSB.Internationalization;
|
||||
using MikuSB.TcpSharp;
|
||||
using MikuSB.Util;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MikuSB.GameServer.Command;
|
||||
|
||||
public class CommandManager
|
||||
{
|
||||
public static Logger Logger { get; } = new("CommandManager");
|
||||
|
||||
public static Dictionary<string, ICommands> Commands { get; } = [];
|
||||
public static Dictionary<string, CommandInfoAttribute> CommandInfo { get; } = [];
|
||||
public static Dictionary<string, string> CommandAlias { get; } = []; // <aliaName, fullName>
|
||||
|
||||
public static void RegisterCommands()
|
||||
{
|
||||
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
|
||||
if (typeof(ICommands).IsAssignableFrom(type) && !type.IsAbstract)
|
||||
RegisterCommand(type);
|
||||
|
||||
Logger.Info(I18NManager.Translate("Server.ServerInfo.RegisterItem", Commands.Count.ToString(),
|
||||
I18NManager.Translate("Word.Command")));
|
||||
}
|
||||
|
||||
public static void RegisterCommand(Type type)
|
||||
{
|
||||
var attr = type.GetCustomAttribute<CommandInfoAttribute>();
|
||||
if (attr == null) return;
|
||||
var instance = Activator.CreateInstance(type);
|
||||
if (instance is not ICommands command) return;
|
||||
Commands.Add(attr.Name, command);
|
||||
CommandInfo.Add(attr.Name, attr);
|
||||
|
||||
// register alias
|
||||
foreach (var alias in attr.Alias) // add alias
|
||||
CommandAlias.Add(alias, attr.Name);
|
||||
}
|
||||
|
||||
public static async void HandleCommand(string input, ICommandSender sender)
|
||||
{
|
||||
try
|
||||
{
|
||||
var argInfo = new CommandArg(input, sender);
|
||||
var target = sender.GetSender();
|
||||
|
||||
foreach (var arg in argInfo.Args.ToList()) // Copy
|
||||
{
|
||||
switch (arg[0])
|
||||
{
|
||||
case '-':
|
||||
argInfo.Attributes.Add(arg[1..]);
|
||||
break;
|
||||
case '@':
|
||||
_ = int.TryParse(arg[1..], out target);
|
||||
argInfo.Args.Remove(arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
argInfo.TargetUid = target;
|
||||
if (SocketListener.Connections.Values.ToList().Find(item =>
|
||||
(item as Connection)?.Player?.Uid == target) is Connection con)
|
||||
argInfo.Target = con;
|
||||
|
||||
// find register cmd
|
||||
var cmdName = argInfo.Args[0];
|
||||
if (CommandAlias.TryGetValue(cmdName, out var fullName)) cmdName = fullName;
|
||||
if (!Commands.TryGetValue(cmdName, out var command))
|
||||
{
|
||||
await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.CommandNotFound"));
|
||||
return;
|
||||
}
|
||||
argInfo.Args.RemoveAt(0);
|
||||
var cmdInfo = CommandInfo[cmdName];
|
||||
|
||||
// Check cmd perms
|
||||
if (!AccountData.HasPerm(cmdInfo.Perm, sender.GetSender()))
|
||||
{
|
||||
await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.NoPermission"));
|
||||
return;
|
||||
}
|
||||
if (argInfo.Target?.Player?.Uid != sender.GetSender() && !AccountData.HasPerm([PermEnum.Other], sender.GetSender()))
|
||||
{
|
||||
await sender.SendMsg(I18NManager.Translate("Game.Command.Notice.NoPermission"));
|
||||
return;
|
||||
}
|
||||
|
||||
// find CommandMethodAttribute
|
||||
var isFound = false;
|
||||
foreach (var methodInfo in command.GetType().GetMethods())
|
||||
{
|
||||
var attr = methodInfo.GetCustomAttribute<CommandMethodAttribute>();
|
||||
if (attr == null) continue;
|
||||
if (argInfo.Args.Count > 0 && attr.MethodName == argInfo.Args[0])
|
||||
{
|
||||
argInfo.Args.RemoveAt(0);
|
||||
isFound = true;
|
||||
methodInfo.Invoke(command, [argInfo]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isFound) return;
|
||||
|
||||
// find CommandDefaultAttribute
|
||||
foreach (var methodInfo in command.GetType().GetMethods())
|
||||
{
|
||||
var attr = methodInfo.GetCustomAttribute<CommandDefaultAttribute>();
|
||||
if (attr == null) continue;
|
||||
isFound = true;
|
||||
methodInfo.Invoke(command, [argInfo]);
|
||||
break;
|
||||
}
|
||||
if (isFound) return;
|
||||
|
||||
// failed to find method
|
||||
await sender.SendMsg(I18NManager.Translate(cmdInfo.Usage));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Error(I18NManager.Translate("Game.Command.Notice.InternalError", ex.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
41
GameServer/Command/CommandSender.cs
Normal file
41
GameServer/Command/CommandSender.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using MikuSB.Enums.Player;
|
||||
using MikuSB.GameServer.Game.Player;
|
||||
using MikuSB.Util;
|
||||
|
||||
namespace MikuSB.GameServer.Command;
|
||||
|
||||
public interface ICommandSender
|
||||
{
|
||||
public ValueTask SendMsg(string msg);
|
||||
|
||||
public int GetSender();
|
||||
}
|
||||
|
||||
public class ConsoleCommandSender(Logger logger) : ICommandSender
|
||||
{
|
||||
public async ValueTask SendMsg(string msg)
|
||||
{
|
||||
logger.Info(msg);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public int GetSender()
|
||||
{
|
||||
return (int)ServerEnum.Console;
|
||||
}
|
||||
}
|
||||
|
||||
public class PlayerCommandSender(PlayerInstance player) : ICommandSender
|
||||
{
|
||||
public PlayerInstance Player = player;
|
||||
|
||||
public async ValueTask SendMsg(string msg)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
public int GetSender()
|
||||
{
|
||||
return Player.Uid;
|
||||
}
|
||||
}
|
||||
52
GameServer/Command/Commands/CommandHelp.cs
Normal file
52
GameServer/Command/Commands/CommandHelp.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using MikuSB.Enums.Player;
|
||||
using MikuSB.Internationalization;
|
||||
using MikuSB.Util.Extensions;
|
||||
|
||||
namespace MikuSB.GameServer.Command.Commands;
|
||||
|
||||
[CommandInfo("help", "Game.Command.Help.Desc", "Game.Command.Help.Usage", ["h"], [PermEnum.Support, PermEnum.Trial])]
|
||||
public class CommandHelp : ICommands
|
||||
{
|
||||
[CommandDefault]
|
||||
public async static ValueTask Help(CommandArg arg)
|
||||
{
|
||||
if (arg.Args.Count == 1)
|
||||
{
|
||||
var cmd = arg.Args[0];
|
||||
if (CommandManager.CommandInfo == null || !CommandManager.CommandInfo.TryGetValue(cmd, out var command))
|
||||
{
|
||||
await arg.SendMsg(I18NManager.Translate("Game.Command.Notice.CommandNotFound"));
|
||||
return;
|
||||
}
|
||||
|
||||
var msg =
|
||||
$"/{command.Name} - {I18NManager.Translate(command.Description)}\n{I18NManager.Translate(command.Usage)}";
|
||||
if (command.Alias.Length > 0)
|
||||
msg +=
|
||||
$"\n{I18NManager.Translate("Game.Command.Help.CommandAlias")} {command.Alias.ToList().ToArrayString()}";
|
||||
if (command.Perm != null)
|
||||
msg += $"\n{I18NManager.Translate("Game.Command.Help.CommandPermission")} {string.Join(", ", command.Perm.Select(perm => perm.ToString()))}";
|
||||
|
||||
await arg.SendMsg(msg + "\n");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
await arg.SendMsg(I18NManager.Translate("Game.Command.Help.Commands"));
|
||||
if (CommandManager.CommandInfo == null) return;
|
||||
|
||||
foreach (var command in CommandManager.CommandInfo.Values)
|
||||
{
|
||||
var msg =
|
||||
$"/{command.Name} - {I18NManager.Translate(command.Description)}\n{I18NManager.Translate(command.Usage)}";
|
||||
if (command.Alias.Length > 0)
|
||||
msg +=
|
||||
$"\n{I18NManager.Translate("Game.Command.Help.CommandAlias")} {command.Alias.ToList().ToArrayString()}";
|
||||
|
||||
if (command.Perm != null)
|
||||
msg += $"\n{I18NManager.Translate("Game.Command.Help.CommandPermission")} {string.Join(", ", command.Perm.Select(perm => perm.ToString()))}";
|
||||
await arg.SendMsg(msg + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
GameServer/Game/BasePlayerManager.cs
Normal file
8
GameServer/Game/BasePlayerManager.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using MikuSB.GameServer.Game.Player;
|
||||
|
||||
namespace MikuSB.GameServer.Game;
|
||||
|
||||
public class BasePlayerManager(PlayerInstance player)
|
||||
{
|
||||
public PlayerInstance Player { get; private set; } = player;
|
||||
}
|
||||
98
GameServer/Game/Player/PlayerInstance.cs
Normal file
98
GameServer/Game/Player/PlayerInstance.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Database.Account;
|
||||
using MikuSB.Database.Player;
|
||||
using MikuSB.GameServer.Server;
|
||||
using MikuSB.TcpSharp;
|
||||
using MikuSB.Util.Extensions;
|
||||
|
||||
namespace MikuSB.GameServer.Game.Player;
|
||||
|
||||
public class PlayerInstance(PlayerGameData data)
|
||||
{
|
||||
#region Property
|
||||
public Connection? Connection { get; set; }
|
||||
|
||||
public static readonly List<PlayerInstance> _playerInstances = [];
|
||||
public int Uid { get; set; }
|
||||
public bool Initialized { get; set; }
|
||||
public bool IsNewPlayer { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data & Manager
|
||||
|
||||
public PlayerGameData Data { get; set; } = data;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initializers
|
||||
public PlayerInstance(int uid) : this(new PlayerGameData { Uid = uid })
|
||||
{
|
||||
// new player
|
||||
IsNewPlayer = true;
|
||||
Data.Name = AccountData.GetAccountByUid(uid)?.Username;
|
||||
|
||||
DatabaseHelper.CreateInstance(Data);
|
||||
|
||||
var t = Task.Run(async () =>
|
||||
{
|
||||
await InitialPlayerManager();
|
||||
});
|
||||
t.Wait();
|
||||
|
||||
Initialized = true;
|
||||
|
||||
}
|
||||
private async ValueTask InitialPlayerManager()
|
||||
{
|
||||
Uid = Data.Uid;
|
||||
Data.LastActiveTime = Extensions.GetUnixSec();
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
public T InitializeDatabase<T>() where T : BaseDatabaseDataHelper, new()
|
||||
{
|
||||
var instance = DatabaseHelper.GetInstanceOrCreateNew<T>(Uid);
|
||||
return instance!;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Network
|
||||
public async ValueTask OnEnterGame()
|
||||
{
|
||||
if (!Initialized) await InitialPlayerManager();
|
||||
}
|
||||
|
||||
public async ValueTask OnLogin()
|
||||
{
|
||||
_playerInstances.Add(this);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static PlayerInstance? GetPlayerInstanceByUid(long uid)
|
||||
=> _playerInstances.FirstOrDefault(player => player.Uid == uid);
|
||||
public void OnLogoutAsync()
|
||||
{
|
||||
_playerInstances.Remove(this);
|
||||
}
|
||||
public async ValueTask SendPacket(BasePacket packet)
|
||||
{
|
||||
if (Connection?.IsOnline == true) await Connection.SendPacket(packet);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Actions
|
||||
public async ValueTask OnHeartBeat()
|
||||
{
|
||||
DatabaseHelper.ToSaveUidList.SafeAdd(Uid);
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Serialization
|
||||
|
||||
#endregion
|
||||
}
|
||||
28
GameServer/GameServer.csproj
Normal file
28
GameServer/GameServer.csproj
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<CETCompat>false</CETCompat>
|
||||
<RootNamespace>MikuSB.GameServer</RootNamespace>
|
||||
<StartupObject></StartupObject>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<AssemblyName>MikuGameServer</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Game\Hero\**" />
|
||||
<EmbeddedResource Remove="Game\Hero\**" />
|
||||
<None Remove="Game\Hero\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Common\Common.csproj" />
|
||||
<ProjectReference Include="..\TcpSharp\TcpSharp.csproj" />
|
||||
<ProjectReference Include="..\Proto\Proto.csproj" />
|
||||
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
142
GameServer/Server/Connection.cs
Normal file
142
GameServer/Server/Connection.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using MikuSB.Enums.Packet;
|
||||
using MikuSB.GameServer.Game.Player;
|
||||
using MikuSB.GameServer.Server.Packet;
|
||||
using MikuSB.TcpSharp;
|
||||
using MikuSB.Util;
|
||||
using System.Buffers;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace MikuSB.GameServer.Server;
|
||||
|
||||
public class Connection(Socket socket, IPEndPoint remote) : SocketConnection(socket, remote)
|
||||
{
|
||||
private static readonly Logger Logger = new("GameServer");
|
||||
|
||||
public PlayerInstance? Player { get; set; }
|
||||
|
||||
private static readonly HashSet<string> DummyPacketNames =
|
||||
[
|
||||
|
||||
];
|
||||
|
||||
public override async void Start()
|
||||
{
|
||||
Logger.Info($"New connection from {RemoteEndPoint}.");
|
||||
State = SessionStateEnum.WAITING_FOR_TOKEN;
|
||||
await ReceiveLoop();
|
||||
}
|
||||
|
||||
public override void Stop(bool isServerStop = false)
|
||||
{
|
||||
Player?.OnLogoutAsync();
|
||||
SocketListener.UnregisterConnection(this);
|
||||
base.Stop(isServerStop);
|
||||
}
|
||||
|
||||
public static int GetInt32(byte[] buf, int index)
|
||||
{
|
||||
int networkValue = BitConverter.ToInt32(buf, index);
|
||||
return IPAddress.NetworkToHostOrder((int)networkValue);
|
||||
}
|
||||
|
||||
protected async Task ReceiveLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
var stream = new NetworkStream(Socket, ownsSocket: false);
|
||||
|
||||
while (SocketConnected())
|
||||
{
|
||||
var decodedPacket = await new PacketCodec().ReadPacketAsync(stream, CancelToken.Token);
|
||||
|
||||
if (decodedPacket == null)
|
||||
{
|
||||
Logger.Info("Client disconnected");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (decodedPacket.Framing)
|
||||
{
|
||||
case PacketFraming.FourByteLittleEndianLength:
|
||||
case PacketFraming.TwoByteBigEndianLength:
|
||||
Framing = decodedPacket.Framing;
|
||||
LogPacket("Recv", decodedPacket.CmdId, decodedPacket.Body.ToArray(),Framing);
|
||||
await HandlePacket(decodedPacket.CmdId, decodedPacket.Body.ToArray());
|
||||
break;
|
||||
|
||||
case PacketFraming.Control:
|
||||
Logger.Info("Control packet received");
|
||||
// Handle control packet if needed
|
||||
break;
|
||||
|
||||
case PacketFraming.Unknown:
|
||||
Logger.Warn("Unknown packet format received");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Logger.Info("ReceiveLoop cancelled");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Info($"ReceiveLoop error: {ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Socket.Close();
|
||||
}
|
||||
Stop();
|
||||
}
|
||||
|
||||
private async Task HandlePacket(ushort opcode, byte[] payload)
|
||||
{
|
||||
var packetName = LogMap.GetValueOrDefault(opcode);
|
||||
if (DummyPacketNames.Contains(packetName!))
|
||||
{
|
||||
await SendDummy(packetName!);
|
||||
Logger.Info($"[Dummy] Send Dummy {packetName}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the Handler for this opcode
|
||||
var handler = HandlerManager.GetHandler(opcode);
|
||||
if (handler != null)
|
||||
{
|
||||
// Handle
|
||||
// Make sure session is ready for packets
|
||||
var state = State;
|
||||
try
|
||||
{
|
||||
await handler.OnHandle(this, payload, (ushort)DownStreamSeqNo);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error(e.Message, e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ConfigManager.Config.ServerOption.EnableDebug &&
|
||||
ConfigManager.Config.ServerOption.DebugNoHandlerPacket && !IgnoreLog.Contains(opcode))
|
||||
Logger.Error($"No handler found for {packetName}({opcode})");
|
||||
|
||||
//if (ConfigManager.Config.ServerOption.AutoSendResponseWhenNoHandler)
|
||||
//{
|
||||
// await SendDummy(packetName);
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
private async Task SendDummy(string packetName)
|
||||
{
|
||||
var respName = packetName.Replace("Req", "Rsp"); // Get the response packet name
|
||||
if (respName == packetName) return; // do not send rsp when resp name = recv name
|
||||
var respOpcode = LogMap.FirstOrDefault(x => x.Value == respName).Key; // Get the response opcode
|
||||
|
||||
// Send Rsp
|
||||
await SendPacket(respOpcode);
|
||||
}
|
||||
}
|
||||
13
GameServer/Server/Listener.cs
Normal file
13
GameServer/Server/Listener.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using MikuSB.TcpSharp;
|
||||
|
||||
namespace MikuSB.GameServer.Server;
|
||||
|
||||
public class Listener : SocketListener
|
||||
{
|
||||
public static Connection? GetActiveConnection(int uid)
|
||||
{
|
||||
var con = Connections.Values.FirstOrDefault(c =>
|
||||
(c as Connection)?.Player?.Uid == uid && c.State == SessionStateEnum.ACTIVE) as Connection;
|
||||
return con;
|
||||
}
|
||||
}
|
||||
6
GameServer/Server/Packet/Handler.cs
Normal file
6
GameServer/Server/Packet/Handler.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace MikuSB.GameServer.Server.Packet;
|
||||
|
||||
public abstract class Handler
|
||||
{
|
||||
public abstract Task OnHandle(Connection connection, byte[] data, ushort SeqNo = 0);
|
||||
}
|
||||
31
GameServer/Server/Packet/HandlerManager.cs
Normal file
31
GameServer/Server/Packet/HandlerManager.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace MikuSB.GameServer.Server.Packet;
|
||||
|
||||
public static class HandlerManager
|
||||
{
|
||||
public static Dictionary<int, Handler> handlers = [];
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
var classes = Assembly.GetExecutingAssembly().GetTypes(); // Get all classes in the assembly
|
||||
foreach (var cls in classes)
|
||||
{
|
||||
var attribute = (Opcode?)Attribute.GetCustomAttribute(cls, typeof(Opcode));
|
||||
|
||||
if (attribute != null) handlers.Add(attribute.CmdId, (Handler)Activator.CreateInstance(cls)!);
|
||||
}
|
||||
}
|
||||
|
||||
public static Handler? GetHandler(int cmdId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return handlers[cmdId];
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
GameServer/Server/Packet/Opcode.cs
Normal file
7
GameServer/Server/Packet/Opcode.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace MikuSB.GameServer.Server.Packet;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public class Opcode(int cmdId) : Attribute
|
||||
{
|
||||
public int CmdId = cmdId;
|
||||
}
|
||||
50
GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs
Normal file
50
GameServer/Server/Packet/Recv/Login/HandlerReqLogin.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using MikuSB.Data;
|
||||
using MikuSB.Database;
|
||||
using MikuSB.Database.Account;
|
||||
using MikuSB.Database.Player;
|
||||
using MikuSB.GameServer.Game.Player;
|
||||
using MikuSB.GameServer.Server.Packet.Send.Login;
|
||||
using MikuSB.Proto;
|
||||
using MikuSB.TcpSharp;
|
||||
using MikuSB.Util;
|
||||
|
||||
namespace MikuSB.GameServer.Server.Packet.Recv.Login;
|
||||
|
||||
[Opcode(CmdIds.ReqLogin)]
|
||||
public class HandlerReqLogin : Handler
|
||||
{
|
||||
public override async Task OnHandle(Connection connection, byte[] data, ushort seqNo)
|
||||
{
|
||||
var req = ReqLogin.Parser.ParseFrom(data);
|
||||
var account = AccountData.GetAccountByUid(1);
|
||||
if (account == null)
|
||||
{
|
||||
AccountData.CreateAccount("MIKU", 0, "");
|
||||
account = AccountData.GetAccountByUid(1);
|
||||
if (account == null)
|
||||
{
|
||||
await connection.SendPacket(CmdIds.NtfLogout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!ResourceManager.IsLoaded)
|
||||
// resource manager not loaded, return
|
||||
return;
|
||||
var prev = Listener.GetActiveConnection(account.Uid);
|
||||
if (prev != null)
|
||||
{
|
||||
await connection.SendPacket(CmdIds.NtfLogout);
|
||||
prev.Stop();
|
||||
}
|
||||
|
||||
connection.State = SessionStateEnum.WAITING_FOR_LOGIN;
|
||||
var pd = DatabaseHelper.GetInstance<PlayerGameData>(account.Uid);
|
||||
connection.Player = pd == null ? new PlayerInstance(account.Uid) : new PlayerInstance(pd);
|
||||
|
||||
connection.DebugFile = Path.Combine(ConfigManager.Config.Path.LogPath, "Debug/", $"{account.Uid}/",
|
||||
$"Debug-{DateTime.Now:yyyy-MM-dd HH-mm-ss}.log");
|
||||
await connection.Player.OnEnterGame();
|
||||
connection.Player.Connection = connection;
|
||||
await connection.SendPacket(new PacketRspLogin(connection.Player!));
|
||||
}
|
||||
}
|
||||
29
GameServer/Server/Packet/Send/Login/PacketRspLogin.cs
Normal file
29
GameServer/Server/Packet/Send/Login/PacketRspLogin.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using MikuSB.GameServer.Game.Player;
|
||||
using MikuSB.TcpSharp;
|
||||
using MikuSB.Proto;
|
||||
using MikuSB.Util.Extensions;
|
||||
|
||||
namespace MikuSB.GameServer.Server.Packet.Send.Login;
|
||||
|
||||
public class PacketRspLogin : BasePacket
|
||||
{
|
||||
public PacketRspLogin(PlayerInstance player) : base(CmdIds.RspLogin)
|
||||
{
|
||||
var proto = new RspLogin
|
||||
{
|
||||
Timestamp = (uint)Extensions.GetUnixSec(),
|
||||
WorldChannel = 1,
|
||||
AreaId = 1,
|
||||
Data = new Player
|
||||
{
|
||||
Pid = (ulong)player.Data.Uid,
|
||||
Account = player.Data.Name,
|
||||
Name = player.Data.Name,
|
||||
Level = 80
|
||||
},
|
||||
NeedRename = false
|
||||
};
|
||||
|
||||
SetData(proto);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user