From 61c58a511415ae7cdc390b2db5feab939ae11822 Mon Sep 17 00:00:00 2001 From: Longfeng Qin Date: Sun, 22 Sep 2024 11:14:48 +0800 Subject: [PATCH] =?UTF-8?q?=E9=83=A8=E5=88=86=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/Client.cs | 34 ++- Server/Models/PSOData.cs | 17 ++ Server/Models/PSOPalette.cs | 237 ++++++++++++++++++ .../{11-00-Login.cs => 11-00-SegaIDLogin.cs} | 9 +- .../21-02-FullPaletteInfoRequest.cs | 20 ++ .../11-ClientPacket/11-01-LoginDataPacket.cs | 227 ++++++++++++++++- .../19-01-SystemMessagePacket.cs | 4 +- .../21-03-FullPaletteInfoPacket.cs | 12 +- .../21-0F-NewDefaultPAsPacket.cs | 33 ++- Server/Server.csproj | 4 +- 10 files changed, 562 insertions(+), 35 deletions(-) create mode 100644 Server/Models/PSOPalette.cs rename Server/Packets/Handlers/11-ClientHandler/{11-00-Login.cs => 11-00-SegaIDLogin.cs} (98%) create mode 100644 Server/Packets/Handlers/21-PaletteHandler/21-02-FullPaletteInfoRequest.cs diff --git a/Server/Client.cs b/Server/Client.cs index ab33a22..41322e0 100644 --- a/Server/Client.cs +++ b/Server/Client.cs @@ -257,16 +257,20 @@ namespace PSO2SERVER , _server.StartTime.ToShortTimeString().Replace('/', '-').Replace(':', '-') ); - using (var stream = File.OpenWrite(filename)) + try { - if (fromClient) + using (var stream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.None)) { - stream.WriteByte(typeA); - stream.WriteByte(typeB); - stream.WriteByte(flags1); - stream.WriteByte(flags2); + if (fromClient) + { + stream.Write(new byte[] { typeA, typeB, flags1, flags2 }, 0, 4); + } + stream.Write(packet, 0, packet.Length); } - stream.Write(packet, 0, packet.Length); + } + catch (Exception ex) + { + Console.WriteLine($"记录数据包时出错: {ex.Message}"); } } @@ -290,13 +294,17 @@ namespace PSO2SERVER , _server.StartTime.ToShortTimeString().Replace('/', '-').Replace(':', '-') ); - using (var stream = File.OpenWrite(filename)) + try { - stream.WriteByte(typeA); - stream.WriteByte(typeB); - stream.WriteByte(flags1); - stream.WriteByte(flags2); - stream.Write(packet, 0, packet.Length); + using (var stream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.None)) + { + stream.Write(new byte[] { typeA, typeB, flags1, flags2 }, 0, 4); + stream.Write(packet, 0, packet.Length); + } + } + catch (Exception ex) + { + Console.WriteLine($"记录未知数据包时出错: {ex.Message}"); } } } diff --git a/Server/Models/PSOData.cs b/Server/Models/PSOData.cs index a229e56..9445f44 100644 --- a/Server/Models/PSOData.cs +++ b/Server/Models/PSOData.cs @@ -3,6 +3,8 @@ using System.Runtime.InteropServices; using System.Collections.Generic; using System.Linq; using System.Text; +using System.IO; +using PSO2SERVER.Packets; namespace PSO2SERVER.Models { @@ -43,5 +45,20 @@ namespace PSO2SERVER.Models ObjectType = type; MapID = mapid; } + public void ReadFromStream(PacketReader reader) + { + ID = reader.ReadUInt32(); + padding = reader.ReadUInt32(); // 读取填充 + ObjectType = (ObjectType)reader.ReadUInt16(); + MapID = reader.ReadUInt16(); + } + + public void WriteToStream(PacketWriter writer) + { + writer.Write(ID); + writer.Write(padding); // 写入填充 + writer.Write((UInt16)ObjectType); + writer.Write(MapID); + } } } diff --git a/Server/Models/PSOPalette.cs b/Server/Models/PSOPalette.cs new file mode 100644 index 0000000..eaf41d1 --- /dev/null +++ b/Server/Models/PSOPalette.cs @@ -0,0 +1,237 @@ +using PSO2SERVER.Packets; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static PSO2SERVER.Models.PSOPalette; + +namespace PSO2SERVER.Models +{ + public class PSOPalette + { + public struct Palette + { + public uint CurPalette; + public uint CurSubpalette; + public uint CurBook; + public WeaponPalette[] Palettes; + public SubPalette[] Subpalettes; + public List DefaultPas; + + // 构造函数 + public static Palette Create() + { + Palette palette = new Palette + { + CurPalette = 0, + CurSubpalette = 0, + CurBook = 0, + Palettes = new WeaponPalette[6], + Subpalettes = new SubPalette[6], + DefaultPas = new List() + }; + + // 初始化 Palettes + for (int i = 0; i < palette.Palettes.Length; i++) + { + palette.Palettes[i] = WeaponPalette.Create(); + } + + // 初始化 Subpalettes(根据需要可以进行自定义初始化) + for (int i = 0; i < palette.Subpalettes.Length; i++) + { + palette.Subpalettes[i] = SubPalette.Create(); // 这里可以根据需要进行初始化 + } + + return palette; + } + + public void ReadFromStream(PacketReader reader) + { + CurPalette = reader.ReadUInt32(); + CurSubpalette = reader.ReadUInt32(); + CurBook = reader.ReadUInt32(); + + for (int i = 0; i < Palettes.Length; i++) + { + Palettes[i].ReadFromStream(reader); + } + + for (int i = 0; i < Subpalettes.Length; i++) + { + Subpalettes[i].ReadFromStream(reader); + } + + int defaultPasCount = reader.ReadInt32(); // 假设先读入数量 + DefaultPas.Clear(); + for (int i = 0; i < defaultPasCount; i++) + { + DefaultPas.Add(reader.ReadUInt32()); + } + } + + public void WriteToStream(PacketWriter writer) + { + writer.Write(CurPalette); + writer.Write(CurSubpalette); + writer.Write(CurBook); + + for (int i = 0; i < Palettes.Length; i++) + { + Palettes[i].WriteToStream(writer); + } + + for (int i = 0; i < Subpalettes.Length; i++) + { + Subpalettes[i].WriteToStream(writer); + } + + writer.Write(DefaultPas.Count); + foreach (var value in DefaultPas) + { + writer.Write(value); + } + } + } + public struct PalettePA + { + /// PA ID. + public byte ID { get; set; } + /// PA category. + public byte Category { get; set; } + public byte Unk { get; set; } + /// PA level. + public byte Level { get; set; } + + public PalettePA(byte id, byte category, byte unk, byte level) + { + ID = id; + Category = category; + Unk = unk; + Level = level; + } + + public void ReadFromStream(PacketReader reader) + { + ID = reader.ReadByte(); + Category = reader.ReadByte(); + Unk = reader.ReadByte(); + Level = reader.ReadByte(); + } + + public void WriteToStream(PacketWriter writer) + { + writer.Write(ID); + writer.Write(Category); + writer.Write(Unk); + writer.Write(Level); + } + } + + public struct SubPalette + { + // // 创建 SubPalette 实例 + // var subPalette = SubPalette.Create(); + + //// 从流中读取数据 + //using (var reader = new PacketReader(yourStream)) + //{ + // subPalette.ReadFromStream(reader); + //} + + //// 将数据写入流 + //using (var writer = new PacketWriter(yourStream)) + //{ + // subPalette.WriteToStream(writer); + //} + /// Items in the subpalette. + public PalettePA[] Items { get; set; } + // 初始化数组 + public static SubPalette Create() + { + return new SubPalette { Items = new PalettePA[12] }; + } + + public void ReadFromStream(PacketReader reader) + { + for (int i = 0; i < Items.Length; i++) + { + var palettePA = new PalettePA(); + palettePA.ReadFromStream(reader); + Items[i] = palettePA; + } + } + + public void WriteToStream(PacketWriter writer) + { + foreach (var item in Items) + { + item.WriteToStream(writer); + } + } + } + + public struct WeaponPalette + { + public ulong Uuid { get; set; } + public uint Unk1 { get; set; } + public PalettePA Unk2 { get; set; } + public PalettePA Unk3 { get; set; } + public PalettePA Unk4 { get; set; } + public uint[] Unk { get; set; } // 初始化时需指定长度 + public uint PetId { get; set; } + public PalettePA[] Skills { get; set; } // 初始化时需指定长度 + + public static WeaponPalette Create() + { + return new WeaponPalette + { + Unk = new uint[3], + Skills = new PalettePA[6] + }; + } + + public void ReadFromStream(PacketReader reader) + { + Uuid = reader.ReadUInt64(); + Unk1 = reader.ReadUInt32(); + Unk2 = new PalettePA(); + Unk2.ReadFromStream(reader); + Unk3 = new PalettePA(); + Unk3.ReadFromStream(reader); + Unk4 = new PalettePA(); + Unk4.ReadFromStream(reader); + for (int i = 0; i < Unk.Length; i++) + { + Unk[i] = reader.ReadUInt32(); + } + PetId = reader.ReadUInt32(); + for (int i = 0; i < Skills.Length; i++) + { + Skills[i] = new PalettePA(); + Skills[i].ReadFromStream(reader); + } + } + + public void WriteToStream(PacketWriter writer) + { + writer.Write(Uuid); + writer.Write(Unk1); + Unk2.WriteToStream(writer); + Unk3.WriteToStream(writer); + Unk4.WriteToStream(writer); + foreach (var value in Unk) + { + writer.Write(value); + } + writer.Write(PetId); + foreach (var skill in Skills) + { + skill.WriteToStream(writer); + } + } + } + } +} diff --git a/Server/Packets/Handlers/11-ClientHandler/11-00-Login.cs b/Server/Packets/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs similarity index 98% rename from Server/Packets/Handlers/11-ClientHandler/11-00-Login.cs rename to Server/Packets/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs index 05c145d..fd5d192 100644 --- a/Server/Packets/Handlers/11-ClientHandler/11-00-Login.cs +++ b/Server/Packets/Handlers/11-ClientHandler/11-00-SegaIDLogin.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; namespace PSO2SERVER.Packets.Handlers { [PacketHandlerAttr(0x11, 0x00)] - public class Login : PacketHandler + public class SegaIDLogin : PacketHandler { public uint Unk1 { get; set; } public uint Unk2 { get; set; } @@ -167,7 +167,7 @@ namespace PSO2SERVER.Packets.Handlers //mystery.Write((uint)100); //context.SendPacket(0x11, 0x49, 0, mystery.ToArray()); - // Login response packet + // SegaIDLogin response packet if (user == null) { @@ -210,9 +210,4 @@ namespace PSO2SERVER.Packets.Handlers Japanese = 0, English = 1 } - - public class SegaIDLoginPacket - { - - } } \ No newline at end of file diff --git a/Server/Packets/Handlers/21-PaletteHandler/21-02-FullPaletteInfoRequest.cs b/Server/Packets/Handlers/21-PaletteHandler/21-02-FullPaletteInfoRequest.cs new file mode 100644 index 0000000..ce3ce4a --- /dev/null +++ b/Server/Packets/Handlers/21-PaletteHandler/21-02-FullPaletteInfoRequest.cs @@ -0,0 +1,20 @@ +using System; +using PSO2SERVER.Models; +using PSO2SERVER.Packets.PSOPackets; + +namespace PSO2SERVER.Packets.Handlers +{ + [PacketHandlerAttr(0x21, 0x02)] + class FullPaletteInfoRequest : PacketHandler + { + public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size) + { + var info = string.Format("[<--] 接收到的数据 (hex): {0} 字节", data.Length); + Logger.WriteHex(info, data); + + var Palette = PSOPalette.Palette.Create(); + + context.SendPacket(new FullPaletteInfoPacket(Palette)); + } + } +} \ No newline at end of file diff --git a/Server/Packets/PSOPackets/11-ClientPacket/11-01-LoginDataPacket.cs b/Server/Packets/PSOPackets/11-ClientPacket/11-01-LoginDataPacket.cs index 88281d6..f52736b 100644 --- a/Server/Packets/PSOPackets/11-ClientPacket/11-01-LoginDataPacket.cs +++ b/Server/Packets/PSOPackets/11-ClientPacket/11-01-LoginDataPacket.cs @@ -1,30 +1,237 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; +using Mysqlx; using PSO2SERVER.Models; namespace PSO2SERVER.Packets.PSOPackets { class LoginDataPacket : Packet { - private string blockName, error; - private uint userid; + public enum LoginStatus : UInt32 + { + /// + /// Login was successful. + /// + Success = 0, + + /// + /// Login failed. + /// + Failure = 1, + + /// + /// Undefined status. + /// + Undefined = 0xFFFFFFFF + } + + public LoginStatus Status; + public string Error; + public ObjectHeader Player; + public string BlockName; + public float Unk1; + public uint Unk2; + public uint LevelCap; + public uint LevelCap2; + public uint Unk5; + public float Unk6; + public float Unk7; + public uint Unk8; + public float Unk9; + public float Unk10; + public uint Unk11; + public float Unk12; + public uint Unk13; + public float[] Unk14; // Length: 10 + public float[] Unk15; // Length: 21 + public float Unk16; + public float Unk17; + public float[] Unk18; // Length: 9 + public uint[] Unk19; // Length: 2 + public uint Unk20; + public uint Unk21; + public float[] Unk22; // Length: 3 + public uint Unk23; + public float Unk24; + public float Unk25; + public uint Unk26; + public byte[] Unk27; // Length: 12 + public string Unk28; + public uint Unk29; + public string Unk30; + public uint Unk31; + + private string ReadFixedString(PacketReader reader, int length) + { + byte[] bytes = reader.ReadBytes(length); + return Encoding.UTF8.GetString(bytes).TrimEnd('\0'); + } + public void ReadFromStream(PacketReader reader) + { + Status = (LoginStatus)reader.ReadInt32(); + Error = ReadFixedString(reader, 32); // 0x20 = 32 + Player.ReadFromStream(reader); + BlockName = ReadFixedString(reader, 32); + Unk1 = reader.ReadSingle(); + Unk2 = reader.ReadUInt32(); + LevelCap = reader.ReadUInt32(); + LevelCap2 = reader.ReadUInt32(); + Unk5 = reader.ReadUInt32(); + Unk6 = reader.ReadSingle(); + Unk7 = reader.ReadSingle(); + Unk8 = reader.ReadUInt32(); + Unk9 = reader.ReadSingle(); + Unk10 = reader.ReadSingle(); + Unk11 = reader.ReadUInt32(); + Unk12 = reader.ReadSingle(); + Unk13 = reader.ReadUInt32(); + + Unk14 = new float[10]; + for (int i = 0; i < Unk14.Length; i++) + { + Unk14[i] = reader.ReadSingle(); + } + + Unk15 = new float[21]; + for (int i = 0; i < Unk15.Length; i++) + { + Unk15[i] = reader.ReadSingle(); + } + + Unk16 = reader.ReadSingle(); + Unk17 = reader.ReadSingle(); + + Unk18 = new float[9]; + for (int i = 0; i < Unk18.Length; i++) + { + Unk18[i] = reader.ReadSingle(); + } + + Unk19 = new uint[2]; + for (int i = 0; i < Unk19.Length; i++) + { + Unk19[i] = reader.ReadUInt32(); + } + + Unk20 = reader.ReadUInt32(); + Unk21 = reader.ReadUInt32(); + + Unk22 = new float[3]; + for (int i = 0; i < Unk22.Length; i++) + { + Unk22[i] = reader.ReadSingle(); + } + + Unk23 = reader.ReadUInt32(); + Unk24 = reader.ReadSingle(); + Unk25 = reader.ReadSingle(); + Unk26 = reader.ReadUInt32(); + Unk27 = reader.ReadBytes(12); + Unk28 = ReadFixedString(reader, 32); + Unk29 = reader.ReadUInt32(); + Unk30 = ReadFixedString(reader, 32); + Unk31 = reader.ReadUInt32(); + } + + private void WriteFixedString(PacketWriter writer, string str, int length) + { + byte[] bytes = new byte[length]; + byte[] strBytes = Encoding.UTF8.GetBytes(str); + Array.Copy(strBytes, bytes, Math.Min(strBytes.Length, length)); + writer.Write(bytes); + } + + public void WriteToStream(PacketWriter writer) + { + writer.Write((int)Status); + writer.WriteUtf16(Error, 0x8BA4, 0xB6); + + if (Player.ID == 0) + { + for (var i = 0; i < 0xEC; i++) + writer.Write((byte)0); + } + else + { + //WriteFixedString(writer, Error, 32); + Player.WriteToStream(writer); + WriteFixedString(writer, BlockName, 32); + writer.Write(Unk1); + writer.Write(Unk2); + writer.Write(LevelCap); + writer.Write(LevelCap2); + writer.Write(Unk5); + writer.Write(Unk6); + writer.Write(Unk7); + writer.Write(Unk8); + writer.Write(Unk9); + writer.Write(Unk10); + writer.Write(Unk11); + writer.Write(Unk12); + writer.Write(Unk13); + + foreach (var val in Unk14) + { + writer.Write(val); + } + + foreach (var val in Unk15) + { + writer.Write(val); + } + + writer.Write(Unk16); + writer.Write(Unk17); + + foreach (var val in Unk18) + { + writer.Write(val); + } + + foreach (var val in Unk19) + { + writer.Write(val); + } + + writer.Write(Unk20); + writer.Write(Unk21); + + foreach (var val in Unk22) + { + writer.Write(val); + } + + writer.Write(Unk23); + writer.Write(Unk24); + writer.Write(Unk25); + writer.Write(Unk26); + writer.Write(Unk27); + WriteFixedString(writer, Unk28, 32); + writer.Write(Unk29); + WriteFixedString(writer, Unk30, 32); + writer.Write(Unk31); + } + } public LoginDataPacket(string blockName, string error, uint userid) { - this.blockName = blockName; - this.error = error; - this.userid = userid; + Status = (userid == 0) ? LoginStatus.Failure : LoginStatus.Success; + Error = error; + Player.ID = userid; + Player.ObjectType = ObjectType.Player; + BlockName = blockName; } public override byte[] Build() { var resp = new PacketWriter(); - resp.Write((uint)((userid == 0) ? 1 : 0)); // Status flag: 0=success, 1=error - resp.WriteUtf16(error, 0x8BA4, 0xB6); + resp.Write((uint)Status); // Status flag: 0=success, 1=error + resp.WriteUtf16(Error, 0x8BA4, 0xB6); - if (userid == 0) + if (Player.ID == 0) { for (var i = 0; i < 0xEC; i++) resp.Write((byte)0); @@ -33,8 +240,8 @@ namespace PSO2SERVER.Packets.PSOPackets // TODO: Explore this data! Some if it seems really important. (May contain level cap setting + more) - resp.WriteStruct(new ObjectHeader(userid, ObjectType.Player)); - resp.WriteFixedLengthUtf16(blockName, 0x20); // This is right + resp.WriteStruct(Player); + resp.WriteFixedLengthUtf16(BlockName, 0x20); // This is right // Set things to "default" values; Dunno these purposes yet. resp.Write(0x42700000); //0 resp.Write(7); //4 diff --git a/Server/Packets/PSOPackets/19-LobbyPacket/19-01-SystemMessagePacket.cs b/Server/Packets/PSOPackets/19-LobbyPacket/19-01-SystemMessagePacket.cs index fa5fffa..5dea97b 100644 --- a/Server/Packets/PSOPackets/19-LobbyPacket/19-01-SystemMessagePacket.cs +++ b/Server/Packets/PSOPackets/19-LobbyPacket/19-01-SystemMessagePacket.cs @@ -20,9 +20,9 @@ namespace PSO2SERVER.Packets.PSOPackets } private readonly string _message; - private string _unk; + //private string _unk; private readonly MessageType _type; - private uint _msg_num; + //private uint _msg_num; public SystemMessagePacket(string message, MessageType type) { diff --git a/Server/Packets/PSOPackets/21-PalettePacket/21-03-FullPaletteInfoPacket.cs b/Server/Packets/PSOPackets/21-PalettePacket/21-03-FullPaletteInfoPacket.cs index 2c07dc5..7837d1d 100644 --- a/Server/Packets/PSOPackets/21-PalettePacket/21-03-FullPaletteInfoPacket.cs +++ b/Server/Packets/PSOPackets/21-PalettePacket/21-03-FullPaletteInfoPacket.cs @@ -1,16 +1,20 @@ using PSO2SERVER.Models; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; +using static PSO2SERVER.Models.PSOPalette; namespace PSO2SERVER.Packets.PSOPackets { public class FullPaletteInfoPacket : Packet { + private Palette Palette = Palette.Create(); - public FullPaletteInfoPacket() + public FullPaletteInfoPacket(Palette palette) { + this.Palette = palette; } #region implemented abstract members of Packet @@ -18,6 +22,12 @@ namespace PSO2SERVER.Packets.PSOPackets public override byte[] Build() { var pkt = new PacketWriter(); + Palette.WriteToStream(pkt); + byte[] byteArray = pkt.ToArray(); + + var info = string.Format("[-->] 发送的数据 (hex): {0} 字节", byteArray.Length); + Logger.WriteHex(info, byteArray); + return pkt.ToArray(); } diff --git a/Server/Packets/PSOPackets/21-PalettePacket/21-0F-NewDefaultPAsPacket.cs b/Server/Packets/PSOPackets/21-PalettePacket/21-0F-NewDefaultPAsPacket.cs index 11b8c32..17f82ed 100644 --- a/Server/Packets/PSOPackets/21-PalettePacket/21-0F-NewDefaultPAsPacket.cs +++ b/Server/Packets/PSOPackets/21-PalettePacket/21-0F-NewDefaultPAsPacket.cs @@ -1,6 +1,7 @@ using PSO2SERVER.Models; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; @@ -8,10 +9,40 @@ namespace PSO2SERVER.Packets.PSOPackets { public class NewDefaultPAsPacket : Packet { - public uint[] Default { get; set; } = new uint[0x1A0]; // 默认大小为0x1A0 + + public const int FixedLength = 0x1A0; // 416 bytes + public const int SeekAfter = 0x240; // 576 bytes + + public List Default { get; set; } public NewDefaultPAsPacket() { + Default = new List(FixedLength / sizeof(uint)); // 初始化为 0x1A0 / 4 + } + + public void ReadFromStream(PacketReader reader) + { + // 读取 Default + for (int i = 0; i < Default.Capacity; i++) + { + Default.Add(reader.ReadUInt32()); + } + + // 跳过填充 + reader.BaseStream.Seek(SeekAfter - FixedLength, SeekOrigin.Current); + } + + public void WriteToStream(PacketWriter writer) + { + // 写入 Default + foreach (var value in Default) + { + writer.Write(value); + } + + // 填充到 0x240 + long paddingSize = SeekAfter - FixedLength; + writer.BaseStream.Seek(paddingSize, SeekOrigin.Current); } #region implemented abstract members of Packet diff --git a/Server/Server.csproj b/Server/Server.csproj index 3506012..939243e 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -169,6 +169,7 @@ + @@ -212,6 +213,7 @@ + @@ -378,7 +380,7 @@ - +