using PSO2SERVER.Protocol; using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; using static PSO2SERVER.Models.CharacterStruct; namespace PSO2SERVER.Models { [StructLayout(LayoutKind.Sequential)] public unsafe struct ShortItemId { public byte ItemType; public byte Id; public ushort Subid; } [StructLayout(LayoutKind.Sequential)] public unsafe struct ItemId { public ushort ItemType; public ushort Id; public ushort Unk3; public ushort Subid; } [StructLayout(LayoutKind.Explicit)] public struct Items { [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; [FieldOffset(0)] public PSO2ItemNone None; } [StructLayout(LayoutKind.Sequential)] public struct PSO2Items { public ulong uuid; public ItemId id; public Items data; public byte[] ToByteArray() { using (var pkt = new PacketWriter()) { pkt.Write(uuid); pkt.WriteStruct(id); pkt.WriteStruct(data); return pkt.ToArray(); } } // 从字节流转换为结构体 public static PSO2Items FromByteArray(byte[] byteArray) { using (var reader = new PacketReader(byteArray)) { PSO2Items items = new PSO2Items { uuid = reader.ReadUInt64(), id = reader.ReadStruct(), data = reader.ReadStruct() }; return items; } } public ulong GetGUID() { return uuid; } public void SetGUID(ulong guid) { uuid = guid; } } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemNone { } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemWeapon { public byte flags; public byte element; public byte force; public byte grind; public byte grindPercent; public byte unknown1; public short unknown2; public fixed short affixes[8]; public uint potential; public byte extend; public byte unknown3; public ushort unknown4; public uint unknown5; public uint unknown6; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemClothing { public ushort flags; public fixed byte unk1[0x14]; public HSVColor Color; public fixed byte unk2[0xA]; public ushort Unk3; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemConsumable { public ushort flags; public fixed byte unk1[0x24]; public ushort amount; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemCamo { public byte unk1; public byte unk2; public byte unk3; public fixed byte unk4[0x24]; public byte unk5; } [StructLayout(LayoutKind.Sequential)] public unsafe struct PSO2ItemUnit { public byte flags; public byte EnhLevel; public byte EnhPercent; public byte Unk1; // 使用 fixed 数组来存储附加信息 public fixed ushort Affixes[8]; // Item affix IDs (0 to 4095) public fixed byte unk4[0x7]; public uint Potential; // 使用 fixed 数组来存储未知字段 public fixed byte Unk2[4]; public uint Unk3; public ushort Unk4; public ushort Unk5; // 提供访问固定数组的属性 public Span AffixSpan { get { fixed (ushort* p = Affixes) { return new Span(p, 8); } } } } public struct Campaign { /// Campaign ID. public uint Id; // 对应 Rust 的 u32 /// Start timestamp. public TimeSpan StartDate; // 对应 Rust 的 Duration /// End timestamp. public TimeSpan EndDate; // 对应 Rust 的 Duration /// Campaign title (固定长度 0x3E). public const int TitleLength = 0x3E; public byte[] titleBytes; public string Title { get => Encoding.ASCII.GetString(titleBytes).TrimEnd('\0'); set { titleBytes = new byte[TitleLength]; byte[] valueBytes = Encoding.ASCII.GetBytes(value); Array.Copy(valueBytes, titleBytes, Math.Min(valueBytes.Length, TitleLength)); } } /// Campaign conditions (固定长度 0x102). public const int ConditionsLength = 0x102; public byte[] conditionsBytes; public string Conditions { get => Encoding.ASCII.GetString(conditionsBytes).TrimEnd('\0'); set { conditionsBytes = new byte[ConditionsLength]; byte[] valueBytes = Encoding.ASCII.GetBytes(value); Array.Copy(valueBytes, conditionsBytes, Math.Min(valueBytes.Length, ConditionsLength)); } } // 从数据流中读取 Campaign public static Campaign FromStream(Stream stream) { using (BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, true)) { Campaign campaign = new Campaign { Id = reader.ReadUInt32(), StartDate = TimeSpan.FromTicks(reader.ReadInt64()), EndDate = TimeSpan.FromTicks(reader.ReadInt64()), }; campaign.titleBytes = reader.ReadBytes(TitleLength); campaign.conditionsBytes = reader.ReadBytes(ConditionsLength); return campaign; } } // 将 Campaign 写入数据流 public void WriteToStream(Stream stream) { using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) { writer.Write(Id); writer.Write(StartDate.Ticks); writer.Write(EndDate.Ticks); writer.Write(titleBytes); writer.Write(conditionsBytes); } } } [StructLayout(LayoutKind.Sequential)] public struct NamedId { public string Name { get; set; } public ItemId Id { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct CampaignItemDefinition { /// /// Campaign ID. /// public uint CampaignId; // Equivalent to u32 /// /// Number of items in the following array. /// public uint ItemAmount; // Equivalent to u32 /// /// Campaign items. /// public List Items; // Using List instead of fixed-size array } [StructLayout(LayoutKind.Sequential)] public struct CampaignItem { /// /// Item ID. /// public ItemId Id; // Assuming ItemId is a type, use it as-is /// /// Item amount. /// public uint Amount; // Equivalent to u32 /// /// Unknown field. /// public uint Unk; // Equivalent to u32 } [StructLayout(LayoutKind.Sequential)] public struct StorageInfo { /// /// Total space in the storage. /// public uint TotalSpace; // Equivalent to u32 /// /// Used space in the storage. /// public uint UsedSpace; // Equivalent to u32 /// /// Storage ID. /// public byte StorageId; // Equivalent to u8 /// /// Storage type (?). /// public byte StorageType; // Equivalent to u8 /// /// Is the storage locked (?). /// public byte IsLocked; // Equivalent to u8 /// /// Is the storage enabled (?). /// public byte IsEnabled; // Equivalent to u8 } [StructLayout(LayoutKind.Sequential)] public struct MaterialStorageItem { /// /// Item category. /// public ushort Id; // Equivalent to u16 /// /// Item ID. /// public ushort Subid; // Equivalent to u16 /// /// Item amount. /// public ushort Amount; // Equivalent to u16 /// /// Unknown field. /// public ushort Unk4; // Equivalent to u16 } [StructLayout(LayoutKind.Sequential)] public struct UUIDAmount { public ulong Uuid { get; set; } public ushort Amount { get; set; } public ushort Unk { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct EquipedItem { public PSO2Items Item { get; set; } public uint Unk { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct MoveStorageItemRequest { public ulong Uuid { get; set; } public byte Amount { get; set; } public byte Unk { get; set; } public ushort StorageId { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct NewStorageItem { public PSO2Items Item { get; set; } public uint StorageId { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct NewInventoryItem { public PSO2Items Item { get; set; } public ushort Amount { get; set; } public ushort IsNew { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct UpdatedItem { public ulong Uuid { get; set; } public uint NewAmount { get; set; } public uint StorageId { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct UpdatedInventoryItem { public ulong Uuid { get; set; } public ushort NewAmount { get; set; } public ushort Moved { get; set; } } [StructLayout(LayoutKind.Sequential)] public struct UpdatedStorageItem { public ulong Uuid { get; set; } public ushort NewAmount { get; set; } public ushort Moved { get; set; } public uint StorageId { get; set; } } }