feat(runtime): FA2 -- RuntimeFellowshipState + RuntimeAllegianceState sibling J-owners

Two new sibling Runtime owners under GameRuntime per the Slice-J
pattern (D2): RuntimeFellowshipState is session-scoped (a new
RuntimeGenerationResetStage.Fellowship clears it at every generation
reset, matching the ExternalContainer precedent); RuntimeAllegianceState
survives reconnect behind a HasServerSeed-style one-way latch and
participates in NO reset stage (its data persists like a real
disconnect does not sever allegiance membership).

Fellowship: full-update REPLACE, incremental-fellow UPSERT, self-vs-
other quit/dismiss removal (self clears the whole snapshot), disband
clear, and retail's leader hand-off rule for the Quit button
(RequiresLeaderHandoffBeforeQuit -- the current leader quitting WITHOUT
disbanding must send 0x0290 AssignNewLeader before 0x00A3, lane B
§2.5/§3.6).

Allegiance: seeded by AllegianceUpdate (0x0020, always self) and,
self-gated on TargetGuid == playerGuid(), by AllegianceInfoResponse
(0x027C); wraps the FA1-assembled flat AllegianceMemberRecord list
directly (AllegianceTree was deleted at FA1 -- nothing left to wrap).

Both apply the full 8-edit J-owner template: construction + fault
points + Owner/View properties + CaptureOwnership + a new
GameRuntimeTeardownStage pair (FellowshipDisposed/AllegianceDisposed,
stage count 11->13) + RuntimeGameplayOwnershipSnapshot inclusion +
RuntimeStateCheckpoint/trace fields. IRuntimeFellowshipCommands/
IRuntimeAllegianceCommands added to IGameRuntimeCommands and
implemented on DirectGameRuntimeCommandAdapter. No IRuntimeEventObserver
member added (D2) -- consumers poll Snapshot.Revision.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-12 01:31:54 +02:00
parent 1c40104896
commit 369729f06a
19 changed files with 1949 additions and 35 deletions

View file

@ -299,6 +299,76 @@ public interface IRuntimeSocialCommands
in RuntimeSquelchCommand command);
}
// ── Fellowship / Allegiance (Campaign FA slice FA2, 2026-08-12) ────────────
/// <summary>
/// Generation-gated fellowship outbound actions. <see cref="Quit"/>
/// implements retail's leader hand-off rule itself (lane B §2.5/§3.6): the
/// current leader quitting WITHOUT disbanding sends <c>0x0290</c> (assign
/// a non-leader fellow) BEFORE <c>0x00A3</c> — see
/// <c>RuntimeFellowshipState.RequiresLeaderHandoffBeforeQuit</c>.
/// <see cref="SetPanelOpen"/> is D4's <c>0x00A6</c> panel-visibility
/// declaration, the command the fellowship page's show/hide seam calls
/// (FA3+); without it ACE freezes the roster's vitals stream at join.
/// </summary>
public interface IRuntimeFellowshipCommands
{
RuntimeCommandResult Create(
RuntimeGenerationToken expectedGeneration,
string fellowshipName,
bool shareXp);
RuntimeCommandResult Recruit(
RuntimeGenerationToken expectedGeneration,
uint targetGuid);
RuntimeCommandResult Dismiss(
RuntimeGenerationToken expectedGeneration,
uint targetGuid);
RuntimeCommandResult Quit(
RuntimeGenerationToken expectedGeneration,
bool disband);
RuntimeCommandResult AssignLeader(
RuntimeGenerationToken expectedGeneration,
uint newLeaderGuid);
RuntimeCommandResult SetOpen(
RuntimeGenerationToken expectedGeneration,
bool isOpen);
RuntimeCommandResult SetPanelOpen(
RuntimeGenerationToken expectedGeneration,
bool panelOpen);
}
/// <summary>Generation-gated allegiance outbound actions.</summary>
public interface IRuntimeAllegianceCommands
{
RuntimeCommandResult Swear(
RuntimeGenerationToken expectedGeneration,
uint patronGuid);
RuntimeCommandResult Break(
RuntimeGenerationToken expectedGeneration,
uint targetGuid);
RuntimeCommandResult Kick(
RuntimeGenerationToken expectedGeneration,
uint vassalGuid);
/// <summary><c>0x027B</c> — empty name queries self.</summary>
RuntimeCommandResult RequestInfo(
RuntimeGenerationToken expectedGeneration,
string playerName);
/// <summary><c>0x001F</c> — the allegiance panel's subscribe/unsubscribe toggle.</summary>
RuntimeCommandResult SetUpdateSubscription(
RuntimeGenerationToken expectedGeneration,
bool on);
}
public interface IGameRuntimeCommands
{
IRuntimeSessionCommands Session { get; }
@ -322,4 +392,8 @@ public interface IGameRuntimeCommands
IRuntimeCharacterCommands Character { get; }
IRuntimeSocialCommands Social { get; }
IRuntimeFellowshipCommands Fellowship { get; }
IRuntimeAllegianceCommands Allegiance { get; }
}