Ahoy! Baritone can now swim and use boats - #5127
AverWasTaken wants to merge 3 commits into
Conversation
baritone crosses water by bobbing along the bottom at wading speed, which is nine ticks a block. this sprint swims along the surface instead, about seven, and prices water that way in the planner. sprint is what enters and keeps the vanilla swim state, so Movement.update() forces it while in water. vanilla only starts sprinting from the key on the ground or with the eye under, and cancels it at the surface, which is exactly where the old bob kept us, so a small redirect in updateSwimming latches the state the same tick instead of waiting for gravity to pull the eye down. vertical control is pitch, not the jump key. with jump held vanilla settles at a vertical speed set purely by the look pitch (about 25 degrees down hovers, level rises, steeper sinks), so a PD hold on feet height turns into a pitch and the player sits 0.15 under the surface, head out for air, body in for the swim state. cutting the jump key at a depth band used to launch us clean out of the water and porpoise, since the momentum after a rise carries four times the last tick's speed. tuned against the exact update equations: no overshoot from any depth, settles within a second. shallow water crawls the bottom until air runs low, then stands up to breathe until it's full. bends get turned into early. water only eats a fifth of the momentum a tick, so arriving at a corner at swim speed coasts half a block through it. braking flickers the swim state (vanilla drops sprint the moment W stops counting as forward), so instead aim at the block after the bend once the corner is inside coasting distance and let the momentum be the entry angle. mining from a swim didn't work at all: the swim look mode returned SERVER for everything, so the crosshair never landed on the block, and vanilla mines 5x slower off the ground and 5x again with the eye under. block interacts get CLIENT like the normal freeLook branch, and shouldSwim() is false while PREPPING so we stand up to mine.
with a boat in the inventory the planner prices open water at rowing speed and the executor takes it from there: on the last block of shore (or floating in the water, if the path jumped in) it places the boat out over the lane, climbs in, rows the water nodes, and at the far side punches its own boat so the item drops right where it lands, then carries on on foot. boatMinWaterLength says how much water is worth the place and scuttle overhead, boatPickup whether to take the boat along. water only counts when the boat actually fits: surface water, headroom for a rider, no bubble columns nearby, and every neighbour at water level open, because the hull is 1.375 wide and the item refuses to place it (and a moving one scrapes) with a bank beside the node. that also routes A* a block off the banks. the first block off the shore gets a pass on its neighbours since the bank's always there. driving is a little model predictive loop on the exact vanilla boat physics: for every tap it could do, roll the next twelve ticks and keep the one that lands nearest the lane point ahead without leaving the lane, clipping a bank, pointing the wrong way or fidgeting with the keys. anything steering on heading alone hunts, nine tenths of the velocity carries over every tick. tuned offline against the same equations. the lane it tracks is the node centers pulled taut, since the staircase water paths otherwise turn into a wave. the trip owns the executor tick while it runs, keeps pathPosition moving so plan ahead still works, survives splices, and a fresh executor that finds the player already rowing just carries on. boat stretches render as a lane in colorBoatPath.
ce59c5e to
f797ded
Compare
rowing was priced at 2.857 a block and costHeuristic is 3.563, so the heuristic overestimated anywhere near water and A* went on scenic detours across the lake. boat water now costs at least costHeuristic. still less than half of swimming so it gets picked all the same. the lane edges were drawn per segment with per segment normals, which gaps on the outside of a turn and folds into a bow tie on the inside. now each side is one connected line off averaged normals, and edge points that end up closer to the centerline than the lane is wide get skipped so the inside just cuts the corner.
|
yep you called it on A*. rowing was priced at 20/7 = 2.857 a block and costHeuristic is 3.563, so the heuristic overestimated anywhere near water and the search went on a scenic tour of the lake. boat water now costs on the trace: each segment was drawing its own pair of offset lines with its own normal, so the outside of a turn gapped and the inside folded over itself into that bow tie. each side is now one connected line off averaged normals, and edge points that land closer to the centerline than the lane is wide get dropped, so the inside of a tight turn just cuts the corner. both in 3926456. on the size, fair. most of it is |


Ahoy! Baritone can now swim and use boats
Branch:
water-improvements->1.21.4What this PR does
Two optional ways to actually cross water instead of bobbing along the bottom of it:
allowSwimmingsprint swims along the waterline with the head out, andallowBoatsplaces a boat from the inventory, rows the path, and takes the boat with it on the far side. Both off by default, both priced into the planner so it picks water routes when they're faster.Why it should be merged
Baritone treats a lake as nine ticks a block of wading, so it walks around anything bigger than a pond. Swimming brings that to about seven and a boat to under three, and the search gets to use that. Everything is behind settings that default to off, the cost side is a
min()in the calculation context, and the boat execution is a self contained state machine the executor hands the tick to, so with the settings off nothing about pathing changes and with them on it's one class to look at if something looks odd.What changed
Swimming first. Vanilla only enters the swim state with the eye underwater and actively cancels sprint at the surface, which is exactly where the old jump bob kept us, so sprint swimming never engaged on its own. Then even once it did, cutting the jump key at a depth band launched the player out of the water and porpoised. And boats: a boat's velocity keeps nine tenths of itself every tick, so anything that steers on heading error overcorrects and hunts, which took a few evenings of rowing into banks to properly appreciate.
allowSwimming: sprint is what keeps the swim state alive, soMovement.update()forces it while in water and a small redirect inupdateSwimminglatches the state without waiting for gravity. Vertical control is pitch, not jump: with jump held vanilla settles at a vertical speed set purely by look pitch, so a PD on feet height turns into a pitch and the player sits 0.15 under the surface with the head out and the body in. Simulated against the exact update equations first, no overshoot from any depth, settles within a second. Shallow water crawls along the bottom until air runs low, then stands up to breathe. Corners are cut early because water momentum coasts half a block past a bend.SWIM_ONE_BLOCK_COST = 20/2.86.shouldSwim()is false while PREPPING so we stand up to mine.allowBoats: with aBoatItemanywhere in the inventory, water nodes a boat can float on are priced atBOAT_ONE_BLOCK_COST = 20/7. "Can float" is surface water with headroom for a rider, no bubble columns in the 3x3 (a whirlpool sinks it, soul sand launches it), and every neighbour at water level open, because the hull is 1.375 wide and the item refuses to place it with a bank beside the node. That last rule also routes A* a block off the banks for free.BoatTrip, owned byPathExecutor: place the boat out over the lane from the shore (or from the water if the path jumped in), climb in, row, and at the far side punch our own boat so the item drops where we land, wait for the feet to sink back into the water node, and hand the tick back. It keepspathPositionmoving so plan ahead still works, survives splices, and a fresh executor that finds the player already rowing just carries on.boatMinWaterLength(default 8) says how much water is worth the place and scuttle overhead,boatPickupwhether to take the boat along.colorBoatPath(default green), two lines either side of the line the boat actually follows. Refused runs drop back to the normal path color.Costs changed: water traversal is
min(wading, swimming)withallowSwimmingandmin(that, rowing)where a boat can float withallowBoats. Nothing else in the cost model moved.Preview
https://www.youtube.com/watch?v=0kdDPz10wvI
Testing
Existing unit tests and the full build (JDK 17) green. Swimming and boats smoke tested in a dev env over a few sessions: lake crossings, rivers with bends, shores a block above the water, falling into water from a ledge, one boat in the inventory, creative and survival pickup, and pausing mid crossing. Known limits: a one wide canal is never boatable,
blockFreeLookon means placement mostly fails since the client never turns, and corners at speed still swing about a block wide inside the lane.