From e33564f1eb5db7ea4d2cdc185e00f8382f88ce05 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Sat, 22 Sep 2018 19:13:59 -0700 Subject: [PATCH 1/6] MovementPillar --- .../pathing/movement/MovementHelper.java | 24 ++++++++------- .../movement/movements/MovementPillar.java | 29 ++++++++++++------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 8c3ad4f4c..dc9bcfa82 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -47,11 +47,12 @@ */ public interface MovementHelper extends ActionCosts, Helper { - static boolean avoidBreaking(BlockPos pos, IBlockState state) { + static boolean avoidBreaking(BetterBlockPos pos, IBlockState state) { + return avoidBreaking(pos.x, pos.y, pos.z, state); + } + + static boolean avoidBreaking(int x, int y, int z, IBlockState state) { Block b = state.getBlock(); - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); return b == Blocks.ICE // ice becomes water, and water can mess up the path || b instanceof BlockSilverfish // obvious reasons // call BlockStateInterface.get directly with x,y,z. no need to make 5 new BlockPos for no reason @@ -322,16 +323,20 @@ static boolean canPlaceAgainst(BlockPos pos) { static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, boolean includeFalling) { IBlockState state = BlockStateInterface.get(position); - return getMiningDurationTicks(context, position, state, includeFalling); + return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); } static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, IBlockState state, boolean includeFalling) { + return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); + } + + static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); - if (!canWalkThrough(position, state)) { + if (!canWalkThrough(x, y, z, state)) { if (!context.allowBreak()) { return COST_INF; } - if (avoidBreaking(position, state)) { + if (avoidBreaking(x, y, z, state)) { return COST_INF; } double m = Blocks.CRAFTING_TABLE.equals(block) ? 10 : 1; // TODO see if this is still necessary. it's from MineBot when we wanted to penalize breaking its crafting table @@ -342,10 +347,9 @@ static double getMiningDurationTicks(CalculationContext context, BetterBlockPos double result = m / strVsBlock; if (includeFalling) { - BetterBlockPos up = position.up(); - IBlockState above = BlockStateInterface.get(up); + IBlockState above = BlockStateInterface.get(x, y + 1, z); if (above.getBlock() instanceof BlockFalling) { - result += getMiningDurationTicks(context, up, above, true); + result += getMiningDurationTicks(context, x, y + 1, z, above, true); } } return result; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index 15e7d52c0..bf77855d3 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -48,9 +48,13 @@ public void reset() { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src).getBlock(); + return cost(context, src.x, src.y, src.z); + } + + public static double cost(CalculationContext context, int x, int y, int z) { + Block fromDown = BlockStateInterface.get(x, y, z).getBlock(); boolean ladder = fromDown instanceof BlockLadder || fromDown instanceof BlockVine; - IBlockState fromDownDown = BlockStateInterface.get(src.down()); + IBlockState fromDownDown = BlockStateInterface.get(x, y - 1, z); if (!ladder) { if (fromDownDown.getBlock() instanceof BlockLadder || fromDownDown.getBlock() instanceof BlockVine) { return COST_INF; @@ -65,24 +69,23 @@ protected double calculateCost(CalculationContext context) { return COST_INF; } if (fromDown instanceof BlockVine) { - if (getAgainst(src) == null) { + if (!hasAgainst(x, y, z)) { return COST_INF; } } - BetterBlockPos toBreakPos = src.up(2); - IBlockState toBreak = BlockStateInterface.get(toBreakPos); + IBlockState toBreak = BlockStateInterface.get(x, y + 2, z); Block toBreakBlock = toBreak.getBlock(); if (toBreakBlock instanceof BlockFenceGate) { return COST_INF; } Block srcUp = null; if (BlockStateInterface.isWater(toBreakBlock) && BlockStateInterface.isWater(fromDown)) { - srcUp = BlockStateInterface.get(dest).getBlock(); + srcUp = BlockStateInterface.get(x, y + 1, z).getBlock(); if (BlockStateInterface.isWater(srcUp)) { return LADDER_UP_ONE_COST; } } - double hardness = MovementHelper.getMiningDurationTicks(context, toBreakPos, toBreak, true); + double hardness = MovementHelper.getMiningDurationTicks(context, x, y + 2, z, toBreak, true); if (hardness >= COST_INF) { return COST_INF; } @@ -90,12 +93,11 @@ protected double calculateCost(CalculationContext context) { if (toBreakBlock instanceof BlockLadder || toBreakBlock instanceof BlockVine) { hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it } else { - BlockPos chkPos = src.up(3); - IBlockState check = BlockStateInterface.get(chkPos); + IBlockState check = BlockStateInterface.get(x, y + 3, z); if (check.getBlock() instanceof BlockFalling) { // see MovementAscend's identical check for breaking a falling block above our head if (srcUp == null) { - srcUp = BlockStateInterface.get(dest).getBlock(); + srcUp = BlockStateInterface.get(x, y + 1, z).getBlock(); } if (!(toBreakBlock instanceof BlockFalling) || !(srcUp instanceof BlockFalling)) { return COST_INF; @@ -120,6 +122,13 @@ protected double calculateCost(CalculationContext context) { } } + public static boolean hasAgainst(int x, int y, int z) { + return BlockStateInterface.get(x + 1, y, z).isBlockNormalCube() || + BlockStateInterface.get(x - 1, y, z).isBlockNormalCube() || + BlockStateInterface.get(x, y, z + 1).isBlockNormalCube() || + BlockStateInterface.get(x, y, z - 1).isBlockNormalCube(); + } + public static BlockPos getAgainst(BlockPos vine) { if (BlockStateInterface.get(vine.north()).isBlockNormalCube()) { return vine.north(); From eaa44c90f30d19da38ee53cd0df1173e57ff3816 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Sat, 22 Sep 2018 19:46:10 -0700 Subject: [PATCH 2/6] MovementAscend --- .../pathing/movement/MovementHelper.java | 29 ++++++-- .../movement/movements/MovementAscend.java | 71 +++++++++++++------ .../baritone/utils/BlockStateInterface.java | 3 + 3 files changed, 78 insertions(+), 25 deletions(-) diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index dc9bcfa82..1f8b9d498 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -166,6 +166,10 @@ static boolean fullyPassable(IBlockState state) { } static boolean isReplacable(BlockPos pos, IBlockState state) { + return isReplacable(pos.getX(), pos.getY(), pos.getZ(), state); + } + + static boolean isReplacable(int x, int y, int z, IBlockState state) { // for MovementTraverse and MovementAscend // block double plant defaults to true when the block doesn't match, so don't need to check that case // all other overrides just return true or false @@ -176,13 +180,19 @@ static boolean isReplacable(BlockPos pos, IBlockState state) { * return ((Integer)worldIn.getBlockState(pos).getValue(LAYERS)).intValue() == 1; * } */ - if (state.getBlock() instanceof BlockSnow) { + Block block = state.getBlock(); + if (block instanceof BlockSnow) { // as before, default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible) - if (mc.world.getChunk(pos) instanceof EmptyChunk) { + if (mc.world.getChunk(x >> 4, z >> 4) instanceof EmptyChunk) { return true; } + return state.getValue(BlockSnow.LAYERS) == 1; + } + if (block instanceof BlockDoublePlant) { + BlockDoublePlant.EnumPlantType kek = state.getValue(BlockDoublePlant.VARIANT); + return kek == BlockDoublePlant.EnumPlantType.FERN || kek == BlockDoublePlant.EnumPlantType.GRASS; } - return state.getBlock().isReplaceable(mc.world, pos); + return state.getBlock().isReplaceable(null, null); } static boolean isDoorPassable(BlockPos doorPos, BlockPos playerPos) { @@ -315,8 +325,15 @@ static boolean canWalkOn(int x, int y, int z) { return canWalkOn(x, y, z, BlockStateInterface.get(x, y, z)); } + static boolean canPlaceAgainst(int x, int y, int z) { + return canPlaceAgainst(BlockStateInterface.get(x, y, z)); + } + static boolean canPlaceAgainst(BlockPos pos) { - IBlockState state = BlockStateInterface.get(pos); + return canPlaceAgainst(BlockStateInterface.get(pos)); + } + + static boolean canPlaceAgainst(IBlockState state) { // TODO isBlockNormalCube isn't the best check for whether or not we can place a block against it. e.g. glass isn't normalCube but we can place against it return state.isBlockNormalCube(); } @@ -330,6 +347,10 @@ static double getMiningDurationTicks(CalculationContext context, BetterBlockPos return getMiningDurationTicks(context, position.x, position.y, position.z, state, includeFalling); } + static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { + return getMiningDurationTicks(context, x, y, z, BlockStateInterface.get(x, y, z), includeFalling); + } + static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); if (!canWalkThrough(x, y, z, state)) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 9d3398a21..9549fe043 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -28,7 +28,6 @@ import baritone.utils.InputOverrideHandler; import baritone.utils.Utils; import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; @@ -55,46 +54,53 @@ public void reset() { @Override protected double calculateCost(CalculationContext context) { - IBlockState srcDown = BlockStateInterface.get(src.down()); + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + IBlockState srcDown = BlockStateInterface.get(x, y - 1, z); if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) { return COST_INF; } // we can jump from soul sand, but not from a bottom slab boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown); - IBlockState toPlace = BlockStateInterface.get(positionToPlace); + IBlockState toPlace = BlockStateInterface.get(destX, y, destZ); boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace); if (jumpingFromBottomSlab && !jumpingToBottomSlab) { return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab } - if (!MovementHelper.canWalkOn(positionToPlace, toPlace)) { + boolean hasToPlace = false; + if (!MovementHelper.canWalkOn(destX, y, z, toPlace)) { if (!context.hasThrowaway()) { return COST_INF; } - if (toPlace.getBlock() != Blocks.AIR && !BlockStateInterface.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(positionToPlace, toPlace)) { + if (toPlace.getBlock() != Blocks.AIR && !BlockStateInterface.isWater(toPlace.getBlock()) && !MovementHelper.isReplacable(destX, y, destZ, toPlace)) { return COST_INF; } // TODO: add ability to place against .down() as well as the cardinal directions // useful for when you are starting a staircase without anything to place against // Counterpoint to the above TODO ^ you should move then pillar instead of ascend for (int i = 0; i < 4; i++) { - BlockPos against1 = positionToPlace.offset(HORIZONTALS[i]); - if (against1.equals(src)) { + int againstX = destX + HORIZONTALS[i].getXOffset(); + int againstZ = destZ + HORIZONTALS[i].getZOffset(); + if (againstX == x && againstZ == z) { continue; } - if (MovementHelper.canPlaceAgainst(against1)) { - return JUMP_ONE_BLOCK_COST + WALK_ONE_BLOCK_COST + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); + if (MovementHelper.canPlaceAgainst(againstX, y, againstZ)) { + hasToPlace = true; + break; } } - return COST_INF; + if (!hasToPlace) { // didn't find a valid place =( + return COST_INF; + } } - if (BlockStateInterface.get(src.up(3)).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us + if (BlockStateInterface.get(x, y + 3, z).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us // HOWEVER, we assume that we're standing in the start position // that means that src and src.up(1) are both air // maybe they aren't now, but they will be by the time this starts - Block srcUp = BlockStateInterface.get(src.up(1)).getBlock(); - Block srcUp2 = BlockStateInterface.get(src.up(2)).getBlock(); - if (!(srcUp instanceof BlockFalling) || !(srcUp2 instanceof BlockFalling)) { + if (!(BlockStateInterface.getBlock(x, y + 1, z) instanceof BlockFalling) || !(BlockStateInterface.getBlock(x, y + 2, z) instanceof BlockFalling)) { // if both of those are BlockFalling, that means that by standing on src // (the presupposition of this Movement) // we have necessarily already cleared the entire BlockFalling stack @@ -109,15 +115,38 @@ protected double calculateCost(CalculationContext context) { // it's possible srcUp is AIR from the start, and srcUp2 is falling // and in that scenario, when we arrive and break srcUp2, that lets srcUp3 fall on us and suffocate us } - double walk = WALK_ONE_BLOCK_COST; - if (jumpingToBottomSlab && !jumpingFromBottomSlab) { - return walk + getTotalHardnessOfBlocksToBreak(context); // we don't hit space we just walk into the slab + double walk; + if (jumpingToBottomSlab) { + if (jumpingFromBottomSlab) { + walk = Math.max(JUMP_ONE_BLOCK_COST, WALK_ONE_BLOCK_COST); // we hit space immediately on entering this action + } else { + walk = WALK_ONE_BLOCK_COST; // we don't hit space we just walk into the slab + } + } else { + if (toPlace.getBlock() == Blocks.SOUL_SAND) { + walk = WALK_ONE_OVER_SOUL_SAND_COST; + } else { + walk = WALK_ONE_BLOCK_COST; + } } - if (!jumpingToBottomSlab && toPlace.getBlock().equals(Blocks.SOUL_SAND)) { - walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST; + + // cracks knuckles + + double totalCost = 0; + totalCost += walk; + if (hasToPlace) { + totalCost += context.placeBlockCost(); + } + totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, false); // TODO MAKE ABSOLUTELY SURE we don't need includeFalling here, from the falling check above + if (totalCost >= COST_INF) { + return COST_INF; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, false); + if (totalCost >= COST_INF) { + return COST_INF; } - // we hit space immediately on entering this action - return Math.max(JUMP_ONE_BLOCK_COST, walk) + getTotalHardnessOfBlocksToBreak(context); + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 2, destZ, true); + return totalCost; } @Override diff --git a/src/main/java/baritone/utils/BlockStateInterface.java b/src/main/java/baritone/utils/BlockStateInterface.java index 6e0c7297b..ff55c7d89 100644 --- a/src/main/java/baritone/utils/BlockStateInterface.java +++ b/src/main/java/baritone/utils/BlockStateInterface.java @@ -98,6 +98,9 @@ public static Block getBlock(BlockPos pos) { return get(pos).getBlock(); } + public static Block getBlock(int x, int y, int z) { + return get(x, y, z).getBlock(); + } /** * Returns whether or not the specified block is From 3c4708fef769d328a66f643a91d94201c47c37ba Mon Sep 17 00:00:00 2001 From: Leijurv Date: Sat, 22 Sep 2018 19:46:18 -0700 Subject: [PATCH 3/6] performance is key --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c2c822002..f0e17d21f 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [![License](https://img.shields.io/github/license/cabaletta/baritone.svg)](LICENSE) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/7150d8ccf6094057b1782aa7a8f92d7d)](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade) -A Minecraft pathfinder bot. This project is an updated version of [Minebot](https://github.com/leijurv/MineBot/), -the original version of the bot for Minecraft 1.8, rebuilt for 1.12.2. +A Minecraft pathfinder bot. This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/), +the original version of the bot for Minecraft 1.8, rebuilt for 1.12.2. Baritone focuses on reliability and particularly performance (it's over 20x faster than MineBot at calculating paths). Features From 2d3cdddc511a9e17ea908e88f8896b7ab6ddd7a4 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Sat, 22 Sep 2018 19:48:33 -0700 Subject: [PATCH 4/6] why do i even do this --- .../pathing/movement/movements/MovementAscend.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 9549fe043..6d313c488 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -96,11 +96,12 @@ public static double cost(CalculationContext context, int x, int y, int z, int d return COST_INF; } } + IBlockState srcUp2 = null; if (BlockStateInterface.get(x, y + 3, z).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us // HOWEVER, we assume that we're standing in the start position // that means that src and src.up(1) are both air // maybe they aren't now, but they will be by the time this starts - if (!(BlockStateInterface.getBlock(x, y + 1, z) instanceof BlockFalling) || !(BlockStateInterface.getBlock(x, y + 2, z) instanceof BlockFalling)) { + if (!(BlockStateInterface.getBlock(x, y + 1, z) instanceof BlockFalling) || !((srcUp2 = BlockStateInterface.get(x, y + 2, z)).getBlock() instanceof BlockFalling)) { // if both of those are BlockFalling, that means that by standing on src // (the presupposition of this Movement) // we have necessarily already cleared the entire BlockFalling stack @@ -137,7 +138,10 @@ public static double cost(CalculationContext context, int x, int y, int z, int d if (hasToPlace) { totalCost += context.placeBlockCost(); } - totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, false); // TODO MAKE ABSOLUTELY SURE we don't need includeFalling here, from the falling check above + if (srcUp2 == null) { + srcUp2 = BlockStateInterface.get(x, y + 2, z); + } + totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); // TODO MAKE ABSOLUTELY SURE we don't need includeFalling here, from the falling check above if (totalCost >= COST_INF) { return COST_INF; } From 1b576eca2850e080f1c84d4c078bee4f9d20bfb5 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Sat, 22 Sep 2018 19:58:03 -0700 Subject: [PATCH 5/6] MovementDescend --- .../movement/movements/MovementDescend.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 85f7f1107..dc0f2e0a0 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -45,14 +45,18 @@ public void reset() { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src.down()).getBlock(); + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { return COST_INF; } - if (!MovementHelper.canWalkOn(positionToPlace)) { + if (!MovementHelper.canWalkOn(destX, y - 2, destZ)) { return COST_INF; } - Block tmp1 = BlockStateInterface.get(dest).getBlock(); + Block tmp1 = BlockStateInterface.get(destX, y - 1, destZ).getBlock(); if (tmp1 == Blocks.LADDER || tmp1 == Blocks.VINE) { return COST_INF; } @@ -62,7 +66,17 @@ protected double calculateCost(CalculationContext context) { // use this ratio to apply the soul sand speed penalty to our 0.8 block distance walk = WALK_ONE_OVER_SOUL_SAND_COST; } - return walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST) + getTotalHardnessOfBlocksToBreak(context); + double totalCost = walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, false); + if (totalCost >= COST_INF) { + return COST_INF; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y, destZ, false); + if (totalCost >= COST_INF) { + return COST_INF; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, true); // only the top block in the 3 we need to mine needs to consider the falling blocks above + return totalCost; } @Override From 0d0eefec9c82e1d0e4f00b71cccfe45342c15f04 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Sat, 22 Sep 2018 22:00:28 -0700 Subject: [PATCH 6/6] s a n i k --- .../pathing/calc/AStarPathFinder.java | 65 +---- .../baritone/pathing/calc/MoveResult.java | 32 ++ .../java/baritone/pathing/calc/Moves.java | 274 ++++++++++++++++++ src/main/java/baritone/pathing/calc/Path.java | 12 +- .../java/baritone/pathing/calc/PathNode.java | 8 - .../pathing/movement/MovementHelper.java | 10 +- .../movement/movements/MovementDescend.java | 99 ++++++- .../movement/movements/MovementDiagonal.java | 33 ++- .../movement/movements/MovementDownward.java | 10 +- .../movement/movements/MovementFall.java | 60 +--- .../movement/movements/MovementTraverse.java | 38 ++- .../utils/ExampleBaritoneControl.java | 10 +- .../utils/pathing/BetterBlockPos.java | 20 ++ 13 files changed, 489 insertions(+), 182 deletions(-) create mode 100644 src/main/java/baritone/pathing/calc/MoveResult.java create mode 100644 src/main/java/baritone/pathing/calc/Moves.java diff --git a/src/main/java/baritone/pathing/calc/AStarPathFinder.java b/src/main/java/baritone/pathing/calc/AStarPathFinder.java index f5c3eacd6..410b5a3fe 100644 --- a/src/main/java/baritone/pathing/calc/AStarPathFinder.java +++ b/src/main/java/baritone/pathing/calc/AStarPathFinder.java @@ -24,16 +24,12 @@ import baritone.pathing.goals.Goal; import baritone.pathing.movement.ActionCosts; import baritone.pathing.movement.CalculationContext; -import baritone.pathing.movement.Movement; -import baritone.pathing.movement.MovementHelper; -import baritone.pathing.movement.movements.*; import baritone.pathing.path.IPath; import baritone.utils.BlockStateInterface; import baritone.utils.Helper; import baritone.utils.pathing.BetterBlockPos; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ChunkProviderClient; -import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import java.util.Collection; @@ -45,7 +41,7 @@ * * @author leijurv */ -public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { +public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper { private final Optional> favoredPositions; @@ -104,12 +100,14 @@ protected Optional calculate0(long timeout) { logDebug("Took " + (System.nanoTime() / 1000000L - startTime) + "ms, " + numMovementsConsidered + " movements considered"); return Optional.of(new Path(startNode, currentNode, numNodes, goal)); } - Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at currentNodePos - for (Movement movementToGetToNeighbor : possibleMovements) { - if (movementToGetToNeighbor == null) { + for (Moves moves : Moves.values()) { + MoveResult res = moves.apply(calcContext, currentNodePos.x, currentNodePos.y, currentNodePos.z); + numMovementsConsidered++; + double actionCost = res.cost; + if (actionCost >= ActionCosts.COST_INF) { continue; } - BetterBlockPos dest = movementToGetToNeighbor.getDest(); + BetterBlockPos dest = new BetterBlockPos(res.destX, res.destY, res.destZ); int chunkX = currentNodePos.x >> 4; int chunkZ = currentNodePos.z >> 4; if (dest.x >> 4 != chunkX || dest.z >> 4 != chunkZ) { @@ -122,14 +120,11 @@ protected Optional calculate0(long timeout) { } } } - // TODO cache cost - double actionCost = movementToGetToNeighbor.getCost(calcContext); - numMovementsConsidered++; if (actionCost >= ActionCosts.COST_INF) { continue; } if (actionCost <= 0) { - throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " calculated implausible cost " + actionCost); + throw new IllegalStateException(moves + " calculated implausible cost " + actionCost); } if (favoring && favored.contains(dest)) { // see issue #18 @@ -139,7 +134,7 @@ protected Optional calculate0(long timeout) { double tentativeCost = currentNode.cost + actionCost; if (tentativeCost < neighbor.cost) { if (tentativeCost < 0) { - throw new IllegalStateException(movementToGetToNeighbor.getClass() + " " + movementToGetToNeighbor + " overflowed into negative " + actionCost + " " + neighbor.cost + " " + tentativeCost); + throw new IllegalStateException(moves + " overflowed into negative " + actionCost + " " + neighbor.cost + " " + tentativeCost); } double improvementBy = neighbor.cost - tentativeCost; // there are floating point errors caused by random combinations of traverse and diagonal over a flat area @@ -150,7 +145,6 @@ protected Optional calculate0(long timeout) { continue; } neighbor.previous = currentNode; - neighbor.previousMovement = movementToGetToNeighbor; neighbor.cost = tentativeCost; neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal; if (neighbor.isOpen) { @@ -203,45 +197,4 @@ protected Optional calculate0(long timeout) { logDebug("No path found =("); return Optional.empty(); } - - - public static Movement[] getConnectedPositions(BetterBlockPos pos, CalculationContext calcContext) { - int x = pos.x; - int y = pos.y; - int z = pos.z; - BetterBlockPos east = new BetterBlockPos(x + 1, y, z); - BetterBlockPos west = new BetterBlockPos(x - 1, y, z); - BetterBlockPos south = new BetterBlockPos(x, y, z + 1); - BetterBlockPos north = new BetterBlockPos(x, y, z - 1); - return new Movement[]{ - new MovementDownward(pos, new BetterBlockPos(x, y - 1, z)), - - new MovementPillar(pos, new BetterBlockPos(x, y + 1, z)), - - new MovementTraverse(pos, east), - new MovementTraverse(pos, west), - new MovementTraverse(pos, north), - new MovementTraverse(pos, south), - - new MovementAscend(pos, new BetterBlockPos(x + 1, y + 1, z)), - new MovementAscend(pos, new BetterBlockPos(x - 1, y + 1, z)), - new MovementAscend(pos, new BetterBlockPos(x, y + 1, z + 1)), - new MovementAscend(pos, new BetterBlockPos(x, y + 1, z - 1)), - - MovementHelper.generateMovementFallOrDescend(pos, east, calcContext), - MovementHelper.generateMovementFallOrDescend(pos, west, calcContext), - MovementHelper.generateMovementFallOrDescend(pos, north, calcContext), - MovementHelper.generateMovementFallOrDescend(pos, south, calcContext), - - new MovementDiagonal(pos, EnumFacing.NORTH, EnumFacing.EAST), - new MovementDiagonal(pos, EnumFacing.NORTH, EnumFacing.WEST), - new MovementDiagonal(pos, EnumFacing.SOUTH, EnumFacing.EAST), - new MovementDiagonal(pos, EnumFacing.SOUTH, EnumFacing.WEST), - - MovementParkour.generate(pos, EnumFacing.EAST, calcContext), - MovementParkour.generate(pos, EnumFacing.WEST, calcContext), - MovementParkour.generate(pos, EnumFacing.NORTH, calcContext), - MovementParkour.generate(pos, EnumFacing.SOUTH, calcContext), - }; - } } diff --git a/src/main/java/baritone/pathing/calc/MoveResult.java b/src/main/java/baritone/pathing/calc/MoveResult.java new file mode 100644 index 000000000..e9c6caa9e --- /dev/null +++ b/src/main/java/baritone/pathing/calc/MoveResult.java @@ -0,0 +1,32 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.pathing.calc; + +public final class MoveResult { + public final int destX; + public final int destY; + public final int destZ; + public final double cost; + + public MoveResult(int x, int y, int z, double cost) { + this.destX = x; + this.destY = y; + this.destZ = z; + this.cost = cost; + } +} diff --git a/src/main/java/baritone/pathing/calc/Moves.java b/src/main/java/baritone/pathing/calc/Moves.java new file mode 100644 index 000000000..dd9496104 --- /dev/null +++ b/src/main/java/baritone/pathing/calc/Moves.java @@ -0,0 +1,274 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.pathing.calc; + +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.movements.*; +import baritone.utils.pathing.BetterBlockPos; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.Tuple; + +public enum Moves { + DOWNWARD() { + @Override + protected Movement apply0(BetterBlockPos src) { // TODO specific return types + return new MovementDownward(src, src.down()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y - 1, z, MovementDownward.cost(context, x, y, z)); + } + }, + + PILLAR() { + @Override + protected Movement apply0(BetterBlockPos src) { // TODO specific return types + return new MovementPillar(src, src.up()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z, MovementPillar.cost(context, x, y, z)); + } + }, + + TRAVERSE_NORTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.north()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y, z - 1, MovementTraverse.cost(context, x, y, z, x, z - 1)); + } + }, + + TRAVERSE_SOUTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.south()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y, z + 1, MovementTraverse.cost(context, x, y, z, x, z + 1)); + } + }, + + TRAVERSE_EAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.east()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x + 1, y, z, MovementTraverse.cost(context, x, y, z, x + 1, z)); + } + }, + + TRAVERSE_WEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementTraverse(src, src.west()); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x - 1, y, z, MovementTraverse.cost(context, x, y, z, x - 1, z)); + } + }, + + ASCEND_NORTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x, src.y + 1, src.z - 1)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z - 1, MovementAscend.cost(context, x, y, z, x, z - 1)); + } + }, + + ASCEND_SOUTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x, src.y + 1, src.z + 1)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z + 1, MovementAscend.cost(context, x, y, z, x, z + 1)); + } + }, + + ASCEND_EAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x + 1, src.y + 1, src.z)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z + 1, MovementAscend.cost(context, x, y, z, x + 1, z)); + } + }, + + ASCEND_WEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementAscend(src, new BetterBlockPos(src.x - 1, src.y + 1, src.z)); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x, y + 1, z - 1, MovementAscend.cost(context, x, y, z, x - 1, z)); + } + }, + + DESCEND_EAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x + 1, z); + return new MoveResult(x + 1, res.getFirst(), z, res.getSecond()); + } + }, + + DESCEND_WEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x - 1, z); + return new MoveResult(x - 1, res.getFirst(), z, res.getSecond()); + } + }, + + DESCEND_NORTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x, z - 1); + return new MoveResult(x, res.getFirst(), z - 1, res.getSecond()); + } + }, + + DESCEND_SOUTH() { + @Override + protected Movement apply0(BetterBlockPos src) { + MoveResult res = apply(new CalculationContext(), src.x, src.y, src.z); + if (res.destY == src.y - 1) { + return new MovementDescend(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } else { + return new MovementFall(src, new BetterBlockPos(res.destX, res.destY, res.destZ)); + } + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + Tuple res = MovementDescend.cost(context, x, y, z, x, z + 1); + return new MoveResult(x, res.getFirst(), z + 1, res.getSecond()); + } + }, + + DIAGONAL_NORTHEAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.NORTH, EnumFacing.EAST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x + 1, y, z - 1, MovementDiagonal.cost(context, x, y, z, x + 1, z - 1)); + } + }, + + DIAGONAL_NORTHWEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.NORTH, EnumFacing.WEST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x - 1, y, z - 1, MovementDiagonal.cost(context, x, y, z, x - 1, z - 1)); + } + }, + + DIAGONAL_SOUTHEAST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.SOUTH, EnumFacing.EAST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x + 1, y, z + 1, MovementDiagonal.cost(context, x, y, z, x + 1, z + 1)); + } + }, + + DIAGONAL_SOUTHWEST() { + @Override + protected Movement apply0(BetterBlockPos src) { + return new MovementDiagonal(src, EnumFacing.SOUTH, EnumFacing.WEST); + } + + @Override + public MoveResult apply(CalculationContext context, int x, int y, int z) { + return new MoveResult(x - 1, y, z + 1, MovementDiagonal.cost(context, x, y, z, x - 1, z + 1)); + } + }, + // TODO parkour + ; + + protected abstract Movement apply0(BetterBlockPos src); + + + public abstract MoveResult apply(CalculationContext context, int x, int y, int z); +} diff --git a/src/main/java/baritone/pathing/calc/Path.java b/src/main/java/baritone/pathing/calc/Path.java index e94f2fb3e..50dd49d76 100644 --- a/src/main/java/baritone/pathing/calc/Path.java +++ b/src/main/java/baritone/pathing/calc/Path.java @@ -89,7 +89,7 @@ private void assemblePath(PathNode start, PathNode end) { LinkedList tempMovements = new LinkedList<>(); // Instead, do it into a linked list, then convert at the end while (!current.equals(start)) { tempPath.addFirst(current.pos); - tempMovements.addFirst(current.previousMovement); + tempMovements.addFirst(runBackwards(current.previous.pos, current.pos)); current = current.previous; } tempPath.addFirst(start.pos); @@ -100,6 +100,16 @@ private void assemblePath(PathNode start, PathNode end) { movements.addAll(tempMovements); } + private static Movement runBackwards(BetterBlockPos src, BetterBlockPos dest) { // TODO this is horrifying + for (Moves moves : Moves.values()) { + Movement move = moves.apply0(src); + if (move.getDest().equals(dest)) { + return move; + } + } + throw new IllegalStateException("Movement became impossible during calculation " + src + " " + dest + " " + dest.subtract(src)); + } + /** * Performs a series of checks to ensure that the assembly of the path went as expected. */ diff --git a/src/main/java/baritone/pathing/calc/PathNode.java b/src/main/java/baritone/pathing/calc/PathNode.java index 8f9895d85..50283cf98 100644 --- a/src/main/java/baritone/pathing/calc/PathNode.java +++ b/src/main/java/baritone/pathing/calc/PathNode.java @@ -19,7 +19,6 @@ import baritone.pathing.goals.Goal; import baritone.pathing.movement.ActionCosts; -import baritone.pathing.movement.Movement; import baritone.utils.pathing.BetterBlockPos; /** @@ -62,12 +61,6 @@ public final class PathNode { */ PathNode previous; - /** - * In the graph search, what previous movement (edge) was taken to get to here - * Mutable and changed by PathFinder - */ - Movement previousMovement; - /** * Is this a member of the open set in A*? (only used during pathfinding) * Instead of doing a costly member check in the open set, cache membership in each node individually too. @@ -85,7 +78,6 @@ public PathNode(BetterBlockPos pos, Goal goal) { this.cost = ActionCosts.COST_INF; this.goal = goal; this.estimatedCostToGoal = goal.heuristic(pos); - this.previousMovement = null; this.isOpen = false; } diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index 1f8b9d498..b1be71fda 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -462,15 +462,7 @@ static void moveTowards(MovementState state, BlockPos pos) { } static Movement generateMovementFallOrDescend(BetterBlockPos pos, BetterBlockPos dest, CalculationContext calcContext) { - // A - //SA - // A - // B - // C - // D - //if S is where you start, B needs to be air for a movementfall - //A is plausibly breakable by either descend or fall - //C, D, etc determine the length of the fall + int x = dest.x; int y = dest.y; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index dc0f2e0a0..275344e4a 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -17,6 +17,7 @@ package baritone.pathing.movement.movements; +import baritone.Baritone; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; @@ -26,11 +27,16 @@ import baritone.utils.InputOverrideHandler; import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; +import net.minecraft.block.BlockFalling; +import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; +import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; public class MovementDescend extends Movement { + private static final Tuple IMPOSSIBLE = new Tuple<>(0, COST_INF); + private int numTicks = 0; public MovementDescend(BetterBlockPos start, BetterBlockPos end) { @@ -45,38 +51,103 @@ public void reset() { @Override protected double calculateCost(CalculationContext context) { - return cost(context, src.x, src.y, src.z, dest.x, dest.z); + Tuple result = cost(context, src.x, src.y, src.z, dest.x, dest.z); + if (result.getFirst() != dest.y) { + return COST_INF; // doesn't apply to us, this position is a fall not a descend + } + return result.getSecond(); } - public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + public static Tuple cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { - return COST_INF; + return IMPOSSIBLE; + } + + double totalCost = 0; + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, false); + if (totalCost >= COST_INF) { + return IMPOSSIBLE; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y, destZ, false); + if (totalCost >= COST_INF) { + return IMPOSSIBLE; + } + totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, true); // only the top block in the 3 we need to mine needs to consider the falling blocks above + if (totalCost >= COST_INF) { + return IMPOSSIBLE; } + + // A + //SA + // A + // B + // C + // D + //if S is where you start, B needs to be air for a movementfall + //A is plausibly breakable by either descend or fall + //C, D, etc determine the length of the fall if (!MovementHelper.canWalkOn(destX, y - 2, destZ)) { - return COST_INF; + return dynamicFallCost(context, x, y, z, destX, destZ, totalCost); } + Block tmp1 = BlockStateInterface.get(destX, y - 1, destZ).getBlock(); if (tmp1 == Blocks.LADDER || tmp1 == Blocks.VINE) { - return COST_INF; + return IMPOSSIBLE; } + // we walk half the block plus 0.3 to get to the edge, then we walk the other 0.2 while simultaneously falling (math.max because of how it's in parallel) double walk = WALK_OFF_BLOCK_COST; if (fromDown == Blocks.SOUL_SAND) { // use this ratio to apply the soul sand speed penalty to our 0.8 block distance walk = WALK_ONE_OVER_SOUL_SAND_COST; } - double totalCost = walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); - totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, false); - if (totalCost >= COST_INF) { - return COST_INF; + totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST); + return new Tuple<>(y - 1, totalCost); + } + + public static Tuple dynamicFallCost(CalculationContext context, int x, int y, int z, int destX, int destZ, double frontBreak) { + if (frontBreak != 0 && BlockStateInterface.get(destX, y + 2, destZ).getBlock() instanceof BlockFalling) { + // if frontBreak is 0 we can actually get through this without updating the falling block and making it actually fall + // but if frontBreak is nonzero, we're breaking blocks in front, so don't let anything fall through this column, + // and potentially replace the water we're going to fall into + return IMPOSSIBLE; } - totalCost += MovementHelper.getMiningDurationTicks(context, destX, y, destZ, false); - if (totalCost >= COST_INF) { - return COST_INF; + for (int fallHeight = 3; true; fallHeight++) { + int newY = y - fallHeight; + if (newY < 0) { + // when pathing in the end, where you could plausibly fall into the void + // this check prevents it from getting the block at y=-1 and crashing + return IMPOSSIBLE; + } + IBlockState ontoBlock = BlockStateInterface.get(destX, newY, destZ); + double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[fallHeight] + frontBreak; + if (ontoBlock.getBlock() == Blocks.WATER) { // TODO flowing check required here? + if (Baritone.settings().assumeWalkOnWater.get()) { + return IMPOSSIBLE; // TODO fix + } + // found a fall into water + return new Tuple<>(newY, tentativeCost); // TODO incorporate water swim up cost? + } + if (MovementHelper.canWalkThrough(destX, newY, destZ, ontoBlock)) { + continue; + } + if (!MovementHelper.canWalkOn(destX, newY, destZ, ontoBlock)) { + return IMPOSSIBLE; + } + if (MovementHelper.isBottomSlab(ontoBlock)) { + return IMPOSSIBLE; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect + } + if (context.hasWaterBucket() && fallHeight <= context.maxFallHeightBucket() + 1) { + return new Tuple<>(newY + 1, tentativeCost + context.placeBlockCost()); // this is the block we're falling onto, so dest is +1 + } + if (fallHeight <= context.maxFallHeightNoWater() + 1) { + // fallHeight = 4 means onto.up() is 3 blocks down, which is the max + return new Tuple<>(newY + 1, tentativeCost); + } else { + return IMPOSSIBLE; + } } - totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, true); // only the top block in the 3 we need to mine needs to consider the falling blocks above - return totalCost; } @Override diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index d092b66ea..5a5b736a6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -53,40 +53,43 @@ private MovementDiagonal(BetterBlockPos start, BetterBlockPos end, BetterBlockPo @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src.down()).getBlock(); + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + Block fromDown = BlockStateInterface.get(x, y - 1, z).getBlock(); if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { return COST_INF; } - if (!MovementHelper.canWalkThrough(positionsToBreak[4]) || !MovementHelper.canWalkThrough(positionsToBreak[5])) { + if (!MovementHelper.canWalkThrough(destX, y, destZ) || !MovementHelper.canWalkThrough(destX, y + 1, destZ)) { return COST_INF; } - BetterBlockPos destDown = dest.down(); - IBlockState destWalkOn = BlockStateInterface.get(destDown); - if (!MovementHelper.canWalkOn(destDown, destWalkOn)) { + IBlockState destWalkOn = BlockStateInterface.get(destX, y - 1, destZ); + if (!MovementHelper.canWalkOn(destX, y - 1, destZ, destWalkOn)) { return COST_INF; } double multiplier = WALK_ONE_BLOCK_COST; // For either possible soul sand, that affects half of our walking - if (destWalkOn.getBlock().equals(Blocks.SOUL_SAND)) { + if (destWalkOn.getBlock() == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } if (fromDown == Blocks.SOUL_SAND) { multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } - Block cuttingOver1 = BlockStateInterface.get(positionsToBreak[2].down()).getBlock(); + Block cuttingOver1 = BlockStateInterface.get(x, y - 1, destZ).getBlock(); if (cuttingOver1 instanceof BlockMagma || BlockStateInterface.isLava(cuttingOver1)) { return COST_INF; } - Block cuttingOver2 = BlockStateInterface.get(positionsToBreak[4].down()).getBlock(); + Block cuttingOver2 = BlockStateInterface.get(destX, y - 1, z).getBlock(); if (cuttingOver2 instanceof BlockMagma || BlockStateInterface.isLava(cuttingOver2)) { return COST_INF; } - IBlockState pb0 = BlockStateInterface.get(positionsToBreak[0]); - IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); - IBlockState pb2 = BlockStateInterface.get(positionsToBreak[2]); - IBlockState pb3 = BlockStateInterface.get(positionsToBreak[3]); - double optionA = MovementHelper.getMiningDurationTicks(context, positionsToBreak[0], pb0, false) + MovementHelper.getMiningDurationTicks(context, positionsToBreak[1], pb1, true); - double optionB = MovementHelper.getMiningDurationTicks(context, positionsToBreak[2], pb2, false) + MovementHelper.getMiningDurationTicks(context, positionsToBreak[3], pb3, true); + IBlockState pb0 = BlockStateInterface.get(x, y, destZ); + IBlockState pb1 = BlockStateInterface.get(x, y + 1, destZ); + IBlockState pb2 = BlockStateInterface.get(destX, y, z); + IBlockState pb3 = BlockStateInterface.get(destX, y + 1, z); + double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false) + MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true); + double optionB = MovementHelper.getMiningDurationTicks(context, destX, y, z, pb2, false) + MovementHelper.getMiningDurationTicks(context, destX, y + 1, z, pb3, true); if (optionA != 0 && optionB != 0) { return COST_INF; } @@ -100,7 +103,7 @@ protected double calculateCost(CalculationContext context) { return COST_INF; } } - if (BlockStateInterface.isWater(src) || BlockStateInterface.isWater(dest)) { + if (BlockStateInterface.isWater(BlockStateInterface.getBlock(x, y, z)) || BlockStateInterface.isWater(BlockStateInterface.getBlock(destX, y, destZ))) { // Ignore previous multiplier // Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water // Not even touching the blocks below diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index b265ddd71..148dc3b38 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -43,17 +43,21 @@ public void reset() { @Override protected double calculateCost(CalculationContext context) { - if (!MovementHelper.canWalkOn(dest.down())) { + return cost(context, src.x, src.y, src.z); + } + + public static double cost(CalculationContext context, int x, int y, int z) { + if (!MovementHelper.canWalkOn(x, y - 2, z)) { return COST_INF; } - IBlockState d = BlockStateInterface.get(dest); + IBlockState d = BlockStateInterface.get(x, y - 1, z); Block td = d.getBlock(); boolean ladder = td == Blocks.LADDER || td == Blocks.VINE; if (ladder) { return LADDER_DOWN_ONE_COST; } else { // we're standing on it, while it might be block falling, it'll be air by the time we get here in the movement - return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, dest, d, false); + return FALL_N_BLOCKS_COST[1] + MovementHelper.getMiningDurationTicks(context, x, y - 1, z, d, false); } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementFall.java b/src/main/java/baritone/pathing/movement/movements/MovementFall.java index a6a9e9f6f..8ff115911 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementFall.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementFall.java @@ -26,13 +26,10 @@ import baritone.pathing.movement.MovementState.MovementTarget; import baritone.utils.*; import baritone.utils.pathing.BetterBlockPos; -import net.minecraft.block.Block; -import net.minecraft.block.BlockFalling; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.player.InventoryPlayer; -import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; +import net.minecraft.util.Tuple; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; @@ -48,58 +45,11 @@ public MovementFall(BetterBlockPos src, BetterBlockPos dest) { @Override protected double calculateCost(CalculationContext context) { - Block fromDown = BlockStateInterface.get(src.down()).getBlock(); - if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { - return COST_INF; + Tuple result = MovementDescend.cost(context, src.x, src.y, src.z, dest.x, dest.z); + if (result.getFirst() != dest.y) { + return COST_INF; // doesn't apply to us, this position is a descend not a fall } - IBlockState fallOnto = BlockStateInterface.get(dest.down()); - if (!MovementHelper.canWalkOn(dest.down(), fallOnto)) { - return COST_INF; - } - if (MovementHelper.isBottomSlab(fallOnto)) { - return COST_INF; // falling onto a half slab is really glitchy, and can cause more fall damage than we'd expect - } - double placeBucketCost = 0.0; - boolean destIsWater = BlockStateInterface.isWater(dest); - if (!destIsWater && src.getY() - dest.getY() > context.maxFallHeightNoWater()) { - if (!context.hasWaterBucket()) { - return COST_INF; - } - if (src.getY() - dest.getY() > context.maxFallHeightBucket()) { - return COST_INF; - } - placeBucketCost = context.placeBlockCost(); - } - double frontThree = 0; - for (int i = 0; i < 3; i++) { - frontThree += MovementHelper.getMiningDurationTicks(context, positionsToBreak[i], false); - // don't include falling because we will check falling right after this, and if it's there it's COST_INF - if (frontThree >= COST_INF) { - return COST_INF; - } - } - if (BlockStateInterface.get(positionsToBreak[0].up()).getBlock() instanceof BlockFalling) { - return COST_INF; - } - for (int i = 3; i < positionsToBreak.length; i++) { - // TODO is this the right check here? - // MiningDurationTicks is all right, but shouldn't it be canWalkThrough instead? - // Lilypads (i think?) are 0 ticks to mine, but they definitely cause fall damage - // Same thing for falling through water... we can't actually do that - // And falling through signs is possible, but they do have a mining duration, right? - if (MovementHelper.getMiningDurationTicks(context, positionsToBreak[i], false) > 0) { - //can't break while falling - - if (i != positionsToBreak.length - 1 || !destIsWater) { - // if we're checking the very last block to mine - // and it's water (so this is a water fall) - // don't consider the cost of "mining" it - // (if assumeWalkOnWater is true, water isn't canWalkThrough) - return COST_INF; - } - } - } - return WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[positionsToBreak.length - 1] + placeBucketCost + frontThree; + return result.getSecond(); } @Override diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index a2d643772..232a8376e 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -57,11 +57,15 @@ public void reset() { @Override protected double calculateCost(CalculationContext context) { - IBlockState pb0 = BlockStateInterface.get(positionsToBreak[0]); - IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); - IBlockState destOn = BlockStateInterface.get(positionToPlace); - Block srcDown = BlockStateInterface.getBlock(src.down()); - if (MovementHelper.canWalkOn(positionToPlace, destOn)) {//this is a walk, not a bridge + return cost(context, src.x, src.y, src.z, dest.x, dest.z); + } + + public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) { + IBlockState pb0 = BlockStateInterface.get(destX, y + 1, destZ); + IBlockState pb1 = BlockStateInterface.get(destX, y, destZ); + IBlockState destOn = BlockStateInterface.get(destX, y - 1, destZ); + Block srcDown = BlockStateInterface.getBlock(x, y - 1, z); + if (MovementHelper.canWalkOn(destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge double WC = WALK_ONE_BLOCK_COST; if (BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock())) { WC = WALK_ONE_IN_WATER_COST; @@ -73,11 +77,11 @@ protected double calculateCost(CalculationContext context) { WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; } } - double hardness1 = MovementHelper.getMiningDurationTicks(context, positionsToBreak[0], pb0, true); + double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb0, true); if (hardness1 >= COST_INF) { return COST_INF; } - double hardness2 = MovementHelper.getMiningDurationTicks(context, positionsToBreak[1], pb1, false); + double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); if (hardness1 == 0 && hardness2 == 0) { if (WC == WALK_ONE_BLOCK_COST && context.canSprint()) { // If there's nothing in the way, and this isn't water or soul sand, and we aren't sneak placing @@ -95,7 +99,7 @@ protected double calculateCost(CalculationContext context) { if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { return COST_INF; } - if (destOn.getBlock().equals(Blocks.AIR) || MovementHelper.isReplacable(positionToPlace, destOn)) { + if (destOn.getBlock().equals(Blocks.AIR) || MovementHelper.isReplacable(destX, y - 1, destZ, destOn)) { boolean throughWater = BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock()); if (BlockStateInterface.isWater(destOn.getBlock()) && throughWater) { return COST_INF; @@ -103,15 +107,21 @@ protected double calculateCost(CalculationContext context) { if (!context.hasThrowaway()) { return COST_INF; } + double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb0, false); + if (hardness1 >= COST_INF) { + return COST_INF; + } + double hardness2 = MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, pb1, true); + double WC = throughWater ? WALK_ONE_IN_WATER_COST : WALK_ONE_BLOCK_COST; for (int i = 0; i < 4; i++) { - BlockPos against1 = dest.offset(HORIZONTALS[i]); - if (against1.equals(src)) { + int againstX = destX + HORIZONTALS[i].getXOffset(); + int againstZ = destZ + HORIZONTALS[i].getZOffset(); + if (againstX == x && againstZ == z) { continue; } - against1 = against1.down(); - if (MovementHelper.canPlaceAgainst(against1)) { - return WC + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); + if (MovementHelper.canPlaceAgainst(againstX, y - 1, againstZ)) { + return WC + context.placeBlockCost() + hardness1 + hardness2; } } if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) { @@ -121,7 +131,7 @@ protected double calculateCost(CalculationContext context) { return COST_INF; // this is obviously impossible } WC = WC * SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST;//since we are placing, we are sneaking - return WC + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); + return WC + context.placeBlockCost() + hardness1 + hardness2; } return COST_INF; // Out.log("Can't walk on " + Baritone.get(positionsToPlace[0]).getBlock()); diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index 6cd2655aa..19a33f32f 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -27,14 +27,9 @@ import baritone.cache.ChunkPacker; import baritone.cache.Waypoint; import baritone.cache.WorldProvider; -import baritone.pathing.calc.AStarPathFinder; import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.goals.*; -import baritone.pathing.movement.ActionCosts; -import baritone.pathing.movement.CalculationContext; -import baritone.pathing.movement.Movement; import baritone.pathing.movement.MovementHelper; -import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.entity.Entity; @@ -433,7 +428,8 @@ public boolean isInGoal(BlockPos pos) { event.cancel(); return; } - if (msg.equals("costs")) { + // TODO + /*if (msg.equals("costs")) { Movement[] movements = AStarPathFinder.getConnectedPositions(new BetterBlockPos(playerFeet()), new CalculationContext()); List moves = new ArrayList<>(Arrays.asList(movements)); while (moves.contains(null)) { @@ -451,6 +447,6 @@ public boolean isInGoal(BlockPos pos) { } event.cancel(); return; - } + }*/ } } diff --git a/src/main/java/baritone/utils/pathing/BetterBlockPos.java b/src/main/java/baritone/utils/pathing/BetterBlockPos.java index e46b27dc7..09727b846 100644 --- a/src/main/java/baritone/utils/pathing/BetterBlockPos.java +++ b/src/main/java/baritone/utils/pathing/BetterBlockPos.java @@ -134,4 +134,24 @@ public BetterBlockPos offset(EnumFacing dir, int dist) { Vec3i vec = dir.getDirectionVec(); return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist); } + + @Override + public BetterBlockPos north() { + return new BetterBlockPos(x, y, z - 1); + } + + @Override + public BetterBlockPos south() { + return new BetterBlockPos(x, y, z + 1); + } + + @Override + public BetterBlockPos east() { + return new BetterBlockPos(x + 1, y, z); + } + + @Override + public BetterBlockPos west() { + return new BetterBlockPos(x - 1, y, z); + } }