feat(net): FA2 -- fellowship/allegiance outbound wrappers + inbound wiring

Adds the missing WorldSession.Send* link for every FA1 fellowship/
allegiance builder (SendFellowshipCreate/Quit/Dismiss/Recruit/
UpdateRequest/AssignNewLeader/ChangeOpenness, SendAllegianceSwear/
Break/Kick/UpdateRequest) and 15 new GameEventWiring.WireAll delegate
holes covering the 11 S->C fellowship/allegiance events. Delegate holes
(not state-object params) because Core.Net cannot reference
AcDream.Runtime, matching the onCharacterOptions/onConfirmationRequest
precedent.

Fixes a real bug found during implementation: GameEventDispatcher.
Dispatch invokes only the single most-recently-registered handler per
GameEventType (RegisterOwned REPLACES, it does not chain-invoke) --
contradicts the seam doc's "the dispatcher supports multiple owned
handlers per type" claim. A literal second registrar.Register call for
AllegianceInfoResponse would have silently killed the already-live
`@allegiance info` chat-text output the moment a caller supplied the
new self-gated Runtime callback. Both behaviors are folded into the
ONE existing registration instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-12 01:31:18 +02:00
parent 1bb707e248
commit 1c40104896
3 changed files with 437 additions and 1 deletions

View file

@ -2315,6 +2315,94 @@ public sealed class WorldSession : IDisposable
SendGameAction(ClientCommandRequests.BuildAllegianceInfoRequest(seq, playerName));
}
// ── Campaign FA slice FA2 (2026-08-12): fellowship + allegiance
// outbound wrappers. SocialActions/AllegianceRequests ship the byte
// builders (repaired/added in FA1); this is the missing
// NextGameActionSequence() + SendGameAction() link every other
// outbound family already has (docs/research/2026-08-11-fa-acdream-seams.md
// §3.2).
/// <summary>Send retail fellowship create (0x00A2).</summary>
public void SendFellowshipCreate(string fellowshipName, bool shareXp)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipCreate(seq, fellowshipName, shareXp));
}
/// <summary>Send retail fellowship quit / disband (0x00A3).</summary>
public void SendFellowshipQuit(bool disband)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipQuit(seq, disband));
}
/// <summary>Send retail fellowship dismiss (0x00A4).</summary>
public void SendFellowshipDismiss(uint targetGuid)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipDismiss(seq, targetGuid));
}
/// <summary>Send retail fellowship recruit (0x00A5).</summary>
public void SendFellowshipRecruit(uint targetGuid)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipRecruit(seq, targetGuid));
}
/// <summary>
/// Send retail fellowship-panel visibility declaration (0x00A6) — D4:
/// gates ACE's <c>0x02C0</c> member-vitals stream (docs/research/
/// 2026-08-11-fa-fellowship-wire.md §4.5).
/// </summary>
public void SendFellowshipUpdateRequest(bool panelOpen)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipUpdateRequest(seq, panelOpen));
}
/// <summary>Send retail fellowship leadership transfer (0x0290).</summary>
public void SendFellowshipAssignNewLeader(uint newLeaderGuid)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipAssignNewLeader(seq, newLeaderGuid));
}
/// <summary>Send retail fellowship openness toggle (0x0291).</summary>
public void SendFellowshipChangeOpenness(bool isOpen)
{
uint seq = NextGameActionSequence();
SendGameAction(SocialActions.BuildFellowshipChangeOpenness(seq, isOpen));
}
/// <summary>Send retail allegiance swear (0x001D).</summary>
public void SendAllegianceSwear(uint patronGuid)
{
uint seq = NextGameActionSequence();
SendGameAction(AllegianceRequests.BuildSwear(seq, patronGuid));
}
/// <summary>Send retail allegiance break (0x001E) — targets your own patron.</summary>
public void SendAllegianceBreak(uint targetGuid)
{
uint seq = NextGameActionSequence();
SendGameAction(AllegianceRequests.BuildBreak(seq, targetGuid));
}
/// <summary>Send retail allegiance kick (0x001E) — targets a vassal.</summary>
public void SendAllegianceKick(uint vassalGuid)
{
uint seq = NextGameActionSequence();
SendGameAction(AllegianceRequests.BuildKick(seq, vassalGuid));
}
/// <summary>Send retail allegiance-panel subscribe/unsubscribe (0x001F).</summary>
public void SendAllegianceUpdateRequest(bool on)
{
uint seq = NextGameActionSequence();
SendGameAction(AllegianceRequests.BuildAllegianceUpdateRequest(seq, on));
}
/// <summary>Send retail @hslist &lt;type&gt; (0x0270).</summary>
public void SendListAvailableHouses(uint houseType)
{