acdream/tests/AcDream.App.Tests/Input/C3cF2AutoEntryWiringTests.cs
Erik 1fc529cdcb fix(interaction): restore distant use after runtime cutover
Runtime GetObjectA lookup became intentionally non-constructing, so static doors and corpses entered MoveToObject without a physics host and their target snapshot timed out at the origin. Ensure the canonical minimal host exists before routing the server move.

Runtime first-entry also grounds the local player before graphical PartArray attachment. That could leave an unmatched startup CMotionInterp node ahead of all later use and cast motion. Drain matched PartArray entries first, then retire only the impossible pre-attach suffix at the presentation attach boundary.

Add focused regressions for static-target host materialization and attach-order reconciliation. User verified near and distant object use in the connected client; focused App tests pass 3/3.
2026-08-03 09:36:53 +02:00

85 lines
3.3 KiB
C#

namespace AcDream.App.Tests.Input;
/// <summary>
/// C3c-F2 (2026-08-02): source pin for the production player-mode auto-entry
/// precondition. <c>PlayerModeAutoEntry</c> is a ONE-SHOT that disarms before
/// invoking its callback, and the production callback completes the world
/// reveal, so an attempt made before the Runtime first-entry conductor has
/// committed permanently seals the reveal with the player never in world —
/// the second link of the connected-gate login wedge
/// (logs/connected-world-gate-20260802-130455: one "not committed yet" line,
/// then event=complete with materialized=False and 5,577
/// readiness-after-terminal rejections). The production context therefore has
/// to report the same commit <c>PlayerModeController.TryEnter</c> requires.
/// The guard's own one-shot/latch behavior is covered behaviorally by
/// AcDream.Core.Tests.Input.AutoEnterPlayerModeTests; only the production
/// context's dependency graph (a ~15-dependency PlayerModeController) has no
/// focused harness, hence the source pin.
/// </summary>
public sealed class C3cF2AutoEntryWiringTests
{
[Fact]
public void ProductionAutoEntryRequiresTheRuntimePublishedController()
{
string source = ReadSource("Input", "PlayerModeAutoEntry.cs");
Assert.DoesNotContain(
"public bool IsPlayerControllerReady => true;",
source,
StringComparison.Ordinal);
Assert.Contains(
"IsRuntimePublished: true",
source,
StringComparison.Ordinal);
Assert.Contains(
"record.PhysicsHost is EntityPhysicsHost",
source,
StringComparison.Ordinal);
}
[Fact]
public void PlayerPresentationAttach_DrainsMatchedAnimationsBeforeStartupMotionSuffix()
{
string source = ReadSource("Input", "PlayerModeController.cs");
int attach = source.IndexOf(
"controller.Motion.DefaultSink =",
StringComparison.Ordinal);
int animationDrain = source.IndexOf(
"sequencer.Manager.HandleEnterWorld();",
attach,
StringComparison.Ordinal);
int unmatchedMotionDrain = source.IndexOf(
"controller.Motion.HandleExitWorld();",
animationDrain,
StringComparison.Ordinal);
Assert.True(attach >= 0, "The player animation sink was not attached.");
Assert.True(
animationDrain > attach,
"Matching PartArray animations must drain after attaching the sink.");
Assert.True(
unmatchedMotionDrain > animationDrain,
"Only the unmatched pre-attach interpreter suffix may drain last.");
}
private static string ReadSource(params string[] relativePath)
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
{
return File.ReadAllText(Path.Combine(
directory.FullName,
"src",
"AcDream.App",
Path.Combine(relativePath)));
}
directory = directory.Parent;
}
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
}
}