Two related fixes for the (255,117,220) -> (237,97,172) route reported
where the bot fell out of a 22-block water descent and landed on the
rim, and where /pathdiag noticeably froze and warped the bot.
DescendTemplate: long-fall water-column overshoot
=================================================
On segment 7 of the route, a 22-block descend Descend (254.5,113,224.5)
-> (253.5,91,224.5), the bot's footprint enters the target column at
the very first airborne tick (X=253.6 footprint inside [253,254], Z=224.7
footprint inside [224,225]). The previous code immediately set
biasTowardExitInAir = true, rotating yaw to the next segment's exit
heading (-Z) for the entire 20+ tick fall. Forward held during the
fall pushed -Z momentum each tick (~0.05 m/tick equilibrium), so by
landing time the bot had drifted ~1 m past the water column and landed
on the dry rim (Z=223.42 vs target Z=224.5). On a real server this
plunge from 22 blocks onto a non-water block would kill the bot.
Two changes in DescendTemplate.cs:
1. Gate the footprint-inside-target bias for non-single-step descends
behind a "near landing" check (remainingFallY <= 1.5 m, ~3 ticks of
free-fall). The bias still applies on single-step descends (where
the fall is too short for drift to matter) and on the final approach
ticks of multi-block falls.
2. Extend the existing riskyOvershoot Back-input brake to fire on any
multi-block descend whose footprint is already inside the target,
not just the PrepareJump exit case. Once the bot is in the column,
the segment's horizontal travel is done; releasing Forward and
pressing Back kills any residual horizontal velocity so the bot
falls straight into the water/landing block instead of accumulating
air-control momentum from holding Forward for 20+ ticks.
Verified on 1.21.11 with /pathdiag on: segment 7 now lands at
(253.59,91,224.42), 0.09 m off target X and 0.08 m off target Z, well
inside the target water column.
PathSegmentManager: PathDiag main-thread offload
=================================================
Diagnostic dumps were emitted line-by-line through _infoLog?.Invoke(),
which calls Log.Info -> ConsoleIO.WriteLogLine -> file logger on the
main 20 TPS tick. A slow-segment dump or failure trace produces
25-200 synchronous log calls; on the affected route a single batch
took 200-500 ms on the tick thread, freezing the position-packet
stream long enough for the server to lose track and then snap the
bot forward when the tick resumed. Symptom on the user side: every
time /pathdiag is on, the bot freezes for ~half a second, then
"teleports" through the queued segments, then freezes again.
Each diagnostic emission point now snapshots its lines into a List
and dispatches them through DispatchDiagnosticsBatch, which appends
to a single chained Task running on TaskScheduler.Default. The chain
preserves emission order across batches so concurrent slow-segment
and seg-> headers do not interleave. The tick path now does O(1)
work per dump (build list, ContinueWith) instead of O(N) console
writes.
Verified by running the same /goto twice (with and without pathdiag):
both runs produced identical trajectories and identical replan
counts, confirming the diagnostics path no longer perturbs movement.
Pathing test suite: 23 failures, identical to baseline. Live regression
on prior 4 zero-replan routes ((237<->252, 237<->244)) all still
complete cleanly.
Made-with: Cursor
The previous shortcut completed an Ascend on any non-FinalStop exit as
soon as the bot's center entered the target column. This made the next
segment start with the bot offset along the prior heading (e.g. +0.45 m
past target column center), which compounded with sprint-jump distance
on the next segment.
Live regression on (237.5, 97, 172.5) -> (252.5, 138, 220.5):
Ascend (258.5,127,222.5)->(257.5,128,223.5) PrepareJump completed at
(257.78,128.00,223.95). Followed by Parkour (257.5,128,223.5)->
(256.5,128,225.5), the sprint-jump launched from progress=+0.38 and
covered ~3.6 m, overshooting the 2.236 m landing target by ~1.7 m. Bot
fell at (255.30,123.94,226.27) and triggered repeated replans until the
5-replan budget was exhausted.
Restrict the shortcut to ExitTransition == Turn, the original target
case where the AscendTemplate yaw and GroundedSegmentController's
exit-heading yaw disagree and oscillate the bot off the landing block.
For PrepareJump/ContinueStraight/LandingRecovery, the GSC handoff is
correct because the next segment shares the segment heading.
Verified zero replans on:
- (237.5,97,172.5) -> (252.5,138,220.5) (79 segments)
- (252.5,138,220.5) -> (237.5,97,172.5) (39 segments)
- (237.5,97,172.5) -> (244.5,122,188.5) (41 segments)
- (244.5,122,188.5) -> (237.5,97,172.5) (25 segments)
Pathing test suite: 23 failures (matches baseline, no new regressions).
Made-with: Cursor
Two coupled fixes for live 258<->237 / 237<->244 routes that previously
hit 5 replans and gave up on Ascend exit=Turn segments.
DescendTemplate: when biasTowardExitInAir is false on a multi-block
diagonal descend, the per-tick targetYaw rotates as the bot drifts past
the landing column mid-fall (e.g. dx=-1 dz=-1 drop yaw cycles 135 -> 90
-> 0 -> 315 over six air ticks). With Forward held, that rotating yaw
pushes air-control momentum perpendicular to the planned trajectory,
sliding the bot ~0.5 m off the landing onto an adjacent block one tier
below. Lock airborne yaw to the segment's start->end heading for those
descends so air drift stays aligned with the diagonal.
AscendTemplate: add a post-landing completion shortcut for non-FinalStop
exits. Once the jump arc puts the bot back on ground at the target's
elevation with its center inside the target column, hand off to the
next template (which snaps yaw on its first tick). Holding the segment
runs both AscendTemplate's top-level yaw smoothing toward a moving
targetYaw AND GroundedSegmentController's segment/exit-heading rotation
each tick. The competing yaw targets oscillate the bot ~80 ticks until
it walks off the 1-block landing's edge and the segment fails. Mirrors
the existing PrepareJump completion gate. FinalStop is excluded so the
last segment still uses IsSettledAtEnd to detect a true stop.
Live verification on 1.21.11 (round-trip 244<->237 plus the originally
failing 237->244 route): four navigations, 0 replans, all 25/41/24/41
segments completed.
Made-with: Cursor
The cardinal-jump side-wall gate previously demanded BOTH lateral
columns be passable along the trajectory, and the landing-overshoot
gate rejected jumps with a wall one block past the landing on the
takeoff axis. Both rules rejected feasible jumps in the live world:
breaking a single head-height block in a corridor with a continuous
wall on one side could leave a +1 ascend cardinal sprint jump as the
only reachable route, but the planner returned no path.
Side-wall check now accepts when at least one lateral side is
passable. The bot footprint (0.6m centred) stays >=0.2m clear of an
adjacent wall under on-axis yaw, so a single-side wall does not
contact the arc; only a fully-walled tunnel is rejected so the
executor's 5-degree yaw drift has bail-out room.
Landing-overshoot check is now a no-op. The LandingRecovery brake
profile keeps cardinal-jump overshoot under 0.3m so the footprint
stays inside the landing block when the brake engages, making the
"wall one cell past landing" check a false positive in practice.
Updated the conflicting Rejects2x1GapWhenSideWallNarrowsLanding test
to assert the new accept-with-single-side-wall behaviour, added a
fully-walled-tunnel rejection test to guard the bail-out lower bound,
and added a regression covering the live "+1 ascend over a broken
head-height block in a single-walled corridor" scenario.
Made-with: Cursor
When a Descend segment is followed by another Descend (or Traverse/
Diagonal) with a different heading, PathSegmentBuilder.Classify
correctly assigned ExitTransition=LandingRecovery, but BuildHints fell
into the `if (turning)` branch first and returned hints with
RequireStableFooting=true. That gate forces GroundedSegmentController to
wait for IsSettledOnTargetBlock (footprint inside, won't leave next tick,
horizontal speed^2 <= 0.0016), so a multi-block diagonal Descend that
landed inside the target block while still carrying ~0.02 m/tick of
residual jump momentum oscillated in place for ~60 ticks (3 seconds)
until the speed decayed.
Move the LandingRecovery branch ahead of the turning branch so the
Descend-carry handoff uses RequireStableFooting=false and the
ShouldComplete shortcut (LandingRecovery + footprint inside target on
the ground) fires the moment the bot reaches the landing column.
Adds two regression tests covering the Descend->turning-Descend handoff
and a sanity guard that ordinary Traverse->turning-Traverse still uses
the turning branch.
Also lifts DiagnosticsTailSize from 64 to 200 and emits an automatic
"slow segment" tick dump from PathSegmentManager whenever a segment
takes >=25 ticks, which is what surfaced this stall.
Made-with: Cursor
* JumpFeasibility.EvaluateStepDescend now requires BOTH cardinal
shoulder columns to be passable. Axis-separated collision
resolution at the corner zeros one velocity component when a
shoulder is walled, sliding the bot straight down past the intended
diagonal landing into a multi-block fall-through (observed on the
251 -> 244 route around (249,136,207)).
* ParkourFeasibility now distinguishes Parkour-carry from
Descend-carry for flat (yDelta == 0) jumps. A Descend often
overshoots the takeoff block, killing the sprint runway the
SprintJumpTemplate needs to reach 5 c2c. Treat Descend-carry like a
cold start: 3.5 m threshold and 2 aligned back-runway blocks. This
prevents the planner from picking impossible 5 c2c flat jumps right
after a Descend (e.g. (248,122,197) -> (248,122,192)).
Verified live on 1.21.11 across all 6 directions between
(251.5,141,210.5), (252.5,138,220.5) and (244.5,122,188.5):
0 replans, 0 segment failures, no mid-path stalls.
Made-with: Cursor
Two complementary fixes for live-server "stuck on a step then replan"
on the 252.5,138,220.5 -> 244.5,122,188.5 route.
Search layer (ParkourFeasibility.HasRunUp): a long flat sprint parkour
(5 c2c, horiz~5) cannot launch from a cold start. Vanilla physics show
that gap=4 dy=0 reaches 5.1075m only with 12 momentum ticks of straight
sprint windup; a 0t standing jump tops out at gap=3 (=4 c2c). When the
previous move type is not Parkour/Descend (i.e. no carried airborne
momentum) we now require two aligned back-runway blocks instead of one
so the executor actually has room to spin sprint up.
Execution layer (GroundedSegmentController.ShouldComplete): the
LandingRecovery early-out used to live below the MinExitSpeed gate. A
Descend that landed inside the destination block but naturally settled
to zero speed (e.g. when the next segment is a fresh Traverse rather
than a chained Parkour) would fail the 0.03 MinExitSpeed check and idle
inside the target block until the per-segment timeout fired, triggering
an unnecessary replan. Move the LandingRecovery footprint check above
the speed gate so a fully-decelerated handoff is accepted.
Verified live on 1.21.11-Vanilla:
- 252.5,138,220.5 -> 244.5,122,188.5: 24 segments, 0 replans (was: 1)
- 244.5,122,188.5 -> 252.5,138,220.5: 48 segments, 0 replans
- 251.5,141,210.5 -> 252.5,138,220.5: 34 segments, 0 replans
- 252.5,138,220.5 -> 251.5,141,210.5: 24 segments, 0 replans
Test suite: 297 passed / 22 known pre-existing failures, no new
regressions vs 5de169db.
Made-with: Cursor
Fix a cluster of execution-layer issues that caused replans and void
falls when traversing narrow ledges and multi-block descents between
(251.5,141,210.5) and (252.5,138,220.5):
- WalkTemplate / GroundedSegmentController: suppress the pre-rotation
bias toward the next segment's exit heading on stable-footing Turn
exits where the next segment is not a jump. The next template
snaps yaw on its first tick anyway, and pre-rotating mid-stride on
a 1-block walkway pushes sprint drift perpendicular to the path and
walks the bot off the edge. Turn exits into a jump still get the
bias so the takeoff direction stays aligned.
- GroundedSegmentController.ShouldComplete: relax the headingReady
gate for Turn exits with stable footing so the segment can complete
once yaw is aligned with either the current or the next segment
heading (within 25/15 deg). Without this the removed bias would
leave the bot stuck at the end of a walkway waiting for a rotation
that never happens.
- DescendTemplate: restrict the airborne exit-heading bias so it only
kicks in when the footprint is inside the landing block, or on
single-step drops where the fall is too short for lateral drift to
miss the landing column. On 2+ block drops the bot now keeps yaw
pointed at the landing center for the whole fall.
- DescendTemplate: add a multi-block overshoot guard on PrepareJump
exits. Once airborne and past the landing end-plane on a 2+ Y
drop, release forward/sprint and press back briefly so air drag
pulls the bot back into the 1x1 landing column instead of sailing
one block past it into the neighbouring void.
Live round-trip between the two goal coordinates now completes with
zero replans in three consecutive runs in each direction. Full unit
test suite is unchanged from the pre-existing baseline (22 failing
tests, all orthogonal to this change).
Made-with: Cursor
Replace seven hand-written IMove classes (MoveTraverse, MoveDiagonal,
MoveAscend, MoveDiagonalAscend, MoveDiagonalDescend, MoveParkour,
MoveSidewallParkour) with a single MoveJump driven by a JumpDescriptor
(XOffset, ZOffset, YDelta, JumpFlavor). JumpFeasibility is the single
source of truth for the physics/cost rules of every jump-family move.
A* no longer iterates a flat IMove[]. The Calculate loop now drives
an IMoveExpander[] that writes into a stackalloc Span<MoveNeighbor>,
eliminating per-iteration heap traffic. JumpExpander enumerates every
jump-family descriptor dynamically; LegacyMoveExpander wraps the
remaining dynamic-landing moves (MoveDescend, MoveSprintDescend,
MoveClimb, MoveFall) so callers that still pass a custom IMove[]
keep working.
Add two O(1) short-circuits at the top of JumpExpander.Expand:
- Hoist the per-node parkour preconditions (AllowParkour + CanSprint,
standing block climbability, feet-liquid, head clearance at y+2)
so ~170 SprintJump + Sidewall descriptors never call JumpFeasibility
when the node cannot take off at all.
- Precompute an 8-way "first step has no floor" table indexed by
(sign(dx), sign(dz)) so SprintJump descriptors in a direction that
has a walkable floor underneath are dropped without Evaluate.
- Add a conservative "any cardinal wall at y or y+1" probe that skips
all 112 Sidewall descriptors when no wall exists adjacent to the
takeoff.
Move tests switch to the new MoveJump.* factory methods. Behavior is
verified by the existing test suite: the 21 pre-existing baseline
failures are preserved exactly, 0 regressions introduced.
Made-with: Cursor
Introduce an EntryPreparationState carried on PathNode + A* context so
sidewall parkour can explicitly request one or more runway traverses
before takeoff instead of silently dropping the move. ParkourFeasibility
gains TryGetRequiredStaticEntryRunupSteps + HasPreparedRunup helpers so
long descends (major=5, dy=-1) only remain feasible when the preceding
node proved the runup.
Widen HasDominantAxisRunUp to accept cold-start sprint-jumps within
~3.1-3.5 blocks horizontally so lone overhang / staircase takeoffs stay
feasible without a 2-block runway (matches Baritone's MomentumBehavior
.ALLOWED contract).
Add a runtime SidewallParkourController that implements the corner
commitment + wall-hug chain during execution.
Extend pathing test fixtures with InitialMomentumTicks, add sidewall
accepted/rejected scenarios, and refresh timing + contract baselines to
reflect the new planner shapes. Document the design in
docs/superpowers/specs and plans.
Made-with: Cursor
Move PathSegmentManager's Replan to Task.Run so the main tick only reads
results and swaps executors, and introduce a _nextExecutor pre-planning
slot so upcoming segments can prepare while the current one finishes.
Relax per-tick yaw/pitch rate limiting: allow instantaneous snapping
before jump ticks (Baritone does this and servers do not kick for it).
Align jump-template success/failure contracts with Baritone:
- Success key shifts from "speed squared" to "feet-on-target block".
- Failure window widened to the ~200 tick range.
- AscendTemplate gets a headBonkClear + edge/side proximity
precondition so launches only happen from a safe takeoff.
Expose an initialMomentumTicks option on TemplateSimulationRunner so
follow-up sidewall scenarios can warm up physics before a template
starts.
Made-with: Cursor
Theory simulator:
- Add 2D side-wall jump physics with yaw sweep for worst-case margin
- Generate sidewall theory cases (flat/ascend/descend, wall_offset 0/1)
- Add momentum-capabilities.json with band compression and max_reach
- Extend models, capabilities, canonical, and renderers for sidewall
Full-coverage parkour test suite (tools/test-parkour.py):
- Derive test matrix from momentum-capabilities.json
- Build linear/neo/ceiling courses via RCON with 7-block clear margin
- Use /goto for pathfinding, parse A* and PathMgr log output
- Stop-at-first-failure per (family, subfamily, dy, ceil, wo) group
- Hierarchical --filter (e.g. linear/flat, ceiling/headhitter/ceil2.5)
- Exclude sidewall from default matrix (identical max_reach to linear)
Pathing execution fixes:
- Align parkour contracts and timing budgets with live test results
- Fix jump-entry yaw snapping for grounded handoffs
- Template helper and sprint jump template refinements
Made-with: Cursor