diff --git a/Server/Database/ServerEf.cs b/Server/Database/ServerEf.cs index e7f393c..9fb60d4 100644 --- a/Server/Database/ServerEf.cs +++ b/Server/Database/ServerEf.cs @@ -5,6 +5,7 @@ using System.Data.Entity; using System.IO; using MySql.Data.EntityFramework; using PSO2SERVER.Models; +using static PSO2SERVER.Models.Character; namespace PSO2SERVER.Database { diff --git a/Server/Models/PSO2Item.cs b/Server/Models/PSO2Item.cs index 0dff497..132de52 100644 --- a/Server/Models/PSO2Item.cs +++ b/Server/Models/PSO2Item.cs @@ -2,27 +2,62 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using static PSO2SERVER.Models.Character; namespace PSO2SERVER.Models { - /* [StructLayout(LayoutKind.Sequential, Pack = 1)] - public unsafe struct PSO2ItemConsumable + public unsafe struct ShortItemId + { + byte ItemType; + byte Id; + ushort Subid; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct ItemId + { + ushort ItemType; + ushort Id; + ushort Unk3; + ushort Subid; + } + + [StructLayout(LayoutKind.Sequential)] + public struct PSO2Items { long guid; - int ID; - int subID; - short unused1; - short quantity; - fixed int unused2[9]; + ItemId id; + Items data; + } + + [StructLayout(LayoutKind.Explicit)] + public struct Items + { + [FieldOffset(0)] + public PSO2ItemNone None; + [FieldOffset(0)] + public PSO2ItemWeapon Weapon; + [FieldOffset(0)] + public PSO2ItemClothing Clothing; + [FieldOffset(0)] + public PSO2ItemConsumable Consumable; + [FieldOffset(0)] + public PSO2ItemCamo Camo; + [FieldOffset(0)] + public PSO2ItemUnit Unit; + [FieldOffset(0)] + public byte[] Unknown; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct PSO2ItemNone + { } [StructLayout(LayoutKind.Sequential, Pack = 1)] public unsafe struct PSO2ItemWeapon { - long guid; - int ID; - int subID; byte flags; byte element; byte force; @@ -31,22 +66,85 @@ namespace PSO2SERVER.Models byte unknown1; short unknown2; fixed short affixes[8]; - int potential; + uint potential; byte extend; byte unknown3; - short unknown4; - int unknown5; - int unknown6; + ushort unknown4; + uint unknown5; + uint unknown6; } - */ - public enum ItemType + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct PSO2ItemClothing { - Consumable, + ushort flags; + fixed byte unk1[0x14]; + public HSVColor Color; + fixed byte unk2[0xA]; + ushort Unk3; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct PSO2ItemConsumable + { + ushort flags; + fixed byte unk1[0x24]; + ushort amount; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct PSO2ItemCamo + { + byte unk1; + byte unk2; + byte unk3; + fixed byte unk4[0x24]; + byte unk5; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct PSO2ItemUnit + { + byte flags; + byte EnhLevel; + byte EnhPercent; + byte Unk1; + + // 使用 fixed 数组来存储附加信息 + fixed ushort Affixes[8]; // Item affix IDs (0 to 4095) + + fixed byte unk4[0x7]; + uint Potential; + + // 使用 fixed 数组来存储未知字段 + fixed byte Unk2[4]; + + uint Unk3; + ushort Unk4; + ushort Unk5; + + // 提供访问固定数组的属性 + Span AffixSpan + { + get + { + fixed (ushort* p = Affixes) + { + return new Span(p, 8); + } + } + } + } + + public enum ItemTypes + { + NoItem, Weapon, - Costume, + Clothing, + Consumable, + Camo, Unit, - Room + Unknown } [Flags] @@ -73,7 +171,7 @@ namespace PSO2SERVER.Models MemoryStream stream; //TODO - ItemType type = ItemType.Consumable; + ItemTypes type = ItemTypes.Consumable; byte[] data = new byte[Size]; public override string ToString() @@ -136,3 +234,47 @@ namespace PSO2SERVER.Models // ... } } +public static class AffixUtils +{ + public static ushort[] ReadPackedAffixes(Stream reader) + { + byte[] packed = new byte[12]; + reader.Read(packed, 0, packed.Length); + + ushort[] affixes = new ushort[8]; + for (int i = 0; i < 4; i++) + { + affixes[i * 2] = BitConverter.ToUInt16(new byte[] { packed[i * 3], (byte)((packed[i * 3 + 2] & 0xF0) >> 4) }, 0); + affixes[i * 2 + 1] = BitConverter.ToUInt16(new byte[] { packed[i * 3 + 1], (byte)(packed[i * 3 + 2] & 0xF) }, 0); + } + return affixes; + } + + public static void WritePackedAffixes(ushort[] affixes, Stream writer) + { + byte[] packed = new byte[12]; + for (int i = 0; i < 4; i++) + { + byte[] affix1 = BitConverter.GetBytes(affixes[i * 2]); + byte[] affix2 = BitConverter.GetBytes(affixes[i * 2 + 1]); + + packed[i * 3] = affix1[0]; + packed[i * 3 + 1] = affix2[0]; + packed[i * 3 + 2] = (byte)((affix1[1] << 4) | (affix2[1] & 0xF)); + } + writer.Write(packed, 0, packed.Length); + } +} + +public class PacketError : Exception +{ + public string PacketName { get; } + public string FieldName { get; } + + public PacketError(string packetName, string fieldName, Exception innerException) + : base($"Error in packet '{packetName}', field '{fieldName}'", innerException) + { + PacketName = packetName; + FieldName = fieldName; + } +} \ No newline at end of file diff --git a/Server/Packets/Handlers/11-ClientHandler/11-04-StartGame.cs b/Server/Packets/Handlers/11-ClientHandler/11-04-CharacterSelected.cs similarity index 92% rename from Server/Packets/Handlers/11-ClientHandler/11-04-StartGame.cs rename to Server/Packets/Handlers/11-ClientHandler/11-04-CharacterSelected.cs index 84ae735..54d4bdb 100644 --- a/Server/Packets/Handlers/11-ClientHandler/11-04-StartGame.cs +++ b/Server/Packets/Handlers/11-ClientHandler/11-04-CharacterSelected.cs @@ -6,9 +6,9 @@ using System.Linq; namespace PSO2SERVER.Packets.Handlers { [PacketHandlerAttr(0x11, 0x04)] - public class StartGame : PacketHandler + public class CharacterSelected : PacketHandler { - public struct StartGamePacket + public struct CharacterSelectedPacket { /// /// Selected character ID. @@ -23,7 +23,7 @@ namespace PSO2SERVER.Packets.Handlers public override void HandlePacket(Client context, byte flags, byte[] data, uint position, uint size) { var reader = new PacketReader(data, position, size); - var pkt = reader.ReadStruct(); + var pkt = reader.ReadStruct(); //Logger.Write("id {0}", charId); diff --git a/Server/Packets/Handlers/11-ClientHandler/11-05-CharacterCreate.cs b/Server/Packets/Handlers/11-ClientHandler/11-05-CharacterCreate.cs index ae7b9d4..d726367 100644 --- a/Server/Packets/Handlers/11-ClientHandler/11-05-CharacterCreate.cs +++ b/Server/Packets/Handlers/11-ClientHandler/11-05-CharacterCreate.cs @@ -19,23 +19,31 @@ namespace PSO2SERVER.Packets.Handlers if (context.User == null) return; + + PacketWriter w = new PacketWriter(); + var reader = new PacketReader(data, position, size); - //var info = string.Format("[<--] 接收到的数据 (hex): "); - //Logger.WriteHex(info, data); + var info = string.Format("[<--] 接收到的数据 (hex): "); + Logger.WriteHex(info, data); var setting = reader.ReadStruct(); - var name = reader.ReadFixedLengthUtf16(16);//玩家名称 宽字符 - - //reader.BaseStream.Seek(0x04, SeekOrigin.Current); // Padding var looks = reader.ReadStruct(); + var unk3 = reader.ReadUInt32(); var jobs = reader.ReadStruct(); + w.WriteStruct(jobs); + Logger.WriteHex(info, w.ToArray()); //Logger.WriteInternal("[CHR] {0} 创建了名为 {1} 的新角色.", context.User.Username, name); var newCharacter = new Character { + unk1 = setting.unk1, + voice_type = setting.voice_type, + unk2 = setting.unk2, + voice_pitch = setting.voice_pitch, Name = name, - Jobs = jobs, Looks = looks, + unk3 = unk3, + Jobs = jobs, Player = context.User }; @@ -56,10 +64,6 @@ namespace PSO2SERVER.Packets.Handlers } newCharacter.player_id = context.User.PlayerId; - newCharacter.unk1 = setting.unk1; - newCharacter.voice_type = setting.voice_type; - newCharacter.unk2 = setting.unk2; - newCharacter.voice_pitch = setting.voice_pitch; //Logger.Write("newCharacter.CharacterId {0} {1}", newCharacter.CharacterId, context.User.PlayerId); diff --git a/Server/Packets/Handlers/11-ClientHandler/11-3E-CharacterSpawn.cs b/Server/Packets/Handlers/11-ClientHandler/11-3E-CharacterSpawn.cs index 279fc51..62abe42 100644 --- a/Server/Packets/Handlers/11-ClientHandler/11-3E-CharacterSpawn.cs +++ b/Server/Packets/Handlers/11-ClientHandler/11-3E-CharacterSpawn.cs @@ -25,6 +25,9 @@ namespace PSO2SERVER.Packets.Handlers { var reader = new PacketReader(data); + var info = string.Format("[<--] 接收到的数据 (hex): "); + Logger.WriteHex(info, data); + reader.BaseStream.Seek(0x38, SeekOrigin.Begin); context.Character.Looks = reader.ReadStruct(); context.Character.Jobs = reader.ReadStruct(); diff --git a/Server/Packets/PSOPackets/11-ClientPacket/11-03-CharacterListPacket.cs b/Server/Packets/PSOPackets/11-ClientPacket/11-03-CharacterListPacket.cs index 406516c..138fba2 100644 --- a/Server/Packets/PSOPackets/11-ClientPacket/11-03-CharacterListPacket.cs +++ b/Server/Packets/PSOPackets/11-ClientPacket/11-03-CharacterListPacket.cs @@ -25,6 +25,44 @@ namespace PSO2SERVER.Packets.PSOPackets //所有这些数据目前都是未知的。 private int _PlayerId; + + /// + /// Available characters. + /// + public Character[] Characters { get; set; } + + //public Item[][] EquippedItems { get; set; } = new Item[10][]; + + /// + /// Character play times. + /// + public uint[] PlayTimes { get; set; } = new uint[30]; + + /// + /// Character deletion flags (flag, deletion timestamp). + /// + public (uint Flag, uint Timestamp)[] DeletionFlags { get; set; } = new (uint, uint)[30]; + + /// + /// Character ship transfer flags. + /// + public (uint Flag, uint Transfer)[] TransferFlags { get; set; } = new (uint, uint)[30]; + + /// + /// Account accessory flag (?). + /// + public ushort AccountAccessory { get; set; } + + /// + /// Login survey flag. + /// + public uint LoginSurvey { get; set; } + + /// + /// Ad flag (on global 12 star unit ad). + /// + public uint Ad { get; set; } + public CharacterListPacket(int PlayerId) { _PlayerId = PlayerId; diff --git a/Server/Server.csproj b/Server/Server.csproj index 4471f7d..7c3a322 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -371,7 +371,7 @@ - +