using System.Buffers.Binary; using System.Collections.Immutable; using System.Diagnostics; using System.Net; using System.Numerics; using System.Reflection; using System.Text.Json; using AcDream.Core.Net; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using AcDream.Headless.Configuration; using AcDream.Headless.Credentials; using AcDream.Headless.Diagnostics; using AcDream.Headless.Hosting; using AcDream.Headless.Platform; using AcDream.Runtime; using AcDream.Runtime.Entities; using AcDream.Runtime.Gameplay; using AcDream.Runtime.Physics; using AcDream.Runtime.Session; using AcDream.Runtime.World; namespace AcDream.Headless.Tests; public sealed class HeadlessSessionHostTests { [Fact] public void LoginCommandsUseTheHeadlessLiveBusAndPreserveWireOrder() { var captured = new List(); var operations = new FixtureSessionOperations { GameActionCapture = body => captured.Add(body), }; using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor( loginCommands: [ "hello", "/tell Bob, secret", "/f group", "@admin raw", "/vt start", ], loginCommandDelayMs: 0), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); RuntimeSessionStartResult result = host.Start(); Assert.Equal(RuntimeSessionStartStatus.Connected, result.Status); Assert.Equal( [ ChatRequests.TalkOpcode, ChatRequests.TellOpcode, ChatRequests.ChatChannelOpcode, ChatRequests.ChatChannelOpcode, ChatRequests.TalkOpcode, ], captured.Select(ActionOpcode)); Assert.Equal( 0x00000800u, BinaryPrimitives.ReadUInt32LittleEndian(captured[2].AsSpan(12))); Assert.Equal( 0x00000002u, BinaryPrimitives.ReadUInt32LittleEndian(captured[3].AsSpan(12))); } [Fact] public void LoginCommandsRouteWireOnlyClientCommandsWithExactPolarityAndOrder() { var captured = new List(); var operations = new FixtureSessionOperations { GameActionCapture = body => captured.Add(body), }; using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor( loginCommands: [ "/permit add Aunt Agatha", "@permit remove Lord Gnarly Beard", "/chat on", "/chat off", "/notell on", "/notell off", ], loginCommandDelayMs: 0), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); Assert.Equal(RuntimeSessionStartStatus.Connected, host.Start().Status); Assert.Equal( [ ClientCommandRequests.AddPlayerPermissionOpcode, ClientCommandRequests.RemovePlayerPermissionOpcode, ClientCommandRequests.ModifyGlobalSquelchOpcode, ClientCommandRequests.ModifyGlobalSquelchOpcode, ClientCommandRequests.ModifyGlobalSquelchOpcode, ClientCommandRequests.ModifyGlobalSquelchOpcode, ], captured.Select(ActionOpcode)); Assert.Equal("Aunt Agatha", StringActionArgument(captured[0])); Assert.Equal("Lord Gnarly Beard", StringActionArgument(captured[1])); Assert.Equal( [ (Add: 0u, MessageType: 2u), (Add: 1u, MessageType: 2u), (Add: 1u, MessageType: 3u), (Add: 0u, MessageType: 3u), ], captured.Skip(2).Select(static body => ( Add: BinaryPrimitives.ReadUInt32LittleEndian( body.AsSpan(12, sizeof(uint))), MessageType: BinaryPrimitives.ReadUInt32LittleEndian( body.AsSpan(16, sizeof(uint)))))); } [Theory] [InlineData("/permit add")] [InlineData("/chat maybe")] [InlineData("/notell maybe")] public void InvalidWireOnlyArgumentKeepsTypedFeedbackWithoutStatusFailure( string invalidCommand) { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-wire-client-errors-{Guid.NewGuid():N}.jsonl"); try { var captured = new List(); var operations = new FixtureSessionOperations { GameActionCapture = body => captured.Add(body), }; using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor( statusFile: statusPath, loginCommands: [ invalidCommand, "after", ], loginCommandDelayMs: 0), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); Assert.Single(captured); Assert.Equal("after", TalkText(captured[0])); // Invalid registered-command arguments are handled exactly as // typed: retail's ClientLocal refusal reaches canonical Runtime // feedback and is not misclassified as a transport failure. host.Runtime.CommunicationOwner.SpewBox.Tick(0d); Assert.Equal( "That is not a valid command.", Assert.Single( host.Runtime.CommunicationOwner.SpewBox.Snapshot()).Text); JsonElement[] events = File.ReadAllLines(statusPath) .Select(static line => JsonDocument.Parse(line).RootElement.Clone()) .ToArray(); Assert.DoesNotContain( events, static item => item.GetProperty("e").GetString() == "loginCommandFailed"); Assert.True(host.Runtime.Session.IsInWorld); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } [Fact] public void LoginCommandFailuresAreVersionedOrderedAndSessionIsolated() { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-login-commands-{Guid.NewGuid():N}.jsonl"); try { var captured = new List(); var operations = new FixtureSessionOperations { GameActionCapture = body => captured.Add(body), }; using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor( statusFile: statusPath, loginCommands: ["/", "/version", "after"], loginCommandDelayMs: 0), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); RuntimeSessionStartResult result = host.Start(); Assert.Equal(RuntimeSessionStartStatus.Connected, result.Status); Assert.True(host.Runtime.Session.IsInWorld); Assert.Single(captured); Assert.Equal(ChatRequests.TalkOpcode, ActionOpcode(captured[0])); JsonElement[] events = File.ReadAllLines(statusPath) .Select(static line => JsonDocument.Parse(line).RootElement.Clone()) .ToArray(); Assert.Equal( [ "started", "connected", "characterList", "enteredWorld", "loginCommandFailed", "loginCommandFailed", ], events.Select(static item => item.GetProperty("e").GetString())); JsonElement[] failures = events .Where(static item => item.GetProperty("e").GetString() == "loginCommandFailed") .ToArray(); Assert.Equal(1, failures[0].GetProperty("v").GetInt32()); Assert.Equal(0, failures[0].GetProperty("commandIndex").GetInt32()); Assert.Equal("/", failures[0].GetProperty("command").GetString()); Assert.Equal( "Chat command routing returned UnknownCommand.", failures[0].GetProperty("error").GetString()); Assert.Equal(1, failures[1].GetProperty("commandIndex").GetInt32()); Assert.Equal( "/version", failures[1].GetProperty("command").GetString()); Assert.Contains( "not available in the headless host", failures[1].GetProperty("error").GetString()); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } [Fact] public void LoginCommandDelayIsGenerationScopedAcrossReconnect() { var time = new ManualTimeProvider(); var captured = new List(); var operations = new FixtureSessionOperations { GameActionCapture = body => captured.Add(body), }; using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor( loginCommands: ["first", "second"], loginCommandDelayMs: 500), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations, timeProvider: time); Assert.Equal(RuntimeSessionStartStatus.Connected, host.Start().Status); Assert.Equal(["first"], captured.Select(TalkText)); host.Tick(0.1d); Assert.Equal(["first"], captured.Select(TalkText)); // Replacement cancels the retiring generation's pending "second" // and starts the configured list once for the new entered-world edge. Assert.Equal(RuntimeSessionStartStatus.Connected, host.Reconnect().Status); Assert.Equal(["first", "first"], captured.Select(TalkText)); time.Advance(TimeSpan.FromMilliseconds(499)); host.Tick(0.1d); Assert.Equal(["first", "first"], captured.Select(TalkText)); time.Advance(TimeSpan.FromMilliseconds(1)); host.Tick(0.1d); Assert.Equal(["first", "first", "second"], captured.Select(TalkText)); } [Fact] public void SingleSessionStartsReconnectsAndConvergesWithoutPresentation() { var operations = new FixtureSessionOperations(); using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); RuntimeSessionStartResult first = host.Start(); ulong firstGeneration = host.Runtime.Generation.Value; host.Tick(0.015d); RuntimeSessionStartResult second = host.Reconnect(); Assert.Equal(RuntimeSessionStartStatus.Connected, first.Status); Assert.Equal(RuntimeSessionStartStatus.Connected, second.Status); Assert.Equal(0x50000002u, first.CharacterId); Assert.Equal("Headless", host.ActiveCharacterName); Assert.True(host.Runtime.Session.IsInWorld); Assert.True(host.Runtime.Generation.Value > firstGeneration); Assert.Equal(2, operations.CreatedSessionCount); Assert.Equal(1, operations.DisposedSessionCount); host.Dispose(); Assert.Equal(2, operations.DisposedSessionCount); Assert.True(host.Runtime.CaptureOwnership().IsConverged); Assert.True(credential.IsDisposed); string diagnostics = diagnosticsOutput.ToString(); Assert.Contains("\"state\":\"start-result\"", diagnostics); Assert.DoesNotContain("password", diagnostics); Assert.DoesNotContain("AcDream.App", diagnostics); } /// /// Campaign LA LA1 review fixes F3/F6: reconnect is a visible lifecycle /// replacement, so the retiring connection must publish disconnected /// before the new connection publishes connected. Its reason is distinct /// from the final host stop and from the terminal process outcome. /// [Fact] public void ReconnectPublishesDisconnectedBeforeTheSecondConnectedEdge() { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-reconnect-status-{Guid.NewGuid():N}.jsonl"); try { var operations = new FixtureSessionOperations(); using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(statusFile: statusPath), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); Assert.Equal( RuntimeSessionStartStatus.Connected, host.Reconnect().Status); host.Dispose(); JsonElement[] events = File.ReadAllLines(statusPath) .Select(static line => JsonDocument.Parse(line).RootElement.Clone()) .ToArray(); Assert.Equal( [ "started", "connected", "characterList", "enteredWorld", "disconnected", "connected", "characterList", "enteredWorld", "disconnected", "exited", ], events.Select(static item => item.GetProperty("e").GetString())); JsonElement[] disconnected = events .Where(static item => item.GetProperty("e").GetString() == "disconnected") .ToArray(); Assert.Equal(2, disconnected.Length); Assert.Equal( "reconnect", disconnected[0].GetProperty("reason").GetString()); Assert.Equal( "stopped", disconnected[1].GetProperty("reason").GetString()); JsonElement exited = events[^1]; Assert.Equal(0, exited.GetProperty("code").GetInt32()); Assert.Equal("graceful", exited.GetProperty("reason").GetString()); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } /// /// Campaign LA slice LA1: proves the status-event writer fires the /// pinned lifecycle vocabulary — started/connected/characterList/ /// enteredWorld/disconnected/exited — in order, from a real /// start+dispose cycle, and that the /// roster surfaced matches /// exactly (before selection has happened — the roster is reported for /// BOTH candidates, not just the selected one). /// [Fact] public void StatusFileReceivesThePinnedLifecycleEventsInOrder() { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-status-{Guid.NewGuid():N}.jsonl"); try { var operations = new FixtureSessionOperations(); using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(statusFile: statusPath), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); RuntimeSessionStartResult started = host.Start(); Assert.Equal(RuntimeSessionStartStatus.Connected, started.Status); host.Dispose(); string[] lines = File.ReadAllLines(statusPath); string[] eventNames = lines .Select(line => JsonDocument.Parse(line) .RootElement.GetProperty("e").GetString()!) .ToArray(); Assert.Equal( [ "started", "connected", "characterList", "enteredWorld", "disconnected", "exited", ], eventNames); using JsonDocument characterListDoc = JsonDocument.Parse( lines[Array.IndexOf(eventNames, "characterList")]); JsonElement characterList = characterListDoc.RootElement; Assert.Equal("account", characterList.GetProperty("accountName").GetString()); Assert.Equal(2, characterList.GetProperty("characters").GetArrayLength()); using JsonDocument enteredWorldDoc = JsonDocument.Parse( lines[Array.IndexOf(eventNames, "enteredWorld")]); Assert.Equal( 0x50000002u, enteredWorldDoc.RootElement.GetProperty("characterId").GetUInt32()); using JsonDocument exitedDoc = JsonDocument.Parse( lines[Array.IndexOf(eventNames, "exited")]); Assert.Equal(0, exitedDoc.RootElement.GetProperty("code").GetInt32()); string contents = File.ReadAllText(statusPath); Assert.DoesNotContain("password", contents, StringComparison.Ordinal); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } [Fact] public void AbsentStatusFileConstructsANoOpWriter() { var operations = new FixtureSessionOperations(); using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret("fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); RuntimeSessionStartResult started = host.Start(); Assert.Equal(RuntimeSessionStartStatus.Connected, started.Status); // No exception, and (implicitly) no file was ever touched — the // writer is a permanent no-op with no configured path. } /// /// Campaign LA slice LA2: a probe-mode session's status stream reports /// started/connected/characterList and then converges straight to /// exited(reason:"probe", code:0) — never enteredWorld — and the /// underlying operations fake proves EnterWorld was literally never /// called (not merely that no wire message happened to arrive). /// [Fact] public void ProbeSessionEmitsRosterThenExitsSuccessfullyWithoutEnteringWorld() { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-probe-status-{Guid.NewGuid():N}.jsonl"); try { var operations = new FixtureSessionOperations(); using var diagnosticsOutput = new StringWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( ProbeDescriptor(statusFile: statusPath), credential, new HeadlessDiagnosticWriter(diagnosticsOutput), operations); RuntimeSessionStartResult started = host.Start(); Assert.Equal(RuntimeSessionStartStatus.ProbeComplete, started.Status); Assert.Equal(0, operations.EnterWorldCallCount); Assert.False(host.Runtime.Session.IsInWorld); host.Dispose(); Assert.Equal(0, operations.EnterWorldCallCount); Assert.True(host.Runtime.CaptureOwnership().IsConverged); string[] lines = File.ReadAllLines(statusPath); string[] eventNames = lines .Select(line => JsonDocument.Parse(line) .RootElement.GetProperty("e").GetString()!) .ToArray(); Assert.DoesNotContain("enteredWorld", eventNames); Assert.Contains("characterList", eventNames); Assert.Contains("exited", eventNames); Assert.True( Array.IndexOf(eventNames, "characterList") < Array.IndexOf(eventNames, "exited"), "characterList must land before the terminal exited event."); using JsonDocument exitedDoc = JsonDocument.Parse( lines[Array.IndexOf(eventNames, "exited")]); Assert.Equal(0, exitedDoc.RootElement.GetProperty("code").GetInt32()); Assert.Equal( "probe", exitedDoc.RootElement.GetProperty("reason").GetString()); string contents = File.ReadAllText(statusPath); Assert.DoesNotContain("password", contents, StringComparison.Ordinal); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } /// /// Campaign LA slice LA2: /// maps a ProbeComplete start to /// (0) rather than — a /// single-session probe-only process must exit cleanly and promptly /// without ever needing SIGINT/cancellation, because /// ProbeHeadlessBotPolicy reports IsComplete immediately. /// [Fact] public async Task ProcessHostMapsProbeCompleteStartToSuccessExitCode() { var configuration = new HeadlessConfiguration { Version = 1, Sessions = [ ProbeDescriptor( provider: HeadlessCredentialProviderKind.StandardInput, credentialReference: "probe-password"), ], }; HeadlessPathSet paths = HeadlessPathSet.Resolve( new HeadlessPathOverrides()); using var diagnostics = new StringWriter(); var operations = new FixtureSessionOperations(); using var host = new HeadlessProcessHost( configuration, paths, new System.IO.StringReader("probe-password" + Environment.NewLine), diagnostics, operations); // Deliberately NOT cancelled — a probe-only process must return on // its own; a hang here would mean the scheduler never recognized // the probe session as already complete. using var cancellation = new CancellationTokenSource( TimeSpan.FromSeconds(10)); HeadlessExitCode result = await host.RunAsync(cancellation.Token); Assert.Equal(HeadlessExitCode.Success, result); Assert.Equal(0, operations.EnterWorldCallCount); Assert.False(cancellation.IsCancellationRequested); } /// /// Campaign LA LA2 review fix: configured probe intent is not proof of a /// completed probe. If the connected session produces no CharacterList, /// Runtime returns NoCharacters, the process returns ConnectionError, and /// the sole terminal status event reports that same non-success instead of /// the former false code-0/reason-probe pair. /// [Fact] public async Task ProbeWithoutRosterReportsTheProcessConnectionErrorExactlyOnce() { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-probe-no-roster-{Guid.NewGuid():N}.jsonl"); try { var configuration = new HeadlessConfiguration { Version = 1, Sessions = [ ProbeDescriptor( provider: HeadlessCredentialProviderKind.StandardInput, credentialReference: "probe-password", statusFile: statusPath), ], }; var operations = new FixtureSessionOperations { Characters = null, }; using var diagnostics = new StringWriter(); using var host = new HeadlessProcessHost( configuration, HeadlessPathSet.Resolve(new HeadlessPathOverrides()), new System.IO.StringReader( "probe-password" + Environment.NewLine), diagnostics, operations); HeadlessExitCode result = await host.RunAsync( CancellationToken.None); Assert.Equal(HeadlessExitCode.ConnectionError, result); Assert.Equal(0, operations.EnterWorldCallCount); Assert.Equal(1, operations.DisposedSessionCount); host.Dispose(); host.Dispose(); string[] lines = File.ReadAllLines(statusPath); JsonElement[] events = lines .Select(static line => JsonDocument.Parse(line).RootElement.Clone()) .ToArray(); Assert.Equal( ["started", "connected", "disconnected", "exited"], events.Select(static item => item.GetProperty("e").GetString())); Assert.DoesNotContain( events, static item => item.GetProperty("e").GetString() == "characterList"); JsonElement exited = Assert.Single( events, static item => item.GetProperty("e").GetString() == "exited"); Assert.Equal( (int)result, exited.GetProperty("code").GetInt32()); Assert.Equal( "connection-error", exited.GetProperty("reason").GetString()); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } /// /// Campaign LA slice LA2: a probe session completing must not tear down /// a sibling play session sharing the same process — the process exit /// code is 0 only once every configured session has succeeded (the /// probe counts as success the instant it completes; the play session /// keeps running until cancellation). /// [Fact] public async Task ProbeSessionSharingAProcessDoesNotTearDownASiblingPlaySession() { var configuration = new HeadlessConfiguration { Version = 1, Sessions = [ ProbeDescriptor( "probe-sibling", provider: HeadlessCredentialProviderKind.StandardInput, credentialReference: "probe-password"), Descriptor( HeadlessCredentialProviderKind.StandardInput, "play-password"), ], }; HeadlessPathSet paths = HeadlessPathSet.Resolve( new HeadlessPathOverrides()); using var diagnostics = new StringWriter(); var operations = new FixtureSessionOperations(); using var host = new HeadlessProcessHost( configuration, paths, new System.IO.StringReader( "probe-password" + Environment.NewLine + "play-password" + Environment.NewLine), diagnostics, operations); using var cancellation = new CancellationTokenSource(); cancellation.Cancel(); HeadlessExitCode result = await host.RunAsync(cancellation.Token); Assert.Equal(HeadlessExitCode.Success, result); Assert.Equal(2, host.Sessions.Count); HeadlessSessionHost probeSession = Assert.Single( host.Sessions, s => s.SessionId == "probe-sibling"); HeadlessSessionHost playSession = Assert.Single( host.Sessions, s => s.SessionId == "bot"); Assert.False(probeSession.Runtime.Session.IsInWorld); Assert.True(playSession.Runtime.Session.IsInWorld); Assert.False(playSession.IsFaulted); } /// /// Campaign LA slice LA2: the configured idle policy follows the /// normal play shape (selector + policy, mode absent), enters world, and /// remains non-terminal through real scheduler turns until cancellation. /// Cancellation stops the process loop; the owning host's ordinary /// disposal transaction then performs graceful session teardown. Status /// events must describe those boundaries truthfully and remain exactly /// once even when disposal is repeated. /// [Fact] public async Task IdlePolicyEntersWorldRunsUntilCancellationAndConvergesExactlyOnce() { string statusPath = Path.Combine( Path.GetTempPath(), $"acdream-headless-idle-status-{Guid.NewGuid():N}.jsonl"); try { var configuration = new HeadlessConfiguration { Version = 1, Sessions = [ Descriptor( HeadlessCredentialProviderKind.StandardInput, "stdin-bot", statusFile: statusPath), ], }; HeadlessPathSet paths = HeadlessPathSet.Resolve( new HeadlessPathOverrides()); using var diagnostics = new StringWriter(); var operations = new FixtureSessionOperations(); using var host = new HeadlessProcessHost( configuration, paths, new System.IO.StringReader( "process-password" + Environment.NewLine), diagnostics, operations); using var cancellation = new CancellationTokenSource(); Task run = host.RunAsync(cancellation.Token); var timeout = Stopwatch.StartNew(); while (operations.TickCallCount < 3 && !run.IsCompleted && timeout.Elapsed < TimeSpan.FromSeconds(10)) { await Task.Delay(5); } Assert.True( operations.TickCallCount >= 3, $"Expected at least 3 idle scheduler turns, observed {operations.TickCallCount}."); Assert.False(run.IsCompleted); Assert.Equal(1, operations.EnterWorldCallCount); Assert.Equal("Headless", host.Session.ActiveCharacterName); Assert.True(host.Session.Runtime.Session.IsInWorld); Assert.False(host.Session.IsPolicyComplete); Assert.Equal( ["started", "connected", "characterList", "enteredWorld"], ReadStatusEventNames(statusPath)); cancellation.Cancel(); HeadlessExitCode result = await run.WaitAsync( TimeSpan.FromSeconds(10)); Assert.Equal(HeadlessExitCode.Success, result); // RunAsync owns scheduling, not the host lifetime. The session // remains honestly connected until its owner disposes it. Assert.True(host.Session.Runtime.Session.IsInWorld); Assert.Equal( ["started", "connected", "characterList", "enteredWorld"], ReadStatusEventNames(statusPath)); host.Dispose(); host.Dispose(); Assert.True(host.Session.Runtime.CaptureOwnership().IsConverged); Assert.Equal(1, operations.DisposedSessionCount); string[] lines = File.ReadAllLines(statusPath); string[] eventNames = ReadStatusEventNames(statusPath); Assert.Equal( [ "started", "connected", "characterList", "enteredWorld", "disconnected", "exited", ], eventNames); using JsonDocument disconnected = JsonDocument.Parse( lines[Array.IndexOf(eventNames, "disconnected")]); Assert.Equal( "stopped", disconnected.RootElement.GetProperty("reason").GetString()); using JsonDocument exited = JsonDocument.Parse( lines[Array.IndexOf(eventNames, "exited")]); JsonElement exit = exited.RootElement; Assert.Equal(0, exit.GetProperty("code").GetInt32()); string? exitReason = exit.GetProperty("reason").GetString(); Assert.False(string.IsNullOrWhiteSpace(exitReason)); Assert.NotEqual("fault", exitReason); Assert.NotEqual("probe", exitReason); Assert.DoesNotContain( "process-password", File.ReadAllText(statusPath), StringComparison.Ordinal); Assert.DoesNotContain( "process-password", diagnostics.ToString(), StringComparison.Ordinal); } finally { if (File.Exists(statusPath)) File.Delete(statusPath); } } [Fact] public async Task DirectCredentialsOverrideSingleConfiguredSession() { var configuration = new HeadlessConfiguration { Version = 1, Sessions = [Descriptor()], }; HeadlessPathSet paths = HeadlessPathSet.Resolve( new HeadlessPathOverrides()); using var diagnostics = new StringWriter(); var operations = new FixtureSessionOperations(); using var host = new HeadlessProcessHost( configuration, paths, TextReader.Null, diagnostics, operations, directCredentials: new HeadlessDirectCredentials( "direct-account", "direct-secret")); using var cancellation = new CancellationTokenSource(); cancellation.Cancel(); HeadlessExitCode result = await host.RunAsync(cancellation.Token); Assert.Equal(HeadlessExitCode.Success, result); Assert.Equal("direct-account", operations.LastUser); Assert.Equal("direct-secret", operations.LastPassword); Assert.DoesNotContain( "direct-secret", diagnostics.ToString()); } [Fact] public async Task DirectCredentialsPreserveDeclaredCharacterOptions() { // MF-1 (Campaign OP OP7 review fix, 2026-08-11): the K3 direct-CLI // launch mode (--user/--password) rebuilds the configured session // descriptor via HeadlessProcessHost.WithAccount before constructing // HeadlessSessionHost. A hand-copied clone that forgets a property // silently drops that feature for this launch mode with no error — // exactly what happened to CharacterOptions. Assert the declared // block survives the direct-credential path by checking the // constructed session's seeder actually saw it. var configuration = new HeadlessConfiguration { Version = 1, Sessions = [ Descriptor(characterOptions: new Dictionary { ["AutoRepeatAttack"] = true, }), ], }; HeadlessPathSet paths = HeadlessPathSet.Resolve( new HeadlessPathOverrides()); using var diagnostics = new StringWriter(); var operations = new FixtureSessionOperations(); using var host = new HeadlessProcessHost( configuration, paths, TextReader.Null, diagnostics, operations, directCredentials: new HeadlessDirectCredentials( "direct-account", "direct-secret")); using var cancellation = new CancellationTokenSource(); cancellation.Cancel(); HeadlessExitCode result = await host.RunAsync(cancellation.Token); Assert.Equal(HeadlessExitCode.Success, result); Assert.Equal("direct-account", operations.LastUser); Assert.NotNull(host.Session.OptionsSeeder); Assert.True(host.Session.OptionsSeeder!.HasDeclaredOptions); } [Fact] public void DirectFirstEntryCompletion_InvokesOnLoginCompleteSentHookThroughProductionWiring() { // SF-4 (Campaign OP OP7 review fix, 2026-08-11): the THIRD // production LoginComplete->seeder hook lives entirely inside // HeadlessSessionHost.CreateEventRoute's own closure (the direct, // non-portal first-entry completion callback wired to // RuntimeFirstEntryDriveController via HeadlessSessionEventRoute's // localPlayerCompleted parameter) — driving it end-to-end would // need a real DAT-backed collision fixture this test project does // not have. Reflection reaches the SAME closure instance // HeadlessSessionHost actually constructed during Start() (not a // hand-rolled reconstruction of it) and invokes it exactly as // RuntimeFirstEntryDriveController would on residence completion, // then proves the seeder reacted through the real wire — the same // "declared option actually sends" proof // HeadlessCharacterOptionsSeederWiringTests uses for the other two // sites. var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(characterOptions: new Dictionary { ["IgnoreAllegianceRequests"] = true, }), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); Assert.NotNull(host.OptionsSeeder); WorldSession session = operations.Sessions[^1]; var sent = new List(); session.GameActionCapture = body => sent.Add(body); object eventRoute = typeof(HeadlessSessionHost) .GetField( "_eventRoute", BindingFlags.NonPublic | BindingFlags.Instance)! .GetValue(host) ?? throw new InvalidOperationException( "HeadlessSessionHost constructed no event route."); var localPlayerCompleted = (Action?)typeof(HeadlessSessionEventRoute) .GetField( "_localPlayerCompleted", BindingFlags.NonPublic | BindingFlags.Instance)! .GetValue(eventRoute); Assert.NotNull(localPlayerCompleted); const uint playerGuid = 0x50000009u; host.Runtime.PlayerIdentity.ServerGuid = playerGuid; RuntimeEntityRecord record = host.Runtime.EntityObjects .RegisterEntity(Spawn(playerGuid)) .Canonical!; // Invoke the PRODUCTION closure directly — proves // HeadlessSessionHost really wires _optionsSeeder?.NoteLoginCompleteSent() // into this callback, not merely that some test double does. localPlayerCompleted!(record); Assert.Contains( sent, body => body.SequenceEqual(GameActionLoginComplete.Build())); // No PlayerDescription has landed yet — HasServerSeed is still // false, so nothing beyond LoginComplete can have sent. Assert.DoesNotContain( sent, body => ActionOpcode(body) == SocialActions.SetSingleCharacterOptionOpcode); session.GameEvents.Dispatch( GameEventEnvelope.TryParse( WrapPlayerDescriptionEnvelope(options1: 0u, options2: 0u))! .Value); Assert.Contains( sent, body => ActionOpcode(body) == SocialActions.SetSingleCharacterOptionOpcode); } [Fact] public void DirectFrameUsesSharedRetailOrderAndMovementCadence() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); HydrateGroundedPlayer(host.Runtime); var sent = new List<(byte[] Body, double Time)>(); var trace = new RuntimeTraceRecorder(); using IDisposable subscription = host.Runtime.Subscribe(trace); operations.Sessions[^1].GameActionCapture = body => sent.Add(( body, host.Runtime.Clock.SimulationTimeSeconds)); RuntimeCommandResult intent = host.Commands.Movement.SetIntent( host.Runtime.Generation, new MovementInput(Forward: true, Run: true)); host.Tick(0.015d); Assert.True(intent.Accepted); Assert.Contains( trace.Entries, static entry => entry.Kind == RuntimeTraceKind.Movement); Assert.Equal( [ MoveToState.MoveToStateAction, AutonomousPosition.AutonomousPositionAction, ], sent.Select(static entry => ActionOpcode(entry.Body)).ToArray()); sent.Clear(); for (int index = 0; index < 70; index++) host.Tick(0.015d); (byte[] Body, double Time)[] positions = sent .Where(static entry => ActionOpcode(entry.Body) == AutonomousPosition.AutonomousPositionAction) .ToArray(); Assert.InRange(positions.Length, 1, 2); Assert.True(positions[^1].Time >= 1d); if (positions.Length == 2) { Assert.True( positions[1].Time - positions[0].Time >= 0.99d); } Assert.DoesNotContain( sent, entry => ActionOpcode(entry.Body) == MoveToState.MoveToStateAction); } [Fact] public void TeardownRetriesOnlyTheUnfinishedSuffix() { var operations = new FixtureSessionOperations(); var writer = new FailOnceTextWriter(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(writer), operations); Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); writer.FailNextWrite = true; Assert.Throws(host.Dispose); Assert.Equal(1, operations.DisposedSessionCount); host.Dispose(); Assert.Equal(1, operations.DisposedSessionCount); Assert.True(host.Runtime.CaptureOwnership().IsConverged); } /// /// B5(a) review fix: /// proved the underlying re-offer MECHANISM works, but hand-constructed /// directly and called /// route.RetryPending() itself — it never touches /// 's own /// _eventRoute?.RetryPending() call. This test drives Tick /// itself (via the placementSinkOverride test seam added for this /// fix, mirroring the existing policyOverride parameter) so a /// regression that deletes or reorders that exact line would fail HERE, /// not just in the lower-level subscription test. /// [Fact] public void TickRetriesAPreviouslyDeclinedPlacementThroughTheRealEventRoute() { const uint remote = 0x70004301u; const uint landblock = 0xA9B40000u; const uint cell = landblock | 0x0001u; const float height = 6f; var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); var sink = new DecliningThenAcceptingPlacementSink(); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations, placementSinkOverride: sink); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); runtime.EntityObjects.Physics.ObserveLocalWorldFrame( cell, teleportAdvanced: false); runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( landblock, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( landblock, 1UL, ready: true); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntity(Spawn(remote, cell)) .Canonical!; runtime.EntityObjects.Entities.SetFinalPhysicsState( record, PhysicsStateFlags.Gravity); runtime.EntityObjects.Entities.SetFullCell( record, cell, landblock); var body = new PhysicsBody { Position = new Vector3(10f, 10f, height), Orientation = Quaternion.Identity, LastUpdateTime = 1d, State = PhysicsStateFlags.Gravity, TransientState = TransientStateFlags.Active, }; body.SnapToCell(cell, body.Position, body.Position); runtime.EntityObjects.Entities.SetPhysicsBody(record, body); record.ObjectClock.Activate(); runtime.EntityObjects.Physics.AcknowledgeSpatialProjection( record, spatial: true); RuntimeEntityPlacementToken token = runtime.EntityObjects.Physics .SetPosition.TryBeginExclusiveAuthoredPlacement( record, record.PositionAuthorityVersion, RuntimeSetPositionOperationKind.RemoteAuthoritative); Assert.True(token.IsValid); RuntimeSetPositionMoverPreparationStatus status = runtime.EntityObjects .Physics.SetPosition.TryPrepareAndSubmitAuthoredPlacement( record, token, RuntimeSetPositionOperationKind.RemoteAuthoritative, PhysicsSetPositionFlags.Teleport | PhysicsSetPositionFlags.Slide, new LoadedSetupCollisionSource(), gameTime: runtime.Clock.SimulationTimeSeconds, out RuntimeSetPositionOutcome outcome, resolveWorldOffsetFromRuntimeFrame: true); Assert.Equal(RuntimeSetPositionMoverPreparationStatus.Prepared, status); Assert.Equal( RuntimeSetPositionStatus.CommittedHostAcknowledgementPending, outcome.Status); // The production HeadlessSessionEventRoute's subscription attached // during host.Start() already observed this Place synchronously — // the fake sink is still declining, so it must remain unacknowledged. Assert.Equal(1, sink.CallCount); Assert.True( runtime.EntityObjects.Physics.SetPosition.TryPeekProjection( out _)); // The sink starts accepting (mirrors a landblock finishing streaming // in) — driving ONE real host tick is what must re-offer the head, // through Tick's own wiring, not a hand-built route. sink.Accept = true; host.Tick(0.015d); Assert.Equal(2, sink.CallCount); Assert.False( runtime.EntityObjects.Physics.SetPosition.TryPeekProjection( out _)); } private sealed class DecliningThenAcceptingPlacementSink : IRuntimePlacementProjectionSink { internal int CallCount { get; private set; } internal bool Accept { get; set; } public bool TryApply(in RuntimePlacementProjectionSnapshot projection) { CallCount++; return Accept; } } [Fact] public void WorldProjectionHydratesCanonicalMovementAndTeleportState() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; // C3c: conductor-driven flow — a live generation admits the initial // residence, the flat landblock's collision generation commits, and // the world projection's pump drives the local first-entry conductor // to completion (the deleted SynchronizeLocalPlayer hand-copy's // replacement). Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000002u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new FixtureCollisionNeighborhood(); // R5/A7 review fix (2026-08-05): the drive controller is now wired // (was omitted in the first pass, leaving the canonical portal arm // a no-op by construction here and dual-host parity with zero // coverage). Mirrors production composition // (HeadlessSessionHost.cs's own construction order): the SAME // RuntimeAcceptedPositionDriveController drives both hosts through // the identical TryExecuteAcceptedPortalArrival entry point. RuntimeAcceptedPositionDriveController acceptedPositionDrive = CreateAcceptedPositionDrive(runtime); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry, acceptedPositionDrive); projection.ProjectSpawn(record, isLocalPlayer: true); PlayerMovementController controller = Assert.IsType( runtime.MovementOwner.Controller); controller.SeedPlacementForTest( new Vector3(48f, 49f, 50f), 0xA9B40001u, new Vector3(48f, 49f, 50f)); projection.ProjectPosition( record, isLocalPlayer: true, PositionTimestampDisposition.Apply); Assert.Same(controller, runtime.MovementOwner.Controller); Assert.Equal(record.LocalEntityId, controller.LocalEntityId); Assert.Equal(new Vector3(48f, 49f, 50f), controller.Position); Assert.Equal( 0xA9B40000u, controller.CellId & 0xFFFF0000u); Assert.True((controller.CellId & 0xFFFFu) < 0x0100u); projection.BeginTeleport(); Assert.Equal(PlayerState.PortalSpace, controller.State); // R5/A7: the destination cell 0xA9B40001 is in the SAME landblock // (0xA9B40000) whose collision generation the test already // committed above, so the canonical portal arm resolves // Committed synchronously - no DeferredCell park needed to exercise // the real headless placement path. A1's headless retry loop // (RuntimeLiveEntitySessionController.PumpPortalCompletion) is // covered separately by // HeadlessPortalDeferredCellCommitsOnPumpAfterCollisionGenerationWake // below. RuntimeDestinationReadiness readiness = projection.PrepareDestination( revealGeneration: 7, new RuntimeTeleportDestination( player, InstanceSequence: 1, PositionSequence: 2, TeleportSequence: 1, ForcePositionSequence: 0, new Position( 0xA9B40001u, new Vector3(96f, 97f, 50f), Quaternion.Identity)), // R5/A7: a real, valid token - `default` was fine for the // old no-op arm but RuntimePortalPlacementAuthority.IsValid // now genuinely gates TryExecuteAcceptedPortalArrival on it. new RuntimeWorldHostProjectionToken(7, 0xA9B40001u)); Assert.True(readiness.IsCollisionReady); Assert.False(readiness.IsUnhydratable); Assert.Equal(PlayerState.InWorld, controller.State); Assert.Equal(2, collision.CenterCount); Assert.Equal(0xA9B40001u, collision.LastCell); // A4/dual-host parity: the canonical placement actually committed - // the body moved to the destination Position, not just the // collision-neighborhood bookkeeping that CenterCount/LastCell // alone would have proven even with the earlier no-op arm. Z // settles 0.005 above the wire value (the foot sphere's bottom // sits at origin + 0.475 - 0.48, LoadedSetupCollisionSource's own // doc comment, ISSUES.md #285) - X/Y are exact, Z is asserted // within that settle tolerance. Assert.Equal(96f, controller.Position.X); Assert.Equal(97f, controller.Position.Y); Assert.Equal(50f, controller.Position.Z, 0.01f); // The resolved outdoor sub-cell index is derived from X/Y within // the landblock (not the wire placeholder 0xA9B40001), same as the // FIRST ProjectPosition assertion above (":406-409") only checks // landblock+indoor-vs-outdoor, not the exact sub-cell. Assert.Equal(0xA9B40000u, controller.CellId & 0xFFFF0000u); Assert.True((controller.CellId & 0xFFFFu) < 0x0100u); } /// /// A1/R5/A7 review fix (2026-08-05): headless has no per-frame anim /// sequencer the way the graphical host does, so its OWN equivalent of /// A1's "hold until committed" mechanism is /// HeadlessSessionWorldProjection.PrepareDestination's /// _awaitingPortalWake polling. This proves it end to end: a /// destination in a landblock whose collision generation is NOT yet /// committed parks (IsCollisionReady: false, body unmoved, no /// throw — DeferredCell is a normal headless outcome per /// PrepareDestination's own doc), and once the destination /// landblock's collision generation commits, the SAME park resolves on /// a later attempt WITHOUT a second concurrent Begin (Runtime's own /// Begin would refuse that with Contention if this class re-attempted /// blindly instead of polling PendingCount). /// [Fact] public void HeadlessPortalPrepareDestinationParksThenCommitsOnCollisionGenerationWake() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000009u; const uint destinationLandblock = 0xAAB40000u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new FixtureCollisionNeighborhood(); RuntimeAcceptedPositionDriveController acceptedPositionDrive = CreateAcceptedPositionDrive(runtime); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry, acceptedPositionDrive); projection.ProjectSpawn(record, isLocalPlayer: true); PlayerMovementController controller = Assert.IsType( runtime.MovementOwner.Controller); controller.SeedPlacementForTest( new Vector3(48f, 49f, 50f), 0xA9B40001u, new Vector3(48f, 49f, 50f)); projection.ProjectPosition( record, isLocalPlayer: true, PositionTimestampDisposition.Apply); projection.BeginTeleport(); var destination = new RuntimeTeleportDestination( player, InstanceSequence: 1, PositionSequence: 2, TeleportSequence: 1, ForcePositionSequence: 0, new Position( destinationLandblock | 0x0001u, new Vector3(10f, 10f, 50f), Quaternion.Identity)); var projectionToken = new RuntimeWorldHostProjectionToken( 7, destinationLandblock | 0x0001u); // Begin's own portal-vs-latest-cell gate (D-T5) requires the // destination's landblock to match the record's LATEST MERGED // Position, not just the transit's retained destination - mirror // what the real inbound Position handler already does before // TryCompletePortal ever runs (LiveEntityNetworkUpdateController's // App-side equivalent). Assert.True(runtime.EntityObjects.TryApplyPosition( new WorldSession.EntityPositionUpdate( player, new CreateObject.ServerPosition( destination.Position.ObjCellId, destination.Position.Frame.Origin.X, destination.Position.Frame.Origin.Y, destination.Position.Frame.Origin.Z, destination.Position.Frame.Orientation.W, destination.Position.Frame.Orientation.X, destination.Position.Frame.Orientation.Y, destination.Position.Frame.Orientation.Z), Velocity: null, PlacementId: null, IsGrounded: true, InstanceSequence: 1, PositionSequence: 3, TeleportSequence: destination.TeleportSequence, ForcePositionSequence: 0), isLocalPlayer: true, forcePositionRotation: Quaternion.Identity, currentLocalVelocity: Vector3.Zero, acknowledgeProjection: null, out _, out _, out _)); // First attempt: destinationLandblock's collision generation was // never begun/committed, so the canonical arm parks DeferredCell. // Must NOT throw (a park is normal, not an error). The dormant // stage (RuntimeSetPositionState's SubmitPreparedPlacementCore // deferred-commit path) already stages the body's Position/CellId // at the destination while it waits (StageDormantCellFrame, // body.InWorld=false) - the reader-visible Position moving early is // that mechanism, not evidence the placement committed; only // PlayerState/IsCollisionReady distinguish "staged" from // "committed" here. RuntimeDestinationReadiness parked = projection.PrepareDestination( revealGeneration: 7, destination, projectionToken); Assert.False(parked.IsCollisionReady); Assert.Equal(PlayerState.PortalSpace, controller.State); // A SECOND attempt while still parked must not double-Begin - // Runtime's own Begin would refuse a genuine second attempt with // Contention, but PrepareDestination's _awaitingPortalWake polls // PendingCount instead of re-attempting, so this must also report // not-ready without throwing. RuntimeDestinationReadiness stillParked = projection.PrepareDestination( revealGeneration: 7, destination, projectionToken); Assert.False(stillParked.IsCollisionReady); // Commit the destination landblock's collision generation and pump // the drive's wake (mirrors HeadlessSessionHost.Tick's own // PumpFirstEntry -> _acceptedPositionDrive.Advance() ordering). runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( destinationLandblock, 1UL); var heights = new byte[81]; Array.Fill(heights, (byte)50); var heightTable = new float[256]; for (int index = 0; index < heightTable.Length; index++) heightTable[index] = index; runtime.EntityObjects.Physics.Engine.AddLandblock( destinationLandblock, new TerrainSurface(heights, heightTable), [], [], worldOffsetX: 0f, worldOffsetY: 0f); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( destinationLandblock, 1UL, ready: true); // RuntimeAcceptedPositionDriveControllerTests.CommitLandblockCollision's // exact proven-working shape: the wake path's // resolveWorldOffsetFromRuntimeFrame requires the destination // landblock's world-frame offset to already be resolvable. runtime.EntityObjects.Physics.ObserveLocalWorldFrame( destinationLandblock | 0x0001u, teleportAdvanced: false); // Drain the placement projection FIFO AFTER committing (exact order // from RuntimeAcceptedPositionDriveControllerTests.DrainPlacementFifo's // call site: commit collision -> drain FIFO -> Advance) - the // deferred park's Withdraw notification is published as part of the // collision-generation commit, not before it. while (runtime.EntityObjects.Physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot head)) { if (!runtime.EntityObjects.Physics.SetPosition .AcknowledgeProjection(head.Token)) { break; } } acceptedPositionDrive.Advance(); RuntimeDestinationReadiness committed = projection.PrepareDestination( revealGeneration: 7, destination, projectionToken); Assert.True(committed.IsCollisionReady); Assert.Equal(PlayerState.InWorld, controller.State); Assert.Equal(10f, controller.Position.X); Assert.Equal(10f, controller.Position.Y); Assert.Equal(destinationLandblock, controller.CellId & 0xFFFF0000u); } /// /// B1 review fix (2026-08-05): headless's required test #2 — the same /// unsound-commit-inference defect /// /// proves the HAPPY path for, exercised on the FORGOTTEN path instead. A /// DeferredCell park killed by an ordinary, unrelated accepted Position /// merge (exactly the ACE 5-10 Hz broadcast RuntimeSetPositionState's /// own doc names as the expected way a far-destination park resolves /// without committing) must leave PrepareDestination reporting /// NOT ready, unchanged, and the /// body never moved to the destination - before the fix, /// PendingCount hitting 0 made PrepareDestination infer /// "committed" and run the full readiness/materialize/LoginComplete /// sequence against an unmoved body. /// [Fact] public void HeadlessPortalPrepareDestinationForgottenByOrdinaryMergeDoesNotLatchAsCommitted() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000009u; const uint destinationLandblock = 0xAAB40000u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new FixtureCollisionNeighborhood(); RuntimeAcceptedPositionDriveController acceptedPositionDrive = CreateAcceptedPositionDrive(runtime); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry, acceptedPositionDrive); projection.ProjectSpawn(record, isLocalPlayer: true); PlayerMovementController controller = Assert.IsType( runtime.MovementOwner.Controller); controller.SeedPlacementForTest( new Vector3(48f, 49f, 50f), 0xA9B40001u, new Vector3(48f, 49f, 50f)); projection.ProjectPosition( record, isLocalPlayer: true, PositionTimestampDisposition.Apply); projection.BeginTeleport(); var destination = new RuntimeTeleportDestination( player, InstanceSequence: 1, PositionSequence: 2, TeleportSequence: 1, ForcePositionSequence: 0, new Position( destinationLandblock | 0x0001u, new Vector3(10f, 10f, 50f), Quaternion.Identity)); var projectionToken = new RuntimeWorldHostProjectionToken( 7, destinationLandblock | 0x0001u); Assert.True(runtime.EntityObjects.TryApplyPosition( new WorldSession.EntityPositionUpdate( player, new CreateObject.ServerPosition( destination.Position.ObjCellId, destination.Position.Frame.Origin.X, destination.Position.Frame.Origin.Y, destination.Position.Frame.Origin.Z, destination.Position.Frame.Orientation.W, destination.Position.Frame.Orientation.X, destination.Position.Frame.Orientation.Y, destination.Position.Frame.Orientation.Z), Velocity: null, PlacementId: null, IsGrounded: true, InstanceSequence: 1, PositionSequence: 3, TeleportSequence: destination.TeleportSequence, ForcePositionSequence: 0), isLocalPlayer: true, forcePositionRotation: Quaternion.Identity, currentLocalVelocity: Vector3.Zero, acknowledgeProjection: null, out _, out _, out _)); // First attempt: destinationLandblock's collision generation was // never begun/committed, so the canonical arm parks DeferredCell. RuntimeDestinationReadiness parked = projection.PrepareDestination( revealGeneration: 7, destination, projectionToken); Assert.False(parked.IsCollisionReady); Assert.Equal(PlayerState.PortalSpace, controller.State); Assert.Equal(1, acceptedPositionDrive.PendingCount); // An ordinary, UNRELATED accepted Position for the same entity - no // new teleport, just a normal broadcast at the SAME already-accepted // teleport sequence - Forgets the parked operation the same way // ACE's 5-10 Hz cadence would (RuntimeSetPositionState.Forget, called // from TryApplyPosition for every accepted, non-Rejected Position). Assert.True(runtime.EntityObjects.TryApplyPosition( new WorldSession.EntityPositionUpdate( player, new CreateObject.ServerPosition( 0x20210001u, 48f, 49f, 50f, 1f, 0f, 0f, 0f), Velocity: null, PlacementId: null, IsGrounded: true, InstanceSequence: 1, PositionSequence: 4, TeleportSequence: destination.TeleportSequence, ForcePositionSequence: 0), isLocalPlayer: true, forcePositionRotation: Quaternion.Identity, currentLocalVelocity: Vector3.Zero, acknowledgeProjection: null, out _, out _, out _)); // Forget (inside TryApplyPosition) cancels the underlying // RuntimeSetPositionState operation immediately, but the drive's OWN // _pending cache only notices on its next Advance() pump - the real // host does this every HeadlessSessionHost.Tick via PumpFirstEntry; // the test drives it explicitly, same as the App-level equivalent. acceptedPositionDrive.Advance(); Assert.Equal(0, acceptedPositionDrive.PendingCount); // Drive well past where the pre-fix inference would have latched // "committed" on the very next PrepareDestination call and then // marched to the full readiness/materialize/LoginComplete sequence. for (int i = 0; i < 10; i++) { RuntimeDestinationReadiness stillNotReady = projection.PrepareDestination( revealGeneration: 7, destination, projectionToken); Assert.False(stillNotReady.IsCollisionReady); } Assert.Equal(PlayerState.PortalSpace, controller.State); } [Fact] public void WorldProjectionIgnoresNormalEchoButBlipsForcePosition() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; // C3c: conductor-driven flow — a live generation admits the initial // residence, the flat landblock's collision generation commits, and // the world projection's pump drives the local first-entry conductor // to completion (the deleted SynchronizeLocalPlayer hand-copy's // replacement). Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000003u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new FixtureCollisionNeighborhood(); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry); projection.ProjectSpawn(record, isLocalPlayer: true); PlayerMovementController controller = Assert.IsType( runtime.MovementOwner.Controller); controller.SeedPlacementForTest( new Vector3(48f, 49f, 50f), 0xA9B40001u, new Vector3(48f, 49f, 50f)); projection.ProjectPosition( record, isLocalPlayer: true, PositionTimestampDisposition.Apply); Assert.Equal(new Vector3(48f, 49f, 50f), controller.Position); var force = new WorldSession.EntityPositionUpdate( player, record.Snapshot.Position!.Value with { PositionX = 72f, PositionY = 73f, }, Velocity: null, PlacementId: null, IsGrounded: true, InstanceSequence: 1, PositionSequence: 2, TeleportSequence: 0, ForcePositionSequence: 1); Assert.True(runtime.EntityObjects.TryApplyPosition( force, isLocalPlayer: true, forcePositionRotation: Quaternion.Identity, currentLocalVelocity: controller.BodyVelocity, acknowledgeProjection: null, out PositionTimestampDisposition disposition, out _, out AcceptedPhysicsTimestamps timestamps)); Assert.Equal( PositionTimestampDisposition.ForcePosition, disposition); // C4 route 2 (2026-08-03): a ForcePosition on the local player no // longer routes through HeadlessSessionWorldProjection.ProjectPosition // at all — RuntimeLiveEntitySessionController.OnPositionUpdated // dispatches it directly to RuntimeAcceptedPositionDriveController // instead (the deleted BlipLocalPlayer's replacement), but R2 review // fix (2026-08-03): it re-centers the collision neighborhood on the // destination FIRST — the deleted BlipLocalPlayer's own CenterOn // side effect, restored via CenterOnAcceptedForcePosition, because a // DeferredCell park this neighborhood's window can never publish is // a dead end, not a real park // (RuntimeAcceptedPositionDriveController.Advance's R1 doc comment). projection.CenterOnAcceptedForcePosition(record); RuntimeAcceptedPositionDriveController acceptedPositionDrive = CreateAcceptedPositionDrive(runtime); RuntimeAcceptedPositionExecutionStatus forceStatus = acceptedPositionDrive.TryExecuteAcceptedLocalPosition( record, force, disposition, timestamps, timestamps.PreviousTeleport); Assert.Equal( RuntimeAcceptedPositionExecutionStatus.Committed, forceStatus); // R7 review fix (2026-08-03): Z is 50.005f — within 5 mm of the // wire's bare 50f — NOT 50.48f. The dat-exact human Setup's foot // sphere is (0,0,0.475) r=.48 (LoadedSetupCollisionSource above); // its bottom sits at origin + 0.475 − 0.48 = origin − 0.005, so a // settled origin lands 0.005 m ABOVE the floor it rests on (measured // empirically against this exact fixture), not a full sphere RADIUS // above it. Retail's BlipPlayer (CPhysicsObj::SetPositionSimple // @0x005162B0, called from SmartBox::BlipPlayer @0x00453940) has // never lifted the origin by a sphere radius — the C4-route-2 FIRST // implementation pass (uncommitted; this file asserts a bare 50f at // HEAD, never 50.48f) had fitted a 50.48f assertion to a dummy // fixture sphere whose offset happened to equal its own radius, not // to retail behavior. The comment it carried described the sphere's // CENTRE, then wrongly asserted that description about // controller.Position, which is the body's ORIGIN // (PlayerMovementController.cs -> PhysicsBody.cs Position), not the // sphere centre. Assert.Equal(new Vector3(72f, 73f, 50.005f), controller.Position); // CenterCount is 2: ProjectSpawn's initial centering plus the // ForcePosition's own re-centering above (R2's restored mechanism). Assert.Equal(2, collision.CenterCount); } /// /// C3c-R1 review F7: a remote Create whose landblock lies outside the /// bounded collision neighborhood's service window can never see its /// deferred placement's collision-generation wake — left alone it would /// pin its residence (and the drive's pending entry) for the whole /// session. The host converts it to the celless completion route before /// the pump: the residence completes with FullCell 0, the accepted wire /// frame stays on the canonical snapshot (the exact pre-flip /// accepted-frame behavior for far remotes), and every ledger converges. /// [Fact] public void FarRemoteCreateCompletesCelllessWithoutPinningItsResidence() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x5000000Bu; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); var collision = new FixtureCollisionNeighborhood(); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry); RuntimeEntityRecord playerRecord = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( playerRecord, playerRecord.CreateIntegrationVersion, playerRecord.Snapshot, replaceGeneration: false)); projection.ProjectSpawn(playerRecord, isLocalPlayer: true); const uint farRemote = 0x70000010u; const uint farCell = 0x00010001u; Assert.False(collision.IsWithinServiceWindow(farCell)); RuntimeEntityRecord remote = runtime.EntityObjects .RegisterEntityWithInitialResidence( Spawn(farRemote, farCell), isLocalPlayer: false) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( remote, remote.CreateIntegrationVersion, remote.Snapshot, replaceGeneration: false)); projection.ProjectSpawn(remote, isLocalPlayer: false); // Celless completion: no pinned residence, FullCell stays 0, the // accepted wire frame survives on the canonical snapshot. Assert.False(runtime.EntityObjects.TryGetInitialCreateResidence( remote, out _)); Assert.Equal(0u, remote.FullCellId); Assert.Equal( farCell, remote.Snapshot.Position!.Value.LandblockId); RuntimeEntityObjectOwnershipSnapshot ownership = runtime.EntityObjects.CaptureOwnership(); Assert.Equal(0, ownership.InitialCreateResidenceLeaseCount); Assert.Equal(0, ownership.FirstEntryDrivePendingCount); Assert.Equal(0, firstEntry.PendingCount); // Draining the placement FIFO the way the host subscription would // converges the completion-receipt ledger too. while (runtime.EntityObjects.Physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot head)) { if (!runtime.EntityObjects.Physics.SetPosition .AcknowledgeProjection(head.Token)) { break; } } Assert.Equal( 0, runtime.EntityObjects.CaptureOwnership() .PendingCompletionReceiptCount); } [Fact] public void PlacementReceiptValidationDoesNotRegainMovementOrPhysicsAuthority() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; // C3c: conductor-driven flow — a live generation admits the initial // residence, the flat landblock's collision generation commits, and // the world projection's pump drives the local first-entry conductor // to completion (the deleted SynchronizeLocalPlayer hand-copy's // replacement). Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000004u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var directProjection = new HeadlessSessionWorldProjection( runtime, new FixtureCollisionNeighborhood(), firstEntry); directProjection.ProjectSpawn(record, isLocalPlayer: true); PlayerMovementController controller = Assert.IsType< PlayerMovementController>(runtime.MovementOwner.Controller); Vector3 positionBefore = controller.Position; Quaternion orientationBefore = controller.BodyOrientation; RuntimePhysicsOwnershipSnapshot physicsBefore = runtime.EntityObjects.Physics.CaptureOwnership(); RuntimePlacementProjectionSnapshot receipt = Placement( runtime, record, RuntimePlacementProjectionKind.Place, new Vector3(600f, 601f, 602f), Quaternion.CreateFromAxisAngle(Vector3.UnitY, 1.2f)); var receiptSink = new HeadlessRuntimePlacementProjectionSink(runtime); Assert.True(receiptSink.TryApply(in receipt)); Assert.Equal(positionBefore, controller.Position); Assert.Equal(orientationBefore, controller.BodyOrientation); Assert.Equal( physicsBefore, runtime.EntityObjects.Physics.CaptureOwnership()); } [Fact] public void PlacementReceiptUsesExactIncarnationAndDiscardIsAckOnly() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntity(Spawn(0x50000005u)) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var projection = new HeadlessRuntimePlacementProjectionSink(runtime); RuntimePlacementProjectionSnapshot place = Placement( runtime, record, RuntimePlacementProjectionKind.Place, Vector3.One, Quaternion.Identity); RuntimePlacementProjectionSnapshot stale = place with { Token = place.Token with { Entity = place.Token.Entity with { Incarnation = unchecked((ushort)( place.Token.Entity.Incarnation + 1)), }, }, }; RuntimePlacementProjectionSnapshot discard = stale with { Kind = RuntimePlacementProjectionKind.Discard, Token = stale.Token with { SessionLifetimeVersion = ulong.MaxValue, }, }; Assert.False(projection.TryApply(in stale)); Assert.True(projection.TryApply(in discard)); } [Fact] public void ExecutorCompletedReceiptIsAcknowledgeOnlyRegardlessOfRecordValidity() { // F1: mirrors PlacementReceiptUsesExactIncarnationAndDiscardIsAckOnly's // stale-token half - proves ExecutorCompleted is acknowledged // unconditionally (never gated by the record-lookup/portal-shape // checks Place/Withdraw depend on), so a genuinely stale/mismatched // token can never wedge the FIFO behind it. var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntity(Spawn(0x50000006u)) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var sink = new HeadlessRuntimePlacementProjectionSink(runtime); RuntimePlacementProjectionSnapshot completion = Placement( runtime, record, RuntimePlacementProjectionKind.ExecutorCompleted, Vector3.One, Quaternion.Identity); RuntimePlacementProjectionSnapshot stale = completion with { Token = completion.Token with { Entity = completion.Token.Entity with { Incarnation = unchecked((ushort)( completion.Token.Entity.Incarnation + 1)), }, SessionLifetimeVersion = ulong.MaxValue, }, }; Assert.True(sink.TryApply(in completion)); Assert.True(sink.TryApply(in stale)); } [Fact] public void SessionEventRouteOwnsOneObserverAndUnsubscribesBeforeNetworkDetach() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; int subscriberCountDuringDetach = -1; var inner = new FixtureEventRoute( onDispose: () => subscriberCountDuringDetach = runtime.EntityObjects.Events.PlacementSubscriberCount); var placements = new HeadlessRuntimePlacementProjectionSink(runtime); var route = new HeadlessSessionEventRoute( inner, runtime, placements); Assert.Equal( 0, runtime.EntityObjects.Events.PlacementSubscriberCount); route.Attach(); route.Attach(); Assert.Equal( 1, runtime.EntityObjects.Events.PlacementSubscriberCount); Assert.Equal(1, inner.AttachCount); route.Dispose(); route.Dispose(); Assert.Equal(0, subscriberCountDuringDetach); Assert.Equal( 0, runtime.EntityObjects.Events.PlacementSubscriberCount); Assert.Equal(1, inner.DisposeCount); } [Fact] public void ReplacementSessionEventRouteGetsOneFreshObserver() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; var placements = new HeadlessRuntimePlacementProjectionSink(runtime); var first = new HeadlessSessionEventRoute( new FixtureEventRoute(), runtime, placements); first.Attach(); Assert.Equal( 1, runtime.EntityObjects.Events.PlacementSubscriberCount); first.Dispose(); Assert.Equal( 0, runtime.EntityObjects.Events.PlacementSubscriberCount); var replacement = new HeadlessSessionEventRoute( new FixtureEventRoute(), runtime, placements); replacement.Attach(); Assert.Equal( 1, runtime.EntityObjects.Events.PlacementSubscriberCount); replacement.Dispose(); Assert.Equal( 0, runtime.EntityObjects.Events.PlacementSubscriberCount); } [Fact] public void SessionEventRouteRetryDoesNotRestorePlacementObserver() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; var inner = new FixtureEventRoute { DisposeFailuresRemaining = 1, }; var route = new HeadlessSessionEventRoute( inner, runtime, new HeadlessRuntimePlacementProjectionSink(runtime)); route.Attach(); Assert.Throws(route.Dispose); Assert.Equal( 0, runtime.EntityObjects.Events.PlacementSubscriberCount); Assert.Equal(1, inner.DisposeCount); route.Dispose(); Assert.Equal(2, inner.DisposeCount); Assert.Equal( 0, runtime.EntityObjects.Events.PlacementSubscriberCount); } [Fact] public void CollisionTransactionCancelsPostAdmissionFaultWithoutWithdrawingActiveWorld() { using var lifetime = new RuntimeEntityObjectLifetime(); RuntimePhysicsState physics = lifetime.Physics; const uint landblockId = 0xA9B4FFFFu; CompleteCollisionGeneration( physics, landblockId, afterAdmission: null, (admission, prepared) => physics.StageCollisionAssets( admission, prepared, CollisionAssets(landblockId, 10f))); Assert.Throws(() => CompleteCollisionGeneration( physics, landblockId, _ => throw new FixtureCollisionPublicationException(), (_, _) => throw new InvalidOperationException( "Staging must not run after the injected admission fault."))); Assert.Equal(10f, physics.Engine.SampleTerrainZ(1f, 1f)); RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership(); Assert.Equal(1, ownership.LandblockCount); Assert.Equal(0, ownership.CollisionAdmissionCount); } [Fact] public void CollisionTransactionYieldsTheFirstNonterminalRuntimePoll() { using var lifetime = new RuntimeEntityObjectLifetime(); RuntimePhysicsState physics = lifetime.Physics; const uint landblockId = 0xA9B4FFFFu; HeadlessCollisionGenerationTransaction transaction = HeadlessCollisionGenerationTransaction.Begin( physics, landblockId, afterAdmission: null, (admission, prepared) => physics.StageCollisionAssets( admission, prepared, CollisionAssets(landblockId, 10f))); HeadlessCollisionGenerationAdvance advance; do { advance = transaction.Advance(); Assert.True(advance.Progressed); } while (!advance.YieldToCaller); Assert.False(advance.Completed); Assert.False(advance.WaitingForProjectionAcknowledgement); Assert.False(transaction.CompletionCommitted); do { advance = transaction.Advance(); Assert.True(advance.Progressed); } while (!advance.Completed && !advance.YieldToCaller); if (!advance.Completed) advance = transaction.Advance(); Assert.True(advance.Completed); Assert.True(transaction.EngineMutationCommitted); Assert.True(transaction.CompletionCommitted); Assert.Equal(0, physics.CaptureOwnership().CollisionAdmissionCount); } [Fact] public void CollisionTransactionRetainsPostEngineCancellationUntilPlaceAck() { using var lifetime = new RuntimeEntityObjectLifetime(); RuntimePhysicsState physics = lifetime.Physics; const uint landblockId = 0xA9B4FFFFu; CompleteCollisionGeneration( physics, landblockId, afterAdmission: null, (admission, prepared) => physics.StageCollisionAssets( admission, prepared, CollisionAssets(landblockId, 10f))); const uint guid = 0x70004201u; const uint cell = 0xA9B40001u; Vector3 position = new(10f, 10f, 0f); RuntimeEntityRecord record = lifetime.RegisterEntity( Spawn(guid)).Canonical!; lifetime.Entities.SetFinalPhysicsState(record, PhysicsStateFlags.Gravity); lifetime.Entities.SetFullCell(record, cell, landblockId); var body = new PhysicsBody { Position = position, Orientation = Quaternion.Identity, LastUpdateTime = 1d, State = PhysicsStateFlags.Gravity, TransientState = TransientStateFlags.Active, }; body.SnapToCell(cell, position, position); lifetime.Entities.SetPhysicsBody(record, body); record.ObjectClock.Activate(); physics.AcknowledgeSpatialProjection(record, spatial: true); RuntimePlacementProjectionToken seeded = SeedRuntimePlacement( physics, record, cell, position); Assert.True(physics.SetPosition.AcknowledgeProjection(seeded)); HeadlessCollisionGenerationTransaction transaction = HeadlessCollisionGenerationTransaction.Begin( physics, landblockId, afterAdmission: null, (admission, prepared) => physics.StageCollisionAssets( admission, prepared, CollisionAssets(landblockId, 20f))); HeadlessCollisionGenerationAdvance advance; do { advance = transaction.Advance(); } while (!advance.WaitingForProjectionAcknowledgement); Assert.False(transaction.EngineMutationCommitted); Assert.True(physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot withdrawal)); Assert.True(physics.SetPosition.AcknowledgeProjection(withdrawal.Token)); do { advance = transaction.Advance(); } while (!advance.WaitingForProjectionAcknowledgement); Assert.True(transaction.EngineMutationCommitted); Assert.True(physics.SetPosition.TryPeekProjection( out RuntimePlacementProjectionSnapshot placement)); Assert.False(transaction.TryCancel()); Assert.True(physics.SetPosition.AcknowledgeProjection(placement.Token)); Assert.True(transaction.TryCancel()); RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership(); Assert.Equal(0, ownership.CollisionPrefixMutationCount); Assert.Equal(0, ownership.CollisionAdmissionCount); } [Fact] public void CollisionTransactionCancelsStagingFaultWithoutWithdrawingActiveWorld() { using var lifetime = new RuntimeEntityObjectLifetime(); RuntimePhysicsState physics = lifetime.Physics; const uint landblockId = 0xA9B4FFFFu; CompleteCollisionGeneration( physics, landblockId, afterAdmission: null, (admission, prepared) => physics.StageCollisionAssets( admission, prepared, CollisionAssets(landblockId, 10f))); Assert.Throws(() => CompleteCollisionGeneration( physics, landblockId, afterAdmission: null, (admission, prepared) => { physics.StageCollisionAssets( admission, prepared, CollisionAssets(landblockId, 25f)); throw new FixtureCollisionPublicationException(); })); Assert.Equal(10f, physics.Engine.SampleTerrainZ(1f, 1f)); RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership(); Assert.Equal(1, ownership.LandblockCount); Assert.Equal(0, ownership.CollisionAdmissionCount); } /// /// #365 test 3 — the coverage gap the diagnosis doc's Q4 names /// (HeadlessSessionHostTests.cs:347-475's existing /// /// uses , which is trivially /// ready and never opens a real admission — the production /// HeadlessCollisionNeighborhood has never been exercised against /// the first-entry conductor while an admission was genuinely open). /// The player's OWN landblock is prepared the same proven way every /// other test in this file does (AddFlatLandblock + /// SetPosition.BeginCollisionGeneration/ /// CommitCollisionGeneration) so it is unconditionally resident — /// instead /// holds a SEPARATE REAL open on /// a DIFFERENT landblock, the same shape as one of the OTHER eight /// landblocks in production's real 3x3 publication plan — proving Step /// 3a's gate covers "an admission is open ANYWHERE in the plan", not /// just the center. /// /// /// Consolidated-review round (2026-08-10), NIT (c): what THIS bounded /// xunit test proves is the GATE, not the #365 hydration stall's /// closure — that broader claim's evidence is the separate live-ACE /// end-to-end run recorded in /// docs/research/2026-08-10-365-headless-hydration-diagnosis.md /// §8 ("hydration now succeeds ... entityCount reaches 136"). This /// test's own scope is narrower and fully mechanical: the conductor /// stays undriven (runtime.MovementOwner.Controller stays null) /// for as long as the unrelated admission is held open, and drives and /// reaches IsRuntimePublished within a bounded tick budget once /// it releases. Temporarily reverting the three IsQuiescent /// gates in HeadlessSessionWorldProjection.ProjectSpawn/ /// ProjectPosition/PumpFirstEntry (never committed — see /// §8's "Verification that the new test... actually discriminates") /// makes this test fail the SAME way §8 itself observed: the /// Assert.Null(runtime.MovementOwner.Controller) checks below /// fail because the controller is ALREADY built AND published /// (CanExecuteLiveMovement = True) while the unrelated admission /// is still open — not merely "reached PublicationCommitted, /// still dormant" as an earlier draft of this comment claimed; that /// wording described a weaker intermediate state than what reverting /// the gate actually produces. /// [Fact] public void RealAdmissionNeverDrivesTheConductorWhileOpenAndHydratesOnceReleased() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000012u; const uint neighborLandblockId = 0xAAB4FFFFu; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence( Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new NeighborAdmissionHeldOpenCollisionNeighborhood( runtime.EntityObjects.Physics, neighborLandblockId); collision.OpenHeldAdmission(); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry); projection.ProjectSpawn(record, isLocalPlayer: true); Assert.False(collision.IsQuiescent); Assert.Null(runtime.MovementOwner.Controller); // Hold the SEPARATE admission open across many pumps — far longer // than the conductor needs to reach mover-prep/placement/ // PublicationCommitted on the ALREADY-READY player landblock, so a // pre-fix conductor being undriven only by luck of a short window // cannot slip through. for (int tick = 0; tick < 50; tick++) { projection.PumpFirstEntry(); Assert.False(collision.IsQuiescent); Assert.Null(runtime.MovementOwner.Controller); } collision.ReleaseHeldAdmission(); Assert.True(collision.IsQuiescent); const int boundedTicks = 200; bool published = false; for (int tick = 0; tick < boundedTicks; tick++) { projection.PumpFirstEntry(); if (runtime.MovementOwner.Controller is { IsRuntimePublished: true }) { published = true; break; } } Assert.True( published, "the local player never reached RuntimePublished within the " + "bounded tick budget after the unrelated admission cleared."); PlayerMovementController controller = Assert.IsType< PlayerMovementController>(runtime.MovementOwner.Controller); Assert.True(controller.IsRuntimePublished); Assert.Equal(0, firstEntry.PendingCount); } /// /// #365 test 4: PumpFirstEntry must not call DriveAll while /// the collision neighborhood reports non-quiescent, and must call it on /// the first tick after quiescence — the exact Step 3a gate, isolated /// from the admission machinery itself via a directly-controllable fake. /// [Fact] public void PumpFirstEntryWithholdsDriveAllUntilQuiescentThenDrivesImmediately() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000013u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new GateControllableCollisionNeighborhood { QuiescentOverride = false, }; var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry); projection.ProjectSpawn(record, isLocalPlayer: true); Assert.Null(runtime.MovementOwner.Controller); Assert.Equal(1, firstEntry.PendingCount); projection.PumpFirstEntry(); Assert.Null(runtime.MovementOwner.Controller); Assert.Equal(1, firstEntry.PendingCount); collision.QuiescentOverride = true; projection.PumpFirstEntry(); Assert.NotNull(runtime.MovementOwner.Controller); } /// /// #365 test 5: HeadlessLocalPlayerFrameHost.CanAdvancePlayer /// tracks the controller's exact publication lifecycle — false while /// dormant (the crash bug's shape), true once published, false again /// once retired (the #356 lifecycle-caller idiom). /// [Fact] public void CanAdvancePlayerReflectsControllerPublicationLifecycle() { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); Assert.True(runtime.Session.IsInWorld); var inertSession = CreateInertLiveSessionHost(); var frameHost = new HeadlessLocalPlayerFrameHost(runtime, inertSession); PlayerMovementController candidate = PlayerMovementController.CreatePublicationCandidate( new PhysicsEngine(), PlayerMovementConstructionOptions.Fallback); candidate.SealPublicationCandidate(); candidate.CommitRuntimeOwnership(new RetailObjectQuantumClock()); runtime.MovementOwner.Controller = candidate; Assert.True(candidate.IsRuntimeOwnedDormant); Assert.False(frameHost.CanAdvancePlayer); candidate.ActivateRuntimePublication(); Assert.True(candidate.IsRuntimePublished); Assert.True(frameHost.CanAdvancePlayer); candidate.RetireRuntimePublication(); Assert.False(frameHost.CanAdvancePlayer); } private static LiveSessionHost CreateInertLiveSessionHost() { var controller = new LiveSessionController( new ThrowingLiveSessionOperations()); return new LiveSessionHost( controller, new LiveSessionHostBindings( new LiveSessionRoutingFactories( _ => throw new NotSupportedException(), _ => throw new NotSupportedException()), _ => { }, new LiveSessionSelectionBindings( _ => { }, _ => { }, _ => { }, _ => { }, _ => { }, () => { }), new LiveSessionEnteredWorldBindings( _ => { }, () => { }, () => { }, _ => { }, () => { }), (_, _, _) => { }, () => { }, _ => { }, _ => { })); } private sealed class ThrowingLiveSessionOperations : ILiveSessionOperations { public IPEndPoint ResolveEndpoint(string host, int port) => throw new NotSupportedException(); public WorldSession CreateSession(IPEndPoint endpoint) => throw new NotSupportedException(); public void Connect( WorldSession session, string user, string password) => throw new NotSupportedException(); public CharacterList.Parsed? GetCharacters(WorldSession session) => throw new NotSupportedException(); public void EnterWorld( WorldSession session, int activeCharacterIndex) => throw new NotSupportedException(); public void Tick(WorldSession session) => throw new NotSupportedException(); public void DisposeSession(WorldSession session) => throw new NotSupportedException(); } private static HeadlessSessionDescriptor Descriptor( HeadlessCredentialProviderKind provider = HeadlessCredentialProviderKind.Environment, string credentialReference = "BOT_PASSWORD", Dictionary? characterOptions = null, string? statusFile = null, IReadOnlyList? loginCommands = null, int loginCommandDelayMs = 500) => new() { Id = "bot", Endpoint = new HeadlessEndpointDescriptor { Host = "127.0.0.1", Port = 9000, }, Account = "account", Character = new HeadlessCharacterSelector { Name = "headless", }, Policy = new HeadlessBotPolicyDescriptor { Id = "idle", }, Credential = new HeadlessCredentialReference { Provider = provider, Reference = credentialReference, }, CharacterOptions = characterOptions, StatusFile = statusFile, LoginCommands = loginCommands is null ? null : [.. loginCommands], LoginCommandDelayMs = loginCommandDelayMs, }; /// Campaign LA slice LA2: a probe-mode descriptor — mode /// "probe", Character/Policy both omitted per the pinned /// contract shape enforces. private static HeadlessSessionDescriptor ProbeDescriptor( string id = "probe-bot", HeadlessCredentialProviderKind provider = HeadlessCredentialProviderKind.Environment, string credentialReference = "PROBE_PASSWORD", string? statusFile = null) => new() { Id = id, Endpoint = new HeadlessEndpointDescriptor { Host = "127.0.0.1", Port = 9000, }, Account = "account", Mode = HeadlessSessionMode.Probe, Credential = new HeadlessCredentialReference { Provider = provider, Reference = credentialReference, }, StatusFile = statusFile, }; private static string[] ReadStatusEventNames(string path) => File.ReadAllLines(path) .Select(static line => { using JsonDocument document = JsonDocument.Parse(line); return document.RootElement.GetProperty("e").GetString()!; }) .ToArray(); private static void HydrateGroundedPlayer(GameRuntime runtime) { const uint player = 0x50000002u; PhysicsEngine engine = runtime.EntityObjects.Physics.Engine; AddFlatLandblock(engine); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntity(Spawn(player)) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var controller = new PlayerMovementController(engine); controller.SeedPlacementForTest( new Vector3(96f, 97f, 50f), 0xA9B40001u, new Vector3(96f, 97f, 50f)); runtime.MovementOwner.Controller = controller; } private static void CompleteCollisionGeneration( RuntimePhysicsState physics, uint landblockId, Action? afterAdmission, Action stage) { HeadlessCollisionGenerationTransaction transaction = HeadlessCollisionGenerationTransaction.Begin( physics, landblockId, afterAdmission, stage); while (true) { HeadlessCollisionGenerationAdvance advance = transaction.Advance(); if (advance.Completed) return; Assert.True(advance.Progressed); Assert.False(advance.WaitingForProjectionAcknowledgement); } } private static RuntimePlacementProjectionToken SeedRuntimePlacement( RuntimePhysicsState physics, RuntimeEntityRecord record, uint cell, Vector3 position) { Type coreMarker = typeof(PhysicsEngine); Type requestType = coreMarker.Assembly.GetType( "AcDream.Core.Physics.PhysicsSetPositionRequest", throwOnError: true)!; Type flagsType = coreMarker.Assembly.GetType( "AcDream.Core.Physics.PhysicsSetPositionFlags", throwOnError: true)!; Type placementClassType = coreMarker.Assembly.GetType( "AcDream.Core.Physics.PhysicsPlacementClass", throwOnError: true)!; object request = Activator.CreateInstance( requestType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, binder: null, args: [ position, Quaternion.Identity, cell, position, ImmutableArray.Empty, 1f, 0.4f, 0.4f, PhysicsStateFlags.None, ObjectInfoState.None, 0u, Enum.ToObject(placementClassType, 0), Enum.ToObject(flagsType, 0x011u), Vector3.Zero, 0f, 0f, 0u, cell, ], culture: null)!; Type runtimeMarker = typeof(RuntimePhysicsState); Type commandType = runtimeMarker.Assembly.GetType( "AcDream.Runtime.Physics.RuntimeSetPositionCommand", throwOnError: true)!; Type kindType = runtimeMarker.Assembly.GetType( "AcDream.Runtime.Physics.RuntimeSetPositionOperationKind", throwOnError: true)!; object command = Activator.CreateInstance( commandType, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, binder: null, args: [ request, Enum.ToObject(kindType, 2), 10d, 0UL, 0f, 0f, default(RuntimePortalPlacementAuthority), ], culture: null)!; MethodInfo apply = physics.SetPosition.GetType().GetMethod( "Apply", BindingFlags.Instance | BindingFlags.NonPublic) ?? throw new MissingMethodException("Runtime SetPosition.Apply"); object outcome = apply.Invoke( physics.SetPosition, [record, record.PositionAuthorityVersion, command])!; return (RuntimePlacementProjectionToken)(outcome.GetType().GetProperty( "Projection", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?.GetValue(outcome) ?? throw new MissingMemberException("Runtime placement projection")); } private static RuntimeLandblockCollisionAssets CollisionAssets( uint landblockId, float terrainHeight) { var heights = new byte[81]; var table = new float[256]; table[0] = terrainHeight; return new RuntimeLandblockCollisionAssets( landblockId, new TerrainSurface(heights, table), Array.Empty(), Array.Empty(), 0f, 0f, 0u); } private sealed class FixtureCollisionPublicationException : Exception; [Fact] public void MissingPreparedCollisionYieldsTypedRetryAndCompletesWhenAvailable() { // C3c contract: the prepared-collision read failure is the // conductor's typed AwaitingCollisionSource retry — no // InvalidDataException (or any exception) escapes the host wiring, // the entity stays a tracked, re-drivable first entry, and the same // sequence completes once the source can serve the Setup. var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000021u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); var source = new FlakySetupCollisionSource(); var firstEntry = new AcDream.Runtime.Session .RuntimeFirstEntryDriveController( runtime.EntityObjects, runtime.Clock, source, () => PlayerMovementConstructionOptions.From( runtime.CharacterOwner.MovementSkills.Snapshot), static _ => new RuntimeLocalPlayerPhysicsActivationPreparation( Radius: 0.48f, Height: 1.835f, RuntimeLocalPlayerShadowDisposition.ProvenShapeless)); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new FixtureCollisionNeighborhood(); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry); projection.ProjectSpawn(record, isLocalPlayer: true); Assert.True(source.SetupReadAttempts >= 1); Assert.Null(runtime.MovementOwner.Controller); Assert.Equal(1, firstEntry.PendingCount); Assert.Equal(0u, record.FullCellId); source.Available = true; // The session tick's retry pump. firstEntry.DriveAll(); Assert.IsType( runtime.MovementOwner.Controller); Assert.Equal(0, firstEntry.PendingCount); Assert.Equal(0xA9B40000u, record.FullCellId & 0xFFFF0000u); Assert.NotEqual(0u, record.FullCellId); } private sealed class FlakySetupCollisionSource : AcDream.Content.IPreparedCollisionSource { internal bool Available { get; set; } internal int SetupReadAttempts { get; private set; } public AcDream.Content.PreparedAssetPresence ProbeCollision( AcDream.Content.Pak.PakAssetType type, uint sourceFileId) => AcDream.Content.PreparedAssetPresence.Available; public AcDream.Content.PreparedCollisionReadResult ReadSetupCollision( uint sourceFileId, CancellationToken cancellationToken = default) { SetupReadAttempts++; if (!Available) { return AcDream.Content.PreparedCollisionReadResult< FlatSetupCollision>.Missing; } return AcDream.Content.PreparedCollisionReadResult< FlatSetupCollision>.Loaded(new FlatSetupCollision( System.Collections.Immutable.ImmutableArray< FlatCollisionCylinder>.Empty, [new FlatCollisionSphere(Vector3.Zero, 0.48f)], height: 0f, radius: 0f, stepUpHeight: 0.4f, stepDownHeight: 0.4f)); } public AcDream.Content.PreparedCollisionReadResult< FlatGfxObjCollisionAsset> ReadGfxObjCollision( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionReadResult< FlatCellStructureCollisionAsset> ReadCellStructureCollision( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionReadResult ReadEnvCellTopology( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionSourceStats CollisionStats => default; public void Dispose() { } } private static AcDream.Runtime.Session.RuntimeFirstEntryDriveController CreateFirstEntryDrive(GameRuntime runtime) => new( runtime.EntityObjects, runtime.Clock, new LoadedSetupCollisionSource(), () => PlayerMovementConstructionOptions.From( runtime.CharacterOwner.MovementSkills.Snapshot), static _ => new RuntimeLocalPlayerPhysicsActivationPreparation( Radius: 0.48f, Height: 1.835f, RuntimeLocalPlayerShadowDisposition.ProvenShapeless)); /// /// C4 route 2 (2026-08-03): mirrors 's /// construction pattern for the accepted-Position drive controller. No /// real WorldSession is needed for these fixture tests — /// LocalPlayerOutboundController.SendImmediatePosition no-ops on a null /// session. /// private static RuntimeAcceptedPositionDriveController CreateAcceptedPositionDrive(GameRuntime runtime) => new( runtime.EntityObjects, runtime.Clock, new LoadedSetupCollisionSource(), new LocalPlayerOutboundController((_, _, _, _, _, _) => { }), () => runtime.Generation, () => runtime.PlayerIdentity.ServerGuid, () => runtime.MovementOwner.Controller, () => runtime.CharacterOwner.UsePositionFromServer, () => null); private sealed class LoadedSetupCollisionSource : AcDream.Content.IPreparedCollisionSource { public AcDream.Content.PreparedAssetPresence ProbeCollision( AcDream.Content.Pak.PakAssetType type, uint sourceFileId) => AcDream.Content.PreparedAssetPresence.Available; public AcDream.Content.PreparedCollisionReadResult ReadSetupCollision( uint sourceFileId, CancellationToken cancellationToken = default) => AcDream.Content.PreparedCollisionReadResult .Loaded(new FlatSetupCollision( System.Collections.Immutable.ImmutableArray< FlatCollisionCylinder>.Empty, // R7 review fix (2026-08-03): the dat-exact human Setup // 0x02000001 spheres (Ts46SphereListConformanceTests.cs // :35-39) — foot (0,0,0.475) r=.48, head/torso // (0,0,1.350) r=.48. The PREVIOUS single dummy sphere at // (0,0,0) r=.48 (offset == radius) made a settled origin // rest a FULL radius above the floor; the real foot // sphere's bottom is origin + 0.475 − 0.48 = origin − // 0.005, so a settled origin lands ON the floor within // 5 mm. Retail's BlipPlayer has never lifted the origin // by a sphere radius — that was a fixture artifact, not // a retail-fidelity gain (docs/ISSUES.md #285 correction). [ new FlatCollisionSphere( new Vector3(0f, 0f, 0.475f), 0.48f), new FlatCollisionSphere( new Vector3(0f, 0f, 1.350f), 0.48f), ], height: 0f, radius: 0f, stepUpHeight: 0.4f, stepDownHeight: 0.4f)); public AcDream.Content.PreparedCollisionReadResult< FlatGfxObjCollisionAsset> ReadGfxObjCollision( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionReadResult< FlatCellStructureCollisionAsset> ReadCellStructureCollision( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionReadResult ReadEnvCellTopology( uint sourceFileId, CancellationToken cancellationToken = default) => throw new NotSupportedException(); public AcDream.Content.PreparedCollisionSourceStats CollisionStats => default; public void Dispose() { } } /// /// D1 (C5b architecture review), the local-player FORCE arm — both /// directions of the reachability rule, driven through the real /// sink. /// /// /// The graphical OnPosition route returns ahead of AD-60's W2 /// for every force status EXCEPT NotApplicable, and falls /// through to it on that one. So: a force the drive HANDLED /// (Committed here) is placement-receipt-authoritative and this /// route must leave residency at the cell the placement RESOLVED, not /// re-stamp the wire cell over it; a force the drive did NOT handle /// (no drive at all — the login-window / route-1 shape) must still /// refresh the cell, because nothing else will. /// /// [Theory] [InlineData(true)] [InlineData(false)] public void LocalForcePosition_CommitsTheWireCellOnlyWhenTheDriveDeclined( bool driveHandlesIt) { var operations = new FixtureSessionOperations(); using var credential = new HeadlessCredentialSecret( "fixture", "password"); using var host = new HeadlessSessionHost( Descriptor(), credential, new HeadlessDiagnosticWriter(TextWriter.Null), operations); GameRuntime runtime = host.Runtime; Assert.Equal( RuntimeSessionStartStatus.Connected, host.Start().Status); const uint player = 0x50000004u; runtime.PlayerIdentity.ServerGuid = player; runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration( 0xA9B40000u, 1UL); AddFlatLandblock(runtime.EntityObjects.Physics.Engine); runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration( 0xA9B40000u, 1UL, ready: true); RuntimeFirstEntryDriveController firstEntry = CreateFirstEntryDrive(runtime); RuntimeEntityRecord record = runtime.EntityObjects .RegisterEntityWithInitialResidence( Spawn(player), isLocalPlayer: true) .Canonical!; Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn( record, record.CreateIntegrationVersion, record.Snapshot, replaceGeneration: false)); var collision = new FixtureCollisionNeighborhood(); var projection = new HeadlessSessionWorldProjection( runtime, collision, firstEntry); projection.ProjectSpawn(record, isLocalPlayer: true); PlayerMovementController controller = Assert.IsType( runtime.MovementOwner.Controller); controller.SeedPlacementForTest( new Vector3(48f, 49f, 50f), 0xA9B40001u, new Vector3(48f, 49f, 50f)); // The residence must be closed before the ordinary post-residence // rules apply at all (the conductor is the sole cell authority // while it is open — asserted separately in the Runtime suite). Assert.False(runtime.EntityObjects.TryGetInitialCreateResidence( record, out _)); using var session = new WorldSession( new IPEndPoint(IPAddress.Loopback, 9000)); var entities = new RuntimeLiveEntitySessionController( runtime, session, log: null, projection, driveHandlesIt ? CreateAcceptedPositionDrive(runtime) : null); LiveEntitySessionSink sink = entities.CreateSink(); // A wire cell that is NOT the cell a placement at (72,73) resolves // to, so "committed the wire cell" and "kept the placement's own // resolved cell" are distinguishable values rather than the same // number arrived at two ways. const uint wireCell = 0xA9B40002u; sink.PositionUpdated(new WorldSession.EntityPositionUpdate( player, new CreateObject.ServerPosition( wireCell, 72f, 73f, 50f, 1f, 0f, 0f, 0f), Velocity: null, PlacementId: null, IsGrounded: true, InstanceSequence: 1, PositionSequence: 2, TeleportSequence: 0, ForcePositionSequence: 1)); if (driveHandlesIt) { // The placement committed and resolved its OWN cell — measured // 0xA9B4001C, the outdoor landcell that actually contains // (72, 73) in this flat landblock, which is neither the wire // cell nor the spawn cell. This route added nothing on top of // it. Assert.Equal(new Vector3(72f, 73f, 50.005f), controller.Position); Assert.Equal(0xA9B4001Cu, record.FullCellId); Assert.NotEqual(wireCell, record.FullCellId); } else { Assert.Equal(wireCell, record.FullCellId); Assert.Equal(0xA9B4FFFFu, record.CanonicalLandblockId); } } private static void AddFlatLandblock(PhysicsEngine engine) { var heights = new byte[81]; Array.Fill(heights, (byte)50); var heightTable = new float[256]; for (int index = 0; index < heightTable.Length; index++) heightTable[index] = index; engine.AddLandblock( 0xA9B4FFFFu, new TerrainSurface(heights, heightTable), [], [], worldOffsetX: 0f, worldOffsetY: 0f); } private static WorldSession.EntitySpawn Spawn( uint guid, uint cellId = 0xA9B40001u) { var position = new CreateObject.ServerPosition( cellId, 96f, 97f, 50f, 1f, 0f, 0f, 0f); var timestamps = new PhysicsTimestamps( Position: 1, Movement: 1, State: 1, Vector: 1, Teleport: 0, ServerControlledMove: 1, ForcePosition: 0, ObjDesc: 1, Instance: 1); var physics = new PhysicsSpawnData( RawState: (uint)PhysicsStateFlags.ReportCollisions, Position: position, Movement: null, AnimationFrame: null, SetupTableId: 0x02000001u, MotionTableId: null, SoundTableId: null, PhysicsScriptTableId: null, Parent: null, Children: null, Scale: null, Friction: null, Elasticity: null, Translucency: null, Velocity: null, Acceleration: null, AngularVelocity: null, DefaultScriptType: null, DefaultScriptIntensity: null, Timestamps: timestamps); return new WorldSession.EntitySpawn( guid, position, 0x02000001u, [], [], [], null, null, "Headless", null, null, null, PhysicsState: physics.RawState, InstanceSequence: 1, MovementSequence: 1, ServerControlSequence: 1, PositionSequence: 1, Physics: physics); } private static RuntimePlacementProjectionSnapshot Placement( GameRuntime runtime, RuntimeEntityRecord record, RuntimePlacementProjectionKind kind, Vector3 position, Quaternion orientation) { RuntimeEntityKey key = Assert.IsType(record.Key); var token = new RuntimePlacementProjectionToken( Sequence: 1, Revision: 1, Entity: key, PositionAuthorityVersion: record.PositionAuthorityVersion, SpatialAuthorityVersion: record.SpatialAuthorityVersion, PlacementCommitVersion: record.PlacementCommitVersion, SessionLifetimeVersion: runtime.EntityObjects.Entities.SessionLifetimeVersion, ExactCellId: record.FullCellId, CollisionGeneration: 1, Portal: default); return new RuntimePlacementProjectionSnapshot( token, kind, position, orientation, CellLocalPosition: position, InContact: false, OnWalkable: false); } private static uint ActionOpcode(byte[] body) => BinaryPrimitives.ReadUInt32LittleEndian( body.AsSpan(8, sizeof(uint))); private static string TalkText(byte[] body) { Assert.Equal(ChatRequests.TalkOpcode, ActionOpcode(body)); ushort length = BinaryPrimitives.ReadUInt16LittleEndian( body.AsSpan(12, sizeof(ushort))); return System.Text.Encoding.ASCII.GetString(body, 14, length); } private static string StringActionArgument(byte[] body) { ushort length = BinaryPrimitives.ReadUInt16LittleEndian( body.AsSpan(12, sizeof(ushort))); return System.Text.Encoding.ASCII.GetString(body, 14, length); } private sealed class ManualTimeProvider : TimeProvider { private long _timestamp; public override long TimestampFrequency => TimeSpan.TicksPerSecond; public override long GetTimestamp() => _timestamp; internal void Advance(TimeSpan duration) => _timestamp = checked(_timestamp + duration.Ticks); } // SF-4 fixture: minimal PlayerDescription (0x0013) body carrying only // the CharacterOptions1/2 trailer fields — copied from // HeadlessCharacterOptionsSeederWiringTests.WrapPlayerDescriptionEnvelope // (mirrors GameEventWiringTests.WireAll_PlayerDescription_PublishesCharacterOptions's // fixture layout). private static byte[] WrapPlayerDescriptionEnvelope( uint options1, uint options2) { var stream = new MemoryStream(); using (var writer = new BinaryWriter( stream, System.Text.Encoding.UTF8, leaveOpen: true)) { writer.Write(0u); // property flags writer.Write(0x52u); // player weenie type writer.Write(0u); // vector flags writer.Write(0u); // has health writer.Write(0x40u); // option flags: CharacterOptions2 writer.Write(options1); writer.Write(0u); // legacy hotbar count writer.Write(0u); // spellbook filters writer.Write(options2); writer.Write(0u); // inventory count writer.Write(0u); // equipped count } byte[] payload = stream.ToArray(); byte[] body = new byte[GameEventEnvelope.HeaderSize + payload.Length]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameEventEnvelope.Opcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), 0u); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), 0u); BinaryPrimitives.WriteUInt32LittleEndian( body.AsSpan(12), (uint)GameEventType.PlayerDescription); Array.Copy(payload, 0, body, GameEventEnvelope.HeaderSize, payload.Length); return body; } private sealed class FixtureSessionOperations : ILiveSessionOperations { private int _enterWorldCallCount; private int _tickCallCount; public List Sessions { get; } = []; public int CreatedSessionCount { get; private set; } public int DisposedSessionCount { get; private set; } public string? LastUser { get; private set; } public string? LastPassword { get; private set; } public Action? GameActionCapture { get; init; } public int EnterWorldCallCount => Volatile.Read(ref _enterWorldCallCount); public int TickCallCount => Volatile.Read(ref _tickCallCount); public CharacterList.Parsed? Characters { get; init; } = new( 0u, [ new CharacterList.Character( 0x50000001u, "Other", 0u), new CharacterList.Character( 0x50000002u, "Headless", 0u), ], [], 11, "account", true, true); public IPEndPoint ResolveEndpoint(string host, int port) => new(IPAddress.Loopback, port); public WorldSession CreateSession(IPEndPoint endpoint) { CreatedSessionCount++; var session = new WorldSession(endpoint); session.GameActionCapture = GameActionCapture; Sessions.Add(session); return session; } public void Connect( WorldSession session, string user, string password) { LastUser = user; LastPassword = password; } public CharacterList.Parsed? GetCharacters( WorldSession session) => Characters; public void EnterWorld( WorldSession session, int activeCharacterIndex) { Interlocked.Increment(ref _enterWorldCallCount); } public void Tick(WorldSession session) { Interlocked.Increment(ref _tickCallCount); } public void DisposeSession(WorldSession session) { DisposedSessionCount++; session.Dispose(); } } private sealed class FailOnceTextWriter : StringWriter { public bool FailNextWrite { get; set; } public override void WriteLine(string? value) { if (FailNextWrite) { FailNextWrite = false; throw new IOException("fixture write failure"); } base.WriteLine(value); } } private sealed class FixtureCollisionNeighborhood : IHeadlessCollisionNeighborhood { public int CenterCount { get; private set; } public uint LastCell { get; private set; } public void CenterOn(uint fullCellId) { CenterCount++; LastCell = fullCellId; } public bool IsReady(uint fullCellId) => fullCellId == LastCell; // #365 Step 3a: this fixture has no real admission/publication // machinery to hold open — every CenterOn/IsReady call is a // synchronous no-op, so it is quiescent by construction. A fake // that ever wants to exercise the gate should override this. public bool IsQuiescent => true; // C3c-R1 review F7: the fixture window mirrors production's 3x3 // membership around the last requested center; no center yet means // "within" (never convert before the first CenterOn). public bool IsWithinServiceWindow(uint fullCellId) { if (LastCell == 0u) return true; int dx = Math.Abs( (int)((fullCellId >> 24) & 0xFFu) - (int)((LastCell >> 24) & 0xFFu)); int dy = Math.Abs( (int)((fullCellId >> 16) & 0xFFu) - (int)((LastCell >> 16) & 0xFFu)); return dx <= 1 && dy <= 1; } } /// /// #365 test 3 support: unlike /// (trivially ready, never opens a real admission), /// /// needs a fake that holds an ACTUAL /// open on a landblock the local player does NOT target — the same /// per-landblock admission primitive production's /// HeadlessCollisionNeighborhood.CreatePublication opens for every /// entry in its 3x3 plan. Cancelling (never committing) sidesteps /// CommitCollisionGeneration's own multi-tick /// TryAcquireCollisionPrefixMutationPermission settlement — this /// fake only needs to prove the SEAL sees an open admission, not drive a /// second full commit cycle to completion. /// private sealed class NeighborAdmissionHeldOpenCollisionNeighborhood( RuntimePhysicsState physics, uint heldLandblockId) : IHeadlessCollisionNeighborhood { private RuntimeCollisionAdmission? _admission; private PreparedLandblockCollisionGeneration? _prepared; internal void OpenHeldAdmission() { _admission = physics.BeginCollisionAdmission(heldLandblockId); _prepared = physics.PrepareCollisionGeneration(_admission); } internal void ReleaseHeldAdmission() { if (_admission is null) return; bool cancelled = physics.CancelCollisionGeneration( _admission, _prepared); Assert.True( cancelled, "the held-open neighbor admission did not cancel in one call."); _admission = null; _prepared = null; } public bool IsQuiescent => _admission is null; public void CenterOn(uint fullCellId) { } public bool IsReady(uint fullCellId) => true; public bool IsWithinServiceWindow(uint fullCellId) => true; } /// /// #365 test 4 support: a fake whose the test /// flips directly, isolating PumpFirstEntry's gate from any real /// admission machinery. /// private sealed class GateControllableCollisionNeighborhood : IHeadlessCollisionNeighborhood { private uint _lastCell; internal bool QuiescentOverride { get; set; } = true; public void CenterOn(uint fullCellId) => _lastCell = fullCellId; public bool IsReady(uint fullCellId) => fullCellId == _lastCell; public bool IsWithinServiceWindow(uint fullCellId) => true; public bool IsQuiescent => QuiescentOverride; } private sealed class FixtureEventRoute( Action? onDispose = null) : ILiveSessionEventRouting { public int AttachCount { get; private set; } public int DisposeCount { get; private set; } public int DisposeFailuresRemaining { get; set; } public void Attach() => AttachCount++; public void Dispose() { DisposeCount++; onDispose?.Invoke(); if (DisposeFailuresRemaining > 0) { DisposeFailuresRemaining--; throw new IOException("fixture route detach failure"); } } } }