feat(app): FA2 -- fellowship/allegiance command routing for graphical + headless hosts

Both LiveSocialSessionBindings construction sites updated together
(LiveSessionRuntimeFactory.cs, HeadlessSessionHost.cs) so the single
GameEventWiring.WireAll registration site serves both hosts identically
(the K-slice unification). CurrentGameRuntimeCommandAdapter implements
IRuntimeFellowshipCommands/IRuntimeAllegianceCommands over the App
command bus (LiveSessionCommandRouter gains 12 new *RuntimeCmd records
+ registrations + LiveSessionCommandBindings send delegates), mirroring
DirectGameRuntimeCommandAdapter's direct-session shape including the
identical Quit leader-hand-off rule read from RuntimeFellowshipState.
CurrentGameRuntimeAdapter (the graphical IGameRuntimeView/
IGameRuntimeCommands composite) exposes the two new views/command
groups. Headless's DirectGameRuntimeCommandAdapter needed no changes --
it already implements both new interfaces from the Runtime-layer
commit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-12 01:32:18 +02:00
parent 369729f06a
commit cced83b483
8 changed files with 363 additions and 3 deletions

View file

@ -50,6 +50,21 @@ internal sealed record LiveSessionCommandBindings(
// research §2.3-§2.7. No-ops when the batched module is clean, matching
// retail's CPlayerModule::SaveToServer(force: 0).
Action SaveCharacterOptions,
// Campaign FA slice FA2 (2026-08-12): fellowship + allegiance send
// wrappers, the App-bus twin of DirectGameRuntimeCommandAdapter's
// direct session.SendXxx calls.
Action<string, bool> SendFellowshipCreate,
Action<uint> SendFellowshipRecruit,
Action<uint> SendFellowshipDismiss,
Action<bool> SendFellowshipQuit,
Action<uint> SendFellowshipAssignNewLeader,
Action<bool> SendFellowshipChangeOpenness,
Action<bool> SendFellowshipUpdateRequest,
Action<uint> SendAllegianceSwear,
Action<uint> SendAllegianceBreak,
Action<uint> SendAllegianceKick,
Action<string> SendAllegianceInfoRequest,
Action<bool> SendAllegianceUpdateRequest,
Action<string>? Log = null);
internal readonly record struct AddShortcutRuntimeCmd(ShortcutEntry Entry);
@ -91,6 +106,22 @@ internal readonly record struct ModifyGlobalSquelchRuntimeCmd(
bool Add,
uint MessageType);
// ── Fellowship / Allegiance (Campaign FA slice FA2, 2026-08-12) ────────────
internal readonly record struct FellowshipCreateRuntimeCmd(
string FellowshipName,
bool ShareXp);
internal readonly record struct FellowshipRecruitRuntimeCmd(uint TargetGuid);
internal readonly record struct FellowshipDismissRuntimeCmd(uint TargetGuid);
internal readonly record struct FellowshipQuitRuntimeCmd(bool Disband);
internal readonly record struct FellowshipAssignNewLeaderRuntimeCmd(uint NewLeaderGuid);
internal readonly record struct FellowshipChangeOpennessRuntimeCmd(bool IsOpen);
internal readonly record struct FellowshipUpdateRequestRuntimeCmd(bool PanelOpen);
internal readonly record struct AllegianceSwearRuntimeCmd(uint PatronGuid);
internal readonly record struct AllegianceBreakRuntimeCmd(uint TargetGuid);
internal readonly record struct AllegianceKickRuntimeCmd(uint VassalGuid);
internal readonly record struct AllegianceInfoRequestRuntimeCmd(string PlayerName);
internal readonly record struct AllegianceUpdateRequestRuntimeCmd(bool On);
/// <summary>
/// Owns the command surface for one exact live-session generation. The router
/// itself is the published bus, so a retained reference becomes inert before
@ -200,6 +231,43 @@ internal sealed class LiveSessionCommandRouter : ILiveSessionCommandRouting
command => SendIfActive(() => bindings.ModifyGlobalSquelch(
command.Add,
command.MessageType)));
commands.Register<FellowshipCreateRuntimeCmd>(
command => SendIfActive(() => bindings.SendFellowshipCreate(
command.FellowshipName,
command.ShareXp)));
commands.Register<FellowshipRecruitRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendFellowshipRecruit(command.TargetGuid)));
commands.Register<FellowshipDismissRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendFellowshipDismiss(command.TargetGuid)));
commands.Register<FellowshipQuitRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendFellowshipQuit(command.Disband)));
commands.Register<FellowshipAssignNewLeaderRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendFellowshipAssignNewLeader(command.NewLeaderGuid)));
commands.Register<FellowshipChangeOpennessRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendFellowshipChangeOpenness(command.IsOpen)));
commands.Register<FellowshipUpdateRequestRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendFellowshipUpdateRequest(command.PanelOpen)));
commands.Register<AllegianceSwearRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendAllegianceSwear(command.PatronGuid)));
commands.Register<AllegianceBreakRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendAllegianceBreak(command.TargetGuid)));
commands.Register<AllegianceKickRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendAllegianceKick(command.VassalGuid)));
commands.Register<AllegianceInfoRequestRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendAllegianceInfoRequest(command.PlayerName)));
commands.Register<AllegianceUpdateRequestRuntimeCmd>(
command => SendIfActive(() =>
bindings.SendAllegianceUpdateRequest(command.On)));
_commands = commands;
}