using System.Buffers.Binary; namespace AcDream.Core.Net.Messages; /// /// Inbound ObjectDelete GameMessage (opcode 0xF747). /// /// /// Retail dispatch path: /// CM_Physics::DispatchSB_DeleteObject 0x006AC6A0 reads guid from /// buf+4 and instance sequence from buf+8, then calls /// SmartBox::HandleDeleteObject 0x00451EA0. ACE emits the same /// layout from GameMessageDeleteObject. /// /// public static class DeleteObject { public const uint Opcode = 0xF747u; /// /// when this delete was sourced from a /// PickupEvent (0xF74A) — the object left the 3-D world view but /// the weenie record still exists (it is moving into a container). /// (default) when sourced from DeleteObject /// (0xF747) — the weenie was destroyed and must be evicted from /// . /// Retail two-table model: PickupEvent removes from object_table; /// DeleteObject removes from weenie_object_table. /// public readonly record struct Parsed(uint Guid, ushort InstanceSequence, bool FromPickup = false); /// /// Parse a 0xF747 body. must start with the /// 4-byte opcode, matching every other parser in this namespace. /// The returned is always /// — this parser handles true destroys only. /// public static Parsed? TryParse(ReadOnlySpan body) { if (body.Length < 10) return null; uint opcode = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(0, 4)); if (opcode != Opcode) return null; uint guid = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(4, 4)); ushort instanceSequence = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(8, 2)); return new Parsed(guid, instanceSequence, FromPickup: false); } }