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:
Erik 2026-07-13 14:50:15 +02:00
parent 5a45a7ac7f
commit 9ea579bdd0
47 changed files with 6659 additions and 99 deletions

View file

@ -1272,6 +1272,153 @@ public sealed class WorldSession : IDisposable
SendGameAction(InteractRequests.BuildTeleToLifestone(seq));
}
/// <summary>Send retail marketplace recall (0x028D).</summary>
public void SendTeleportToMarketplace()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildMarketplace(seq));
}
/// <summary>Send retail full-PK arena recall (0x0027).</summary>
public void SendTeleportToPkArena()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildPkArena(seq));
}
/// <summary>Send retail PKLite arena recall (0x0026).</summary>
public void SendTeleportToPkLiteArena()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildPkLiteArena(seq));
}
/// <summary>Send retail personal-house recall (0x0262).</summary>
public void SendTeleportToHouse()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildHouseRecall(seq));
}
/// <summary>Send retail allegiance-mansion recall (0x0278).</summary>
public void SendTeleportToMansion()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildMansionRecall(seq));
}
/// <summary>Query the local character's played time (0x01C2).</summary>
public void SendQueryAge()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildQueryAge(seq));
}
/// <summary>Query the local character's creation date (0x01C4).</summary>
public void SendQueryBirth()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildQueryBirth(seq));
}
/// <summary>Reply to a server confirmation request (0x0275).</summary>
public void SendConfirmationResponse(uint confirmationType, uint contextId, bool accepted)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildConfirmationResponse(
seq, confirmationType, contextId, accepted));
}
/// <summary>Send the confirmed retail suicide action (0x0279).</summary>
public void SendSuicide()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildSuicide(seq));
}
public void SendSetAfkMode(bool away)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildSetAfkMode(seq, away));
}
public void SendSetAfkMessage(string message)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildSetAfkMessage(seq, message));
}
public void SendEmote(string message)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildEmote(seq, message));
}
public void SendAddFriend(string name)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildAddFriend(seq, name));
}
public void SendRemoveFriend(uint friendId)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildRemoveFriend(seq, friendId));
}
public void SendClearFriends()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildClearFriends(seq));
}
public void SendLegacyFriendsListRequest() =>
SendControlMessage(ClientCommandRequests.BuildLegacyFriendsCommand(0u, string.Empty));
public void SendModifyCharacterSquelch(bool add, uint characterId, string name, uint messageType)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildModifyCharacterSquelch(
seq, add, characterId, name, messageType));
}
public void SendModifyAccountSquelch(bool add, string name)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildModifyAccountSquelch(seq, add, name));
}
public void SendModifyGlobalSquelch(bool add, uint messageType)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildModifyGlobalSquelch(seq, add, messageType));
}
public void SendClearConsent()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildClearConsent(seq));
}
public void SendDisplayConsent()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildDisplayConsent(seq));
}
public void SendRemoveConsent(string name)
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildRemoveConsent(seq, name));
}
public void SendClearDesiredComponents()
{
uint seq = NextGameActionSequence();
SendGameAction(ClientCommandRequests.BuildSetDesiredComponentLevel(
seq, componentId: 0u, amount: uint.MaxValue));
}
/// <summary>Send retail ChangeCombatMode (0x0053).</summary>
public void SendChangeCombatMode(CombatMode mode)
{
@ -1517,10 +1664,20 @@ public sealed class WorldSession : IDisposable
SendGameAction(body);
}
private void SendGameMessage(byte[] gameMessageBody)
private void SendGameMessage(byte[] gameMessageBody) =>
SendGameMessage(gameMessageBody, GameMessageGroup.UIQueue);
/// <summary>
/// Retail <c>Proto_UI::SendToControl</c> path used by standalone control
/// messages such as the legacy <c>0xF7CD</c> friends request.
/// </summary>
private void SendControlMessage(byte[] gameMessageBody) =>
SendGameMessage(gameMessageBody, GameMessageGroup.ControlQueue);
private void SendGameMessage(byte[] gameMessageBody, GameMessageGroup queue)
{
var fragment = GameMessageFragment.BuildSingleFragment(
_fragmentSequence++, GameMessageGroup.UIQueue, gameMessageBody);
_fragmentSequence++, queue, gameMessageBody);
byte[] packetBody = GameMessageFragment.Serialize(fragment);
var header = new PacketHeader
{