User reported that even with the Phase 4.9 ack pump, acdream's character still rendered to other clients as the purple loading haze. Spent another round in holtburger's references and found two more gaps in the post-EnterWorld handshake: 1. Server sends DddInterrogation (game opcode 0xF7E5) and waits for the client to acknowledge dat-list versions. We never replied. Build the canonical empty response (12 bytes: opcode + language=1 + count=0 lists) and ship it as soon as DddInterrogation arrives. 2. LoginComplete was being sent immediately after CharacterEnterWorld in Phase 4.8, which is too early — the server hasn't finished creating the player object yet so it ignores LoginComplete and the player stays in transition state. The correct trigger is the server's PlayerCreate (0xF746) game message for our character; that's when holtburger fires send_login_complete (see references/ holtburger/.../client/messages.rs::PlayerCreate handler). Wired both into ProcessDatagram. Removed the unconditional LoginComplete from the EnterWorld flow. Added a _loginCompleteSent latch so re-PlayerCreate (e.g., across portal teleports) doesn't re-fire LoginComplete during the same session. Reference repo cited per the new CLAUDE.md guidance — holtburger is the authoritative client-behavior reference. Should have looked there sooner; this would have saved the Phase 4.8 false fix. 220 tests still green. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
41 lines
1.4 KiB
C#
41 lines
1.4 KiB
C#
using AcDream.Core.Net.Packets;
|
|
|
|
namespace AcDream.Core.Net.Messages;
|
|
|
|
/// <summary>
|
|
/// Outbound reply to the server's <c>DddInterrogation</c> game message
|
|
/// (0xF7E5). Sent during the post-EnterWorld handshake; without it the
|
|
/// server keeps the client in a transitional state and other clients
|
|
/// see the character as a stationary purple loading haze.
|
|
///
|
|
/// <para>
|
|
/// Wire layout (see
|
|
/// <c>references/holtburger/crates/holtburger-protocol/src/messages/misc/types.rs::DddInterrogationResponseData</c>):
|
|
/// </para>
|
|
/// <list type="bullet">
|
|
/// <item>u32 game-message opcode = 0xF7E6 (<c>DddInterrogationResponse</c>)</item>
|
|
/// <item>u32 language = 1 (English)</item>
|
|
/// <item>u32 count = 0 (no tagged iteration lists; we acknowledge the
|
|
/// interrogation without claiming any dat-list versions). The
|
|
/// loop body is then empty.</item>
|
|
/// </list>
|
|
///
|
|
/// <para>
|
|
/// Total payload: 12 bytes. Should be sent automatically by
|
|
/// <c>WorldSession</c> in response to receiving 0xF7E5.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class DddInterrogationResponse
|
|
{
|
|
public const uint Opcode = 0xF7E6u;
|
|
public const uint EnglishLanguage = 1u;
|
|
|
|
public static byte[] Build()
|
|
{
|
|
var w = new PacketWriter(16);
|
|
w.WriteUInt32(Opcode);
|
|
w.WriteUInt32(EnglishLanguage);
|
|
w.WriteUInt32(0u); // empty TaggedIterationList vec
|
|
return w.ToArray();
|
|
}
|
|
}
|