fix(test): UI probe pointer commands convert canvas to window coordinates; char-select Enter logs its outcome

The automation probe fed element-center CANVAS coordinates straight into
UiRoot.OnMouseMove/Down/Up, which take WINDOW coordinates and map
window->canvas internally (UiRoot.MapWindowToCanvas). The two spaces are
identical on every screen without UiRoot.FixedCanvasSize — every prior
probe gate passed — but the character-select/chargen screens stretch an
authored 800x600 canvas across the window, so every synthetic click and
hover landed at canvas*(canvas/window): nowhere near the target. The
enter-world connected gate's 'click element 0x100003A2' silently did
nothing for two full rounds. Element-derived pointer paths (ClickAt,
DragAt, HoverElement) now convert canvas->window via UiRoot.CanvasScale;
raw 'mousemove x y' stays a passthrough.

CharacterManagementUiController.EnterSelected also logs a once-per-click
outcome line ('[UI] character enter accepted/rejected status=...') — a
refused Enter was previously indistinguishable from a click that never
dispatched (both silent), which cost a connected-gate round to tell
apart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-17 10:52:13 +02:00
parent 5ca1d47d7a
commit 997b720455
2 changed files with 35 additions and 1 deletions

View file

@ -580,6 +580,13 @@ internal sealed class CharacterManagementUiController : IDisposable
// remains authoritative and closes it on InWorld/error/reset.
EnsureEnterWait();
RuntimeCommandResult result = _bindings.Enter();
// Enter-world round (2026-08-17): once-per-click outcome line — a
// refused Enter was previously indistinguishable from a click that
// never dispatched (both silent), which cost a full connected-gate
// round to tell apart.
Console.WriteLine(result.Accepted
? "[UI] character enter accepted"
: $"[UI] character enter rejected status={result.Status}");
if (!result.Accepted)
CloseContext(ref _enterWaitContext, suppressCallback: true);
InvalidateAndTick();

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Numerics;
using System.Text;
using AcDream.Core.Items;
@ -147,7 +148,9 @@ public sealed class RetailUiAutomationProbe
{
var target = FindByDatElementId(datElementId);
if (target is null) return Fail($"element 0x{datElementId:X8} not found");
_root.OnMouseMove((int)target.CenterX, (int)target.CenterY);
// Same canvas→window conversion as ClickAt — see CanvasToWindow.
(int x, int y) = CanvasToWindow((int)target.CenterX, (int)target.CenterY);
_root.OnMouseMove(x, y);
return true;
}
@ -282,8 +285,30 @@ public sealed class RetailUiAutomationProbe
}
}
/// <summary>
/// Enter-world round (2026-08-17): element coordinates from the probe's
/// tree walk are CANVAS coordinates, but <see cref="UiRoot.OnMouseMove"/>/
/// <c>OnMouseDown</c>/<c>OnMouseUp</c> take WINDOW coordinates and map
/// window→canvas internally (<c>UiRoot.MapWindowToCanvas</c>). The two
/// spaces are identical on every ordinary screen (no
/// <see cref="UiRoot.FixedCanvasSize"/>), which is why every prior probe
/// gate passed — the character-select/chargen screens are the first
/// STRETCHED canvases this apparatus drove, and there the mismatch sent
/// every synthetic click to canvas·(canvas/window), i.e. nowhere near the
/// target element. Element-derived pointer paths therefore convert
/// canvas→window here before touching the root.
/// </summary>
private (int x, int y) CanvasToWindow(int x, int y)
{
Vector2 scale = _root.CanvasScale;
return scale == Vector2.One
? (x, y)
: ((int)MathF.Round(x * scale.X), (int)MathF.Round(y * scale.Y));
}
private void ClickAt(int x, int y)
{
(x, y) = CanvasToWindow(x, y);
Advance(16);
_root.OnMouseMove(x, y);
_root.OnMouseDown(UiMouseButton.Left, x, y);
@ -294,6 +319,8 @@ public sealed class RetailUiAutomationProbe
private void DragAt(int startX, int startY, int endX, int endY)
{
(startX, startY) = CanvasToWindow(startX, startY);
(endX, endY) = CanvasToWindow(endX, endY);
Advance(16);
_root.OnMouseMove(startX, startY);
_root.OnMouseDown(UiMouseButton.Left, startX, startY);