feat(chat): port retail client command families
Expand the typed client-command boundary across travel, character queries, local UI and layout controls, AFK and consent, emotes, friends, squelch and filters, and fill-components. Preserve retail packet layouts and queue ownership, import the confirmation dialog, and keep authoritative social state in Core. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
5a45a7ac7f
commit
9ea579bdd0
47 changed files with 6659 additions and 99 deletions
|
|
@ -9,6 +9,7 @@ using AcDream.Core.Net;
|
|||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Core.Player;
|
||||
using AcDream.Core.Spells;
|
||||
using AcDream.Core.Social;
|
||||
using Xunit;
|
||||
|
||||
namespace AcDream.Core.Net.Tests;
|
||||
|
|
@ -142,6 +143,72 @@ public sealed class GameEventWiringTests
|
|||
Assert.Equal(new[] { 0x1500u }, wieldConfirmed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_ConfirmationRequest_UsesRetailTwoIntegerHeader()
|
||||
{
|
||||
var dispatcher = new GameEventDispatcher();
|
||||
var items = new ClientObjectTable();
|
||||
var combat = new CombatState();
|
||||
var spellbook = new Spellbook();
|
||||
var chat = new ChatLog();
|
||||
GameEvents.CharacterConfirmationRequest? received = null;
|
||||
GameEventWiring.WireAll(
|
||||
dispatcher,
|
||||
items,
|
||||
combat,
|
||||
spellbook,
|
||||
chat,
|
||||
onConfirmationRequest: request => received = request);
|
||||
|
||||
byte[] text = MakeString16L("Proceed?");
|
||||
byte[] payload = new byte[8 + text.Length];
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload, 7u);
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(4), 42u);
|
||||
text.CopyTo(payload.AsSpan(8));
|
||||
|
||||
var env = GameEventEnvelope.TryParse(WrapEnvelope(
|
||||
GameEventType.CharacterConfirmationRequest,
|
||||
payload));
|
||||
dispatcher.Dispatch(env!.Value);
|
||||
|
||||
Assert.Equal(new GameEvents.CharacterConfirmationRequest(7u, 42u, "Proceed?"), received);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_FriendsUpdate_ReplacesAuthoritativeSocialState()
|
||||
{
|
||||
var dispatcher = new GameEventDispatcher();
|
||||
var friends = new FriendsState();
|
||||
GameEventWiring.WireAll(
|
||||
dispatcher,
|
||||
new ClientObjectTable(),
|
||||
new CombatState(),
|
||||
new Spellbook(),
|
||||
new ChatLog(),
|
||||
friends: friends);
|
||||
|
||||
byte[] name = MakeString16L("Alice");
|
||||
byte[] payload = new byte[4 + 12 + name.Length + 4 + 4 + 4];
|
||||
int p = 0;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(p), 1u); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(p), 0x50000001u); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(p), 1u); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(p), 0u); p += 4;
|
||||
name.CopyTo(payload.AsSpan(p)); p += name.Length;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(p), 0u); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(payload.AsSpan(p), 0u); p += 4;
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(
|
||||
payload.AsSpan(p), (uint)FriendsUpdateType.Full);
|
||||
|
||||
var env = GameEventEnvelope.TryParse(WrapEnvelope(
|
||||
GameEventType.FriendsListUpdate, payload));
|
||||
dispatcher.Dispatch(env!.Value);
|
||||
|
||||
FriendEntry friend = Assert.Single(friends.Snapshot());
|
||||
Assert.Equal("Alice", friend.Name);
|
||||
Assert.True(friend.Online);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_PopupString_RoutesToChatLog()
|
||||
{
|
||||
|
|
@ -155,6 +222,24 @@ public sealed class GameEventWiringTests
|
|||
Assert.Equal(ChatKind.Popup, chat.Snapshot()[0].Kind);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("", "2d 4h", "You have played for 2d 4h.")]
|
||||
[InlineData("Alice", "1y 2mo", "Alice has played for 1y 2mo.")]
|
||||
public void WireAll_QueryAgeResponse_UsesRetailWording(
|
||||
string name, string age, string expected)
|
||||
{
|
||||
var (d, _, _, _, chat) = MakeAll();
|
||||
byte[] nameBytes = MakeString16L(name);
|
||||
byte[] ageBytes = MakeString16L(age);
|
||||
byte[] payload = [.. nameBytes, .. ageBytes];
|
||||
|
||||
var env = GameEventEnvelope.TryParse(
|
||||
WrapEnvelope(GameEventType.QueryAgeResponse, payload));
|
||||
d.Dispatch(env!.Value);
|
||||
|
||||
Assert.Equal(expected, chat.Snapshot().Single().Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WireAll_PlayerDescription_PopulatesLocalPlayerStateVitals()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue