fix(net): stop dropping every transient string on a chat type that isn't sent
CommunicationTransientString (0x02EB) required a trailing u32 chat type after the message. The server does not send one. Because the string is padded to a four-byte boundary, the remaining length after reading it was always zero, the guard tripped, and the parser returned null for every transient string the server has ever sent. Not most. Every one. Three oracles agree there is no such field. ACE's GameEventCommunicationTransientString writes exactly one WriteString16L and stops. Retail's ClientCommunicationSystem::Handle_Communication__TransientString at 0x0057d460 takes a single PStringBase<char> argument. holtburger carries no type field for the event either. ParseTransient now returns the string. The wiring supplies chat type 0, which is ACE's ChatMessageType.Broadcast and which ACE's own LogTextTypeEnumMapper comment names "Default" — the honest stand-in for a message the server sends untyped. What retail's transient strings should actually look like is a rendering question and belongs with the chat colour work, not here. The existing round-trip test was itself appending the phantom trailing dword, which is exactly why the wrong guard looked correct for as long as it did. It is corrected to the real payload and joined by a case sweeping string lengths zero through four, so no future padding-residue assumption can hide here again. Core.Net tests go 654 to 655. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
6119364306
commit
f416c577d6
3 changed files with 51 additions and 23 deletions
|
|
@ -376,17 +376,31 @@ public sealed class GameEventDispatcherTests
|
|||
[Fact]
|
||||
public void ParseTransient_RoundTrip()
|
||||
{
|
||||
byte[] msg = MakeString16L("Your spell fizzled!");
|
||||
byte[] chatType = new byte[4];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(chatType, 5u);
|
||||
byte[] payload = new byte[msg.Length + 4];
|
||||
Buffer.BlockCopy(msg, 0, payload, 0, msg.Length);
|
||||
Buffer.BlockCopy(chatType, 0, payload, msg.Length, 4);
|
||||
// 0x02EB is a bare string. ACE's
|
||||
// GameEventCommunicationTransientString writes one WriteString16L and
|
||||
// stops; retail's Handle_Communication__TransientString takes a single
|
||||
// string argument. This test used to append a phantom trailing u32
|
||||
// chat type, which is what let the parser's wrong guard look correct.
|
||||
byte[] payload = MakeString16L("Your spell fizzled!");
|
||||
|
||||
var parsed = GameEvents.ParseTransient(payload);
|
||||
Assert.NotNull(parsed);
|
||||
Assert.Equal("Your spell fizzled!", parsed!.Value.Message);
|
||||
Assert.Equal(5u, parsed.Value.ChatType);
|
||||
string? parsed = GameEvents.ParseTransient(payload);
|
||||
|
||||
Assert.Equal("Your spell fizzled!", parsed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regression pin: the padded string consumes the whole payload, so a
|
||||
/// parser demanding four more bytes returned null for every real
|
||||
/// transient string the server has ever sent.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void ParseTransient_ExactAcePayload_IsNotDropped()
|
||||
{
|
||||
foreach (string text in new[] { "", "a", "bb", "ccc", "dddd", "Your spell fizzled!" })
|
||||
{
|
||||
byte[] payload = MakeString16L(text);
|
||||
Assert.Equal(text, GameEvents.ParseTransient(payload));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue