feat(net): port retail physics spawn and event timestamps
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>
This commit is contained in:
parent
d53fe30ffe
commit
8a5d77f7f4
50 changed files with 3809 additions and 649 deletions
|
|
@ -26,8 +26,8 @@ namespace AcDream.Core.Net.Messages;
|
|||
/// <item>u32 opcode (0xF625)</item>
|
||||
/// <item>u32 guid — target object</item>
|
||||
/// <item>ModelData block — see <see cref="CreateObject.ReadModelData"/></item>
|
||||
/// <item>u32 instanceSequence</item>
|
||||
/// <item>u32 visualDescSequence</item>
|
||||
/// <item>u16 instanceSequence</item>
|
||||
/// <item>u16 visualDescSequence (OBJDESC_TS)</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
public static class ObjDescEvent
|
||||
|
|
@ -35,11 +35,14 @@ public static class ObjDescEvent
|
|||
public const uint Opcode = 0xF625u;
|
||||
|
||||
/// <summary>
|
||||
/// One ObjDescEvent: target guid + the new ModelData. Sequence
|
||||
/// counters are read but not surfaced (subscribers don't need them
|
||||
/// — the event always carries the full new appearance).
|
||||
/// One ObjDescEvent: target guid, new ModelData, and retail's packed
|
||||
/// INSTANCE_TS/OBJDESC_TS pair.
|
||||
/// </summary>
|
||||
public readonly record struct Parsed(uint Guid, CreateObject.ModelData ModelData);
|
||||
public readonly record struct Parsed(
|
||||
uint Guid,
|
||||
CreateObject.ModelData ModelData,
|
||||
ushort InstanceSequence,
|
||||
ushort ObjDescSequence);
|
||||
|
||||
/// <summary>
|
||||
/// Parse an ObjDescEvent body (must start with the 4-byte opcode).
|
||||
|
|
@ -61,10 +64,13 @@ public static class ObjDescEvent
|
|||
|
||||
var modelData = CreateObject.ReadModelData(body, ref pos);
|
||||
|
||||
// Trailing instanceSeq + visualDescSeq are read for completeness
|
||||
// but not surfaced — subscribers re-render unconditionally on
|
||||
// every event since each carries the full appearance.
|
||||
return new Parsed(guid, modelData);
|
||||
// PhysicsTimestampPack::UnPack 0x00516F50 is exactly two u16s.
|
||||
// Reject a missing or overlong tail so cursor mistakes cannot turn
|
||||
// into an apparently valid, ungated appearance update.
|
||||
if (body.Length - pos != 4) return null;
|
||||
ushort instance = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(pos));
|
||||
ushort objDesc = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(pos + 2));
|
||||
return new Parsed(guid, modelData, instance, objDesc);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue