From fc2e42cddc6636449082dc2475eb884d57356972 Mon Sep 17 00:00:00 2001 From: bash Date: Fri, 8 May 2026 10:10:06 +0200 Subject: [PATCH] feat(Core/Movement): Cap each MoveSplinePath dispatch at 100y for periodic replanning --- src/Ai/Base/Actions/MovementActions.cpp | 23 +++++++++++++++ src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp | 30 +++++++++++++++----- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 631fd493b..fdbaac69a 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -3232,6 +3232,29 @@ bool MovementAction::LaunchWalkSpline(TravelPlan& state) for (auto& pt : state.walkPoints) bot->UpdateAllowedPositionZ(pt.x, pt.y, pt.z); + // Cap dispatched path length at ~100y. The active spline runs + // until the bot is within ~10y of its endpoint, then MoveFarTo / + // ExecuteTravelPlan replans from the new position. Capping per- + // dispatch distance gives the planner regular re-evaluation + // points (terrain shifts, combat, position drift) instead of + // committing the whole batch upfront. + { + constexpr float maxDispatchLength = 100.0f; + float accumulated = 0.f; + size_t cutoff = state.walkPoints.size(); + for (size_t i = 1; i < state.walkPoints.size(); ++i) + { + accumulated += (state.walkPoints[i] - state.walkPoints[i - 1]).length(); + if (accumulated >= maxDispatchLength) + { + cutoff = i + 1; + break; + } + } + if (cutoff < state.walkPoints.size()) + state.walkPoints.resize(cutoff); + } + // Mount up if (!bot->IsMounted() && !bot->IsInCombat() && bot->IsOutdoors() && bot->IsAlive()) botAI->DoSpecificAction("check mount state", Event(), true); diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 7630cb89f..48d80d990 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -236,13 +236,29 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) if (points.size() >= 2) { - // Cap the chain at 20 waypoints. Beyond that, the - // chained probe's accuracy degrades (more chained - // PathGenerator calls = more stitching artifacts) and - // the spline interpolation between distant waypoints - // is more likely to drift through air. - if (points.size() > 20) - points.resize(20); + // Cap dispatched path length at ~100y. MoveFarTo's + // early-exit (top of function) lets the active spline + // run until bot is within 10y of its endpoint, then + // replans from the new position. Capping per-dispatch + // distance gives the planner regular re-evaluation + // points without the per-tick replan cost of fully + // unbounded chunks. + { + constexpr float maxDispatchLength = 100.0f; + float accumulated = 0.f; + size_t cutoff = points.size(); + for (size_t i = 1; i < points.size(); ++i) + { + accumulated += (points[i] - points[i - 1]).length(); + if (accumulated >= maxDispatchLength) + { + cutoff = i + 1; + break; + } + } + if (cutoff < points.size()) + points.resize(cutoff); + } LOG_INFO("playerbots", "[MoveFar] {} mmap-path | dis={:.0f} | endDist={:.0f} | wp={} | mmapFails={} nodeFails={} | flags={}{}{}", bot->GetName(), dis, endDistToDest, (uint32)points.size(),