diff --git a/Command/Command/Cmd/CommandDebug.cs b/Command/Command/Cmd/CommandDebug.cs index 095e9373..276962f0 100644 --- a/Command/Command/Cmd/CommandDebug.cs +++ b/Command/Command/Cmd/CommandDebug.cs @@ -1,4 +1,5 @@ using EggLink.DanhengServer.Data; +using EggLink.DanhengServer.Data.Custom; using EggLink.DanhengServer.Internationalization; namespace EggLink.DanhengServer.Command.Command.Cmd; @@ -69,4 +70,42 @@ public class CommandDebug : ICommand player.BattleManager!.NextBattleMonsterIds.Add(monsterId); await arg.SendMsg(I18NManager.Translate("Game.Command.Debug.SetStageId")); } + + [CommandMethod("0 customP")] + public async ValueTask AddCustomPacket(CommandArg arg) + { + var conn = arg.Target; + if (conn == null) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.Notice.PlayerNotFound")); + return; + } + + if (arg.Args.Count < 2) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.Notice.InvalidArguments")); + return; + } + + var packetFilePath = arg.Args[1]; + // Load custom packet queue from file + if (!File.Exists(packetFilePath)) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.Debug.CustomPacketFileNotFound")); + return; + } + + var fileContent = await File.ReadAllTextAsync(packetFilePath); + var customPacketQueue = Newtonsoft.Json.JsonConvert.DeserializeObject(fileContent); + + if (customPacketQueue == null || customPacketQueue.Queue.Count == 0) + { + await arg.SendMsg(I18NManager.Translate("Game.Command.Debug.CustomPacketFileInvalid")); + return; + } + + conn.CustomPacketQueue.Clear(); + conn.CustomPacketQueue.AddRange(customPacketQueue.Queue); + await arg.SendMsg(I18NManager.Translate("Game.Command.Debug.CustomPacketFileLoaded")); + } } \ No newline at end of file diff --git a/Common/Data/Custom/CustomPacketQueueConfig.cs b/Common/Data/Custom/CustomPacketQueueConfig.cs new file mode 100644 index 00000000..efb8dc17 --- /dev/null +++ b/Common/Data/Custom/CustomPacketQueueConfig.cs @@ -0,0 +1,24 @@ +using EggLink.DanhengServer.Enums.Server; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace EggLink.DanhengServer.Data.Custom; + +public class CustomPacketQueueConfig +{ + public List Queue { get; set; } = []; +} + +public class PacketActionData +{ + [JsonConverter(typeof(StringEnumConverter))] + public PacketActionTypeEnum Action { get; set; } + public PacketActionParamData Param { get; set; } = new(); +} + +public class PacketActionParamData +{ + public string PacketName { get; set; } = ""; + public string PacketData { get; set; } = ""; + public bool InterruptFormalHandler { get; set; } = false; +} \ No newline at end of file diff --git a/GameServer/Server/Connection.cs b/GameServer/Server/Connection.cs index 4fcbb3d9..29d695ea 100644 --- a/GameServer/Server/Connection.cs +++ b/GameServer/Server/Connection.cs @@ -1,6 +1,8 @@ using System.Buffers; using System.Net; using System.Reflection; +using EggLink.DanhengServer.Data.Custom; +using EggLink.DanhengServer.Enums.Server; using EggLink.DanhengServer.GameServer.Game.MultiPlayer.MarbleGame; using EggLink.DanhengServer.GameServer.Game.Player; using EggLink.DanhengServer.GameServer.Server.Packet; @@ -21,6 +23,16 @@ public class Connection(KcpConversation conversation, IPEndPoint remote) : Danhe public MarbleGameRoomInstance? MarbleRoom { get; set; } public MarbleGamePlayerInstance? MarblePlayer { get; set; } + public List CustomPacketQueue { get; set; } = []; + + private PacketActionData? GetCurActionData() + { + if (CustomPacketQueue.Count == 0) return null; + var actionData = CustomPacketQueue[0]; + + return actionData; + } + public override async void Start() { Logger.Info($"New connection from {RemoteEndPoint}."); @@ -116,6 +128,43 @@ public class Connection(KcpConversation conversation, IPEndPoint remote) : Danhe private async Task HandlePacket(ushort opcode, byte[] header, byte[] payload) { + // check if it's a custom packet + var action = GetCurActionData(); + var packetName = LogMap.GetValueOrDefault(opcode); + + if (action is { Action: PacketActionTypeEnum.WaitForPacket } && action.Param.PacketName == packetName) + { + // while run action + var interrupt = action.Param.InterruptFormalHandler; + while (true) + { + switch (action.Action) + { + case PacketActionTypeEnum.SendPacket: + { + var sendPacket = new BasePacket((ushort)LogMap.FirstOrDefault(x => + x.Value == action.Param.PacketName).Key); + + sendPacket.SetData(action.Param.PacketData); + + await SendPacket(sendPacket); + break; + } + case PacketActionTypeEnum.WaitForPacket: + { + break; + } + } + + CustomPacketQueue.RemoveAt(0); + action = GetCurActionData(); + if (action == null || action.Action == PacketActionTypeEnum.WaitForPacket) break; + } + + if (interrupt) + return; + } + // Find the Handler for this opcode var handler = HandlerManager.GetHandler(opcode); if (handler != null) @@ -198,7 +247,6 @@ public class Connection(KcpConversation conversation, IPEndPoint remote) : Danhe // No handler found // get the packet name - var packetName = LogMap.GetValueOrDefault(opcode); if (packetName == null) return; var respName = packetName.Replace("Cs", "Sc").Replace("Req", "Rsp"); // Get the response packet name