fix(B.6+B.7): flush AutonomousPosition on arrival before re-sending action
Previous re-send-on-arrival didn't actually unstick the action. Trace showed ACE replying to the re-sent Use with another MoveToObject — i.e. ACE's Player.Location was still the pre-walk position, so the 'I'm in range' fast-path didn't fire. Cause: packet ordering. OnAutoWalkArrivedReSendAction was firing the re-send immediately (sub-frame), BEFORE the next per-frame AutonomousPosition heartbeat. ACE processed the Use against stale location data. Fix: SendAutonomousPositionNow() — an out-of-frame AutonomousPosition build using the controller's current authoritative position + rotation + cell. Called from OnAutoWalkArrivedReSendAction BEFORE the re-send. ACE now processes 'I'm here at (target_pos)' then 'Use' in order; CreateMoveToChain's WithinUseRadius shortcut (Player_Move.cs:66) fires immediately and the action completes. [autowalk-flush-ap] trace line under ACDREAM_PROBE_AUTOWALK so the sequence is visible end-to-end: autowalk-end → autowalk-flush-ap → autowalk-arrived-resend → autowalk-out
This commit is contained in:
parent
2dc28bb61f
commit
a0fa3d68a7
1 changed files with 56 additions and 7 deletions
|
|
@ -9170,19 +9170,26 @@ public sealed class GameWindow : IDisposable
|
|||
|
||||
/// <summary>
|
||||
/// B.6+B.7 (2026-05-15). Fires when <see cref="PlayerMovementController.AutoWalkArrived"/>
|
||||
/// signals natural arrival at an auto-walk target. Re-sends the
|
||||
/// Use/PickUp action that started the walk so ACE completes it via
|
||||
/// the WithinUseRadius shortcut even if its server-side MoveToChain
|
||||
/// already gave up.
|
||||
/// signals natural arrival at an auto-walk target. Force-flushes
|
||||
/// the player's current authoritative position to ACE first, then
|
||||
/// re-sends the Use/PickUp. Without the position flush, ACE
|
||||
/// processes the re-sent Use before the next per-frame
|
||||
/// AutonomousPosition heartbeat — so ACE's Player.Location is
|
||||
/// still stale (back where the auto-walk started) and ACE replies
|
||||
/// with another MoveToObject instead of completing the action.
|
||||
/// </summary>
|
||||
private void OnAutoWalkArrivedReSendAction()
|
||||
{
|
||||
if (_pendingPostArrivalAction is not (uint guid, bool isPickup) pending)
|
||||
return;
|
||||
// Clear FIRST to break any retry loop — if ACE somehow re-sends
|
||||
// MoveToObject for the close-range action, we don't want
|
||||
// arrival to fire a third action.
|
||||
// Clear FIRST to break any retry loop.
|
||||
_pendingPostArrivalAction = null;
|
||||
|
||||
// Send a fresh AutonomousPosition NOW so ACE's server-side
|
||||
// Player.Location updates to our arrived position before ACE
|
||||
// sees the re-sent action.
|
||||
SendAutonomousPositionNow();
|
||||
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeAutoWalkEnabled)
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[autowalk-arrived-resend] guid=0x{guid:X8} isPickup={isPickup}"));
|
||||
|
|
@ -9190,6 +9197,48 @@ public sealed class GameWindow : IDisposable
|
|||
else SendUse(guid, isRetryAfterArrival: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// B.6+B.7 (2026-05-15). Send an out-of-frame AutonomousPosition
|
||||
/// packet using the controller's current authoritative state.
|
||||
/// Used to flush position to ACE on auto-walk arrival before
|
||||
/// re-sending the Use/PickUp action; without it, ACE's
|
||||
/// Player.Location is the pre-walk position and the action
|
||||
/// resolves out-of-range.
|
||||
/// </summary>
|
||||
private void SendAutonomousPositionNow()
|
||||
{
|
||||
if (_liveSession is null
|
||||
|| _liveSession.CurrentState != AcDream.Core.Net.WorldSession.State.InWorld
|
||||
|| _playerController is null)
|
||||
return;
|
||||
|
||||
var pos = _playerController.Position;
|
||||
int lbX = _liveCenterX + (int)MathF.Floor(pos.X / 192f);
|
||||
int lbY = _liveCenterY + (int)MathF.Floor(pos.Y / 192f);
|
||||
float localX = pos.X - (lbX - _liveCenterX) * 192f;
|
||||
float localY = pos.Y - (lbY - _liveCenterY) * 192f;
|
||||
uint wireCellId = ((uint)lbX << 24) | ((uint)lbY << 16) | (_playerController.CellId & 0xFFFFu);
|
||||
var wirePos = new System.Numerics.Vector3(localX, localY, pos.Z);
|
||||
var wireRot = YawToAcQuaternion(_playerController.Yaw);
|
||||
byte contactByte = _playerController.IsAirborne ? (byte)0 : (byte)1;
|
||||
|
||||
var seq = _liveSession.NextGameActionSequence();
|
||||
var body = AcDream.Core.Net.Messages.AutonomousPosition.Build(
|
||||
gameActionSequence: seq,
|
||||
cellId: wireCellId,
|
||||
position: wirePos,
|
||||
rotation: wireRot,
|
||||
instanceSequence: _liveSession.InstanceSequence,
|
||||
serverControlSequence: _liveSession.ServerControlSequence,
|
||||
teleportSequence: _liveSession.TeleportSequence,
|
||||
forcePositionSequence: _liveSession.ForcePositionSequence,
|
||||
lastContact: contactByte);
|
||||
_liveSession.SendGameAction(body);
|
||||
if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeAutoWalkEnabled)
|
||||
Console.WriteLine(System.FormattableString.Invariant(
|
||||
$"[autowalk-flush-ap] seq={seq} cell=0x{wireCellId:X8} pos=({wirePos.X:F2},{wirePos.Y:F2},{wirePos.Z:F2})"));
|
||||
}
|
||||
|
||||
private uint? SelectClosestCombatTarget(bool showToast)
|
||||
{
|
||||
if (!_entitiesByServerGuid.TryGetValue(_playerServerGuid, out var playerEntity))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue