Parse the complete PhysicsDesc plus F754/F755 packets, correct every PhysicsState bit, and gate all nine retail update channels with generation-safe immutable snapshots. Preserve ForcePosition, teleport, placement, velocity, parent, pickup, delete, and same-generation CreateObject ordering from the named client. Separate accepted logical lifecycle notifications from retained UI qualities, make GUID replacement and session reset clear every projection exactly once, and add packet, wraparound, malformed-input, parent FIFO, canonical-position, reconnect, and GUID-reuse conformance coverage. Co-Authored-By: Codex <noreply@openai.com>
48 lines
1.6 KiB
C#
48 lines
1.6 KiB
C#
using System.Buffers.Binary;
|
|
|
|
namespace AcDream.Core.Net.Messages;
|
|
|
|
/// <summary>
|
|
/// Inbound <c>PickupEvent</c> GameMessage (opcode <c>0xF74A</c>).
|
|
///
|
|
/// <para>
|
|
/// ACE emits this from <c>Player_Tracking.RemoveTrackedObject(wo, fromPickup: true)</c>
|
|
/// when a player picks up a world item — distinguishes the despawn
|
|
/// from a generic <c>0xF747 DeleteObject</c> (timeout / death /
|
|
/// out-of-LOS). Pickup removes only the object's world projection while
|
|
/// retaining its logical weenie and timestamp owner, so <see cref="WorldSession"/>
|
|
/// publishes it separately through <c>EntityPickedUp</c>.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Wire layout (ACE <c>GameMessagePickupEvent.cs</c>):
|
|
/// <code>
|
|
/// u32 0xF74A
|
|
/// u32 guid
|
|
/// u16 objectInstanceSequence
|
|
/// u16 objectPositionSequence
|
|
/// </code>
|
|
/// </para>
|
|
/// </summary>
|
|
public static class PickupEvent
|
|
{
|
|
public const uint Opcode = 0xF74Au;
|
|
|
|
public readonly record struct Parsed(
|
|
uint Guid, ushort InstanceSequence, ushort PositionSequence);
|
|
|
|
public static Parsed? TryParse(ReadOnlySpan<byte> body)
|
|
{
|
|
if (body.Length < 12)
|
|
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));
|
|
ushort positionSequence = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(10, 2));
|
|
return new Parsed(guid, instanceSequence, positionSequence);
|
|
}
|
|
}
|