diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index c32cde60..575815ea 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -37,7 +37,6 @@ import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; -import net.minecraft.world.IBlockAccess; import java.util.Optional; @@ -210,33 +209,10 @@ public interface MovementHelper extends ActionCosts, Helper { return block.isPassable(bsi.access, bsi.isPassableBlockPos.setPos(x, y, z)); } - - /** - * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), - * not including water, and not including ladders or vines or cobwebs (they slow us down) - * - * @param context Calculation context to provide block state lookup - * @param x The block's x position - * @param y The block's y position - * @param z The block's z position - * @return Whether or not the block at the specified position - */ - static boolean fullyPassable(CalculationContext context, int x, int y, int z) { - return fullyPassable( - context.bsi.access, - context.bsi.isPassableBlockPos.setPos(x, y, z), - context.bsi.get0(x, y, z) - ); - } - - static boolean fullyPassable(IPlayerContext ctx, BlockPos pos) { - return fullyPassable(ctx.world(), pos, ctx.world().getBlockState(pos)); - } - - static boolean fullyPassable(IBlockAccess access, BlockPos pos, IBlockState state) { + static Ternary fullyPassableBlockState(IBlockState state) { Block block = state.getBlock(); if (block == Blocks.AIR) { // early return for most common case - return true; + return YES; } // exceptions - blocks that are isPassable true, but we can't actually jump through if (block == Blocks.FIRE @@ -252,10 +228,49 @@ public interface MovementHelper extends ActionCosts, Helper { || block instanceof BlockTrapDoor || block instanceof BlockEndPortal || block instanceof BlockSkull) { - return false; + return NO; } // door, fence gate, liquid, trapdoor have been accounted for, nothing else uses the world or pos parameters - return block.isPassable(access, pos); + // at least in 1.12.2 vanilla, that is..... + try { // A dodgy catch-all at the end, for most blocks with default behaviour this will work, however where blocks are special this will error out, and we can handle it when we have this information + if (block.isPassable(null, null)) { + return YES; + } else { + return NO; + } + } catch (Throwable exception) { + // see PR #1087 for why + System.out.println("The block " + state.getBlock().getLocalizedName() + " requires a special case due to the exception " + exception.getMessage()); + return MAYBE; + } + } + + /** + * canWalkThrough but also won't impede movement at all. so not including doors or fence gates (we'd have to right click), + * not including water, and not including ladders or vines or cobwebs (they slow us down) + */ + static boolean fullyPassable(CalculationContext context, int x, int y, int z) { + return fullyPassable(context, x, y, z, context.get(x, y, z)); + } + + static boolean fullyPassable(CalculationContext context, int x, int y, int z, IBlockState state) { + return context.precomputedData.fullyPassable(context.bsi, x, y, z, state); + } + + static boolean fullyPassable(IPlayerContext ctx, BlockPos pos) { + IBlockState state = ctx.world().getBlockState(pos); + Ternary fullyPassable = fullyPassableBlockState(state); + if (fullyPassable == YES) { + return true; + } + if (fullyPassable == NO) { + return false; + } + return fullyPassablePosition(new BlockStateInterface(ctx), pos.getX(), pos.getY(), pos.getZ(), state); // meh + } + + static boolean fullyPassablePosition(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + return state.getBlock().isPassable(bsi.access, bsi.isPassableBlockPos.setPos(x, y, z)); } static boolean isReplaceable(int x, int y, int z, IBlockState state, BlockStateInterface bsi) { @@ -488,7 +503,7 @@ public interface MovementHelper extends ActionCosts, Helper { if (context.assumeWalkOnWater) { return false; } - Block blockAbove = context.getBlock(x, y+1, z); + Block blockAbove = context.getBlock(x, y + 1, z); if (blockAbove instanceof BlockLiquid) { return false; } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index a36f84bc..9a3925a0 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -112,13 +112,13 @@ public class MovementParkour extends Movement { maxJump = 3; } } - + // check parkour jumps from smallest to largest for obstacles/walls and landing positions int verifiedMaxJump = 1; // i - 1 (when i = 2) for (int i = 2; i <= maxJump; i++) { int destX = x + xDiff * i; int destZ = z + zDiff * i; - + // check head/feet if (!MovementHelper.fullyPassable(context, destX, y + 1, destZ)) { break; @@ -126,10 +126,10 @@ public class MovementParkour extends Movement { if (!MovementHelper.fullyPassable(context, destX, y + 2, destZ)) { break; } - + // check for ascend landing position IBlockState destInto = context.bsi.get0(destX, y, destZ); - if (!MovementHelper.fullyPassable(context.bsi.access, context.bsi.isPassableBlockPos.setPos(destX, y, destZ), destInto)) { + if (!MovementHelper.fullyPassable(context, destX, y, destZ, destInto)) { if (i <= 3 && context.allowParkourAscend && context.canSprint && MovementHelper.canWalkOn(context, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) { res.x = destX; res.y = y + 1; @@ -139,7 +139,7 @@ public class MovementParkour extends Movement { } break; } - + // check for flat landing position IBlockState landingOn = context.bsi.get0(destX, y - 1, destZ); // farmland needs to be canWalkOn otherwise farm can never work at all, but we want to specifically disallow ending a jump on farmland haha @@ -156,14 +156,14 @@ public class MovementParkour extends Movement { } break; } - + if (!MovementHelper.fullyPassable(context, destX, y + 3, destZ)) { break; } - + verifiedMaxJump = i; } - + // parkour place starts here if (!context.allowParkourPlace) { return; diff --git a/src/main/java/baritone/pathing/precompute/PrecomputedData.java b/src/main/java/baritone/pathing/precompute/PrecomputedData.java index 72eb575b..1b05bee6 100644 --- a/src/main/java/baritone/pathing/precompute/PrecomputedData.java +++ b/src/main/java/baritone/pathing/precompute/PrecomputedData.java @@ -25,7 +25,7 @@ import net.minecraft.block.state.IBlockState; import static baritone.pathing.precompute.Ternary.MAYBE; import static baritone.pathing.precompute.Ternary.YES; -public class PrecomputedData { // TODO add isFullyPassable +public class PrecomputedData { private final int[] data = new int[Block.BLOCK_STATE_IDS.size()]; @@ -34,6 +34,8 @@ public class PrecomputedData { // TODO add isFullyPassable private static final int CAN_WALK_ON_SPECIAL_MASK = 1 << 2; private static final int CAN_WALK_THROUGH_MASK = 1 << 3; private static final int CAN_WALK_THROUGH_SPECIAL_MASK = 1 << 4; + private static final int FULLY_PASSABLE_MASK = 1 << 5; + private static final int FULLY_PASSABLE_SPECIAL_MASK = 1 << 6; private int fillData(int id, IBlockState state) { int blockData = 0; @@ -54,6 +56,14 @@ public class PrecomputedData { // TODO add isFullyPassable blockData |= CAN_WALK_THROUGH_SPECIAL_MASK; } + Ternary fullyPassableState = MovementHelper.fullyPassableBlockState(state); + if (fullyPassableState == YES) { + blockData |= FULLY_PASSABLE_MASK; + } + if (fullyPassableState == MAYBE) { + blockData |= FULLY_PASSABLE_SPECIAL_MASK; + } + blockData |= COMPLETED_MASK; data[id] = blockData; // in theory, this is thread "safe" because every thread should compute the exact same int to write? @@ -89,4 +99,19 @@ public class PrecomputedData { // TODO add isFullyPassable return (blockData & CAN_WALK_THROUGH_MASK) != 0; } } + + public boolean fullyPassable(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + int id = Block.BLOCK_STATE_IDS.get(state); + int blockData = data[id]; + + if ((blockData & COMPLETED_MASK) == 0) { // we need to fill in the data + blockData = fillData(id, state); + } + + if ((blockData & FULLY_PASSABLE_SPECIAL_MASK) != 0) { + return MovementHelper.fullyPassablePosition(bsi, x, y, z, state); + } else { + return (blockData & FULLY_PASSABLE_MASK) != 0; + } + } }