feat(runtime): own character selection flow

This commit is contained in:
Erik 2026-08-14 18:22:17 +02:00
parent 6c4cd2bbc6
commit 0e82cbf700
20 changed files with 2396 additions and 33 deletions

View file

@ -134,6 +134,30 @@ public sealed class SessionPlayerCompositionTests
"LiveSessionRuntimeFactory.cs")), StringComparison.Ordinal);
}
[Fact]
public void GraphicalCompositionPausesOnlyWhenCharacterSelectorIsAbsent()
{
string phase = File.ReadAllText(Path.Combine(
FindRepoRoot(),
"src",
"AcDream.App",
"Composition",
"SessionPlayerComposition.cs"));
Assert.Contains(
"AwaitCharacterSelection:",
phase,
StringComparison.Ordinal);
Assert.Contains(
"d.Options.LiveCharacterSelector is null",
phase,
StringComparison.Ordinal);
Assert.DoesNotContain(
"CharacterList.TrySelectFirstAvailable",
phase,
StringComparison.Ordinal);
}
private sealed class RetryBinding(
string name,
List<string> calls,

View file

@ -62,7 +62,7 @@ public sealed class RuntimeOptionsSessionConfigTests
}
[Fact]
public void AbsentCharacterSelectorLeavesFirstAvailableFallbackInEffect()
public void AbsentCharacterSelectorRemainsNullForGraphicalSelectionFlow()
{
var config = new SessionConfiguration { Version = 1 };
var session = new SessionDescriptor

View file

@ -173,6 +173,44 @@ public sealed class CurrentGameRuntimeAdapterTests
.ToArray());
}
[Fact]
public void AdapterBorrowsPreWorldSelectionAndCommandsEnterTheSameRuntimeOwner()
{
using var harness = new Harness(awaitCharacterSelection: true);
RuntimeGenerationToken initial = harness.Runtime.Generation;
RuntimeSessionStartResult start = harness.Runtime.Session.Start(initial);
Assert.Equal(
RuntimeSessionStartStatus.AwaitingCharacterSelection,
start.Status);
Assert.Equal(
RuntimeCharacterSelectionLifecycle.AwaitingSelection,
harness.Runtime.CharacterSelection.Snapshot.Lifecycle);
Assert.Equal(2, harness.Runtime.CharacterSelection.Snapshot.RosterCount);
RuntimeGenerationToken generation = harness.Runtime.Generation;
Assert.True(harness.Runtime.CharacterSelectionCommands.Highlight(
generation,
0x50000001u).Accepted);
Assert.True(
harness.Runtime.CharacterSelection.Snapshot.Buttons.CanRestore);
Assert.Equal(
RuntimeCommandStatus.Rejected,
harness.Runtime.CharacterSelectionCommands.Enter(generation).Status);
Assert.True(harness.Runtime.CharacterSelectionCommands.Highlight(
generation,
Harness.PlayerGuid).Accepted);
Assert.True(harness.Runtime.CharacterSelectionCommands.Enter(
generation).Accepted);
Assert.Equal(RuntimeLifecycleState.InWorld, harness.Runtime.Lifecycle.State);
Assert.Equal(
RuntimeCharacterSelectionLifecycle.InWorld,
harness.Runtime.CharacterSelection.Snapshot.Lifecycle);
Assert.Equal(Harness.PlayerGuid, harness.Identity.ServerGuid);
}
[Fact]
public void GenerationGateRejectsStaleCommandsAndStopAcknowledgesTeardown()
{
@ -698,7 +736,7 @@ public sealed class CurrentGameRuntimeAdapterTests
private readonly IDisposable _combatModeBinding;
private readonly GameRuntime _gameRuntime;
public Harness()
public Harness(bool awaitCharacterSelection = false)
{
CombatAttackOperations = new CombatAttackOperationsSlot();
CombatModeOperations = new RuntimeCombatModeOperationsSlot();
@ -758,7 +796,11 @@ public sealed class CurrentGameRuntimeAdapterTests
new SelectionTransport(() => _session?.IsInWorld == true),
new NoopInteractionMovement());
Host = CreateHost(_session, Commands, Identity);
Host = CreateHost(
_session,
Commands,
Identity,
awaitCharacterSelection);
Runtime = new CurrentGameRuntimeAdapter(
_gameRuntime,
Host,
@ -873,7 +915,8 @@ public sealed class CurrentGameRuntimeAdapterTests
private static LiveSessionHost CreateHost(
LiveSessionController controller,
RecordingCommandRouting commands,
LocalPlayerIdentityState identity)
LocalPlayerIdentityState identity,
bool awaitCharacterSelection = false)
{
Action noop = static () => { };
var reset = new LiveSessionResetBindings
@ -925,7 +968,8 @@ public sealed class CurrentGameRuntimeAdapterTests
"127.0.0.1",
9000,
"user",
"password"));
"password",
AwaitCharacterSelection: awaitCharacterSelection));
}
private static RuntimeOptions LiveOptions()
@ -1039,6 +1083,10 @@ public sealed class CurrentGameRuntimeAdapterTests
true,
true);
public void StartCharacterSelectionReceive(WorldSession session)
{
}
public void EnterWorld(WorldSession session, int activeCharacterIndex)
{
}