Move caching functions into MovementHelper

This commit is contained in:
scorbett123 2022-06-07 21:52:24 +01:00
parent a6557121a0
commit 9e1a5008ed
9 changed files with 148 additions and 255 deletions

View File

@ -422,7 +422,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
*/
public BetterBlockPos pathStart() { // TODO move to a helper or util class
BetterBlockPos feet = ctx.playerFeet();
if (!precomputedData.canWalkOn(ctx, feet.down())) {
if (!MovementHelper.canWalkOn(ctx, feet.down())) {
if (ctx.player().onGround) {
double playerX = ctx.player().posX;
double playerZ = ctx.player().posZ;
@ -441,7 +441,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
// can't possibly be sneaking off of this one, we're too far away
continue;
}
if (precomputedData.canWalkOn(ctx, possibleSupport.down()) && precomputedData.canWalkThrough(ctx, possibleSupport) && context.precomputedData.canWalkThrough(ctx, possibleSupport.up())) {
if (MovementHelper.canWalkOn(ctx, possibleSupport.down()) && MovementHelper.canWalkThrough(ctx, possibleSupport) && MovementHelper.canWalkThrough(ctx, possibleSupport.up())) {
// this is plausible
//logDebug("Faking path start assuming player is standing off the edge of a block");
return possibleSupport;
@ -451,7 +451,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
} else {
// !onGround
// we're in the middle of a jump
if (precomputedData.canWalkOn(ctx, feet.down().down())) {
if (MovementHelper.canWalkOn(ctx, feet.down().down())) {
//logDebug("Faking path start assuming player is midair and falling");
return feet.down();
}
@ -462,7 +462,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
@Override
public void onSettingChanged(SettingChangedEvent event) {
this.precomputedData.refresh();
this.precomputedData = new PrecomputedData();
}
/**

View File

@ -88,62 +88,105 @@ public interface MovementHelper extends ActionCosts, Helper {
return canWalkThrough(bsi, x, y, z, bsi.get0(x, y, z));
}
// if changing something in this function remember to also change it in precomputed data
static boolean canWalkThrough(CalculationContext context, int x, int y, int z, IBlockState state) {
return context.precomputedData.canWalkThrough(context.bsi, x, y, z, state);
}
static boolean canWalkThrough(CalculationContext context, int x, int y, int z) {
return context.precomputedData.canWalkThrough(context.bsi, x, y, z, context.get(x, y, z));
}
static boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
Optional<Boolean> canWalkOn = canWalkThroughBlockState(state);
return canWalkOn.orElseGet(() -> canWalkThroughPosition(bsi, x, y, z, state));
}
static Optional<Boolean> canWalkThroughBlockState(IBlockState state) {
Block block = state.getBlock();
if (block == Blocks.AIR) { // early return for most common case
return true;
if (block == Blocks.AIR) {
return Optional.of(true);
}
if (block == Blocks.FIRE || block == Blocks.TRIPWIRE || block == Blocks.WEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof BlockSkull || block instanceof BlockTrapDoor || block == Blocks.END_ROD) {
return false;
return Optional.of(false);
}
if (Baritone.settings().blocksToAvoid.value.contains(block)) {
return false;
return Optional.of(false);
}
if (block instanceof BlockDoor || block instanceof BlockFenceGate) {
// Because there's no nice method in vanilla to check if a door is openable or not, we just have to assume
// that anything that isn't an iron door isn't openable, ignoring that some doors introduced in mods can't
// be opened by just interacting.
return block != Blocks.IRON_DOOR;
return Optional.of(block != Blocks.IRON_DOOR);
}
if (block == Blocks.CARPET) {
return Optional.empty();
}
if (block instanceof BlockSnow) {
if (state.getValue(BlockSnow.LAYERS) >= 3) {
return Optional.of(false);
}
return Optional.empty();
}
if (block instanceof BlockLiquid) {
if (state.getValue(BlockLiquid.LEVEL) != 0) {
return Optional.of(false);
} else {
return Optional.empty();
}
}
if (block instanceof BlockCauldron) {
return Optional.of(false);
}
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
return Optional.of(block.isPassable(null, null));
} catch (NullPointerException exception) {
System.out.println("Error");
return Optional.empty();
}
}
static boolean canWalkThroughPosition(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
Block block = state.getBlock();
if (block == Blocks.CARPET) {
return canWalkOn(bsi, x, y - 1, z);
}
if (block instanceof BlockSnow) {
// we've already checked doors and fence gates
// so the only remaining dynamic isPassables are snow and trapdoor
// if they're cached as a top block, we don't know their metadata
// default to true (mostly because it would otherwise make long distance pathing through snowy biomes impossible)
if (!bsi.worldContainsLoadedChunk(x, z)) {
return true;
}
// the check in BlockSnow.isPassable is layers < 5
// while actually, we want < 3 because 3 or greater makes it impassable in a 2 high ceiling
if (state.getValue(BlockSnow.LAYERS) >= 3) {
return false;
}
// ok, it's low enough we could walk through it, but is it supported?
if (block instanceof BlockSnow) { // TODO see if this case is necessary, shouldn't it also check this somewhere else?
return canWalkOn(bsi, x, y - 1, z);
}
if (isFlowing(x, y, z, state, bsi)) {
return false; // Don't walk through flowing liquids
}
if (block instanceof BlockLiquid) {
if (isFlowing(x, y, z, state, bsi)) {
return false;
}
// Everything after this point has to be a special case as it relies on the water not being flowing, which means a special case is needed.
if (Baritone.settings().assumeWalkOnWater.value) {
return false;
}
IBlockState up = bsi.get0(x, y + 1, z);
if (up.getBlock() instanceof BlockLiquid || up.getBlock() instanceof BlockLilyPad) {
return false;
}
return block == Blocks.WATER || block == Blocks.FLOWING_WATER;
}
if (block instanceof BlockCauldron) {
return false;
}
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)
@ -296,32 +339,57 @@ public interface MovementHelper extends ActionCosts, Helper {
* @return Whether or not the specified block can be walked on
*/
static boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
Optional<Boolean> canWalkOn = canWalkOnBlockState(state);
return canWalkOn.orElseGet(() -> canWalkOnPosition(bsi, x, y, z, state));
}
static Optional<Boolean> canWalkOnBlockState(IBlockState state) {
Block block = state.getBlock();
if (block == Blocks.AIR || block == Blocks.MAGMA) {
// early return for most common case (air)
// plus magma, which is a normal cube but it hurts you
return false;
return Optional.of(false);
}
if (state.isBlockNormalCube()) {
return true;
return Optional.of(true);
}
if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.value)) { // TODO reconsider this
return true;
return Optional.of(true);
}
if (block == Blocks.FARMLAND || block == Blocks.GRASS_PATH) {
return true;
return Optional.of(true);
}
if (block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST) {
return true;
return Optional.of(true);
}
if (isWater(block)) {
return Optional.empty();
}
if (Baritone.settings().assumeWalkOnLava.value && MovementHelper.isLava(block)) {
return Optional.empty();
}
if (block instanceof BlockSlab) {
if (!Baritone.settings().allowWalkOnBottomSlab.value) {
if (((BlockSlab) block).isDouble()) {
return Optional.of(true);
}
return Optional.of(state.getValue(BlockSlab.HALF) != BlockSlab.EnumBlockHalf.BOTTOM);
}
return Optional.of(true);
}
return Optional.of(block instanceof BlockStairs);
}
static boolean canWalkOnPosition(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
Block block = state.getBlock();
if (isWater(block)) {
// since this is called literally millions of times per second, the benefit of not allocating millions of useless "pos.up()"
// BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think its a decrease in readability
// BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think it's a decrease in readability
Block up = bsi.get0(x, y + 1, z).getBlock();
if (up == Blocks.WATERLILY || up == Blocks.CARPET) {
return true;
}
if (isFlowing(x, y, z, state, bsi) || block == Blocks.FLOWING_WATER) {
if (MovementHelper.isFlowing(x, y, z, state, bsi) || block == Blocks.FLOWING_WATER) {
// the only scenario in which we can walk on flowing water is if it's under still water with jesus off
return isWater(up) && !Baritone.settings().assumeWalkOnWater.value;
}
@ -329,22 +397,20 @@ public interface MovementHelper extends ActionCosts, Helper {
// if assumeWalkOnWater is off, we can only walk on water if there is water above it
return isWater(up) ^ Baritone.settings().assumeWalkOnWater.value;
}
if (Baritone.settings().assumeWalkOnLava.value && isLava(block) && !isFlowing(x, y, z, state, bsi)) {
if (Baritone.settings().assumeWalkOnLava.value && MovementHelper.isLava(block) && !MovementHelper.isFlowing(x, y, z, state, bsi)) {
return true;
}
if (block == Blocks.GLASS || block == Blocks.STAINED_GLASS) {
return true;
}
if (block instanceof BlockSlab) {
if (!Baritone.settings().allowWalkOnBottomSlab.value) {
if (((BlockSlab) block).isDouble()) {
return true;
}
return state.getValue(BlockSlab.HALF) != BlockSlab.EnumBlockHalf.BOTTOM;
}
return true;
}
return block instanceof BlockStairs;
return false; // If we don't recognise it then we want to just return false to be safe.
}
static boolean canWalkOn(CalculationContext context, int x, int y, int z, IBlockState state) {
return context.precomputedData.canWalkOn(context.bsi, x, y, z, state);
}
static boolean canWalkOn(CalculationContext context, int x, int y, int z) {
return canWalkOn(context, x, y, z, context.get(x, y, z));
}
static boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos, IBlockState state) {

View File

@ -68,7 +68,7 @@ public class MovementAscend extends Movement {
public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) {
IBlockState toPlace = context.get(destX, y, destZ);
double additionalPlacementCost = 0;
if (!context.precomputedData.canWalkOn(context.bsi, x, y, z, toPlace)) {
if (!MovementHelper.canWalkOn(context, destX, y, destZ, toPlace)) {
additionalPlacementCost = context.costOfPlacingAt(destX, y, destZ, toPlace);
if (additionalPlacementCost >= COST_INF) {
return COST_INF;
@ -94,7 +94,7 @@ public class MovementAscend extends Movement {
}
}
IBlockState srcUp2 = context.get(x, y + 2, z); // used lower down anyway
if (context.get(x, y + 3, z).getBlock() instanceof BlockFalling && (context.precomputedData.canWalkThrough(context.bsi, x, y + 1, z) || !(srcUp2.getBlock() instanceof BlockFalling))) {//it would fall on us and possibly suffocate us
if (context.get(x, y + 3, z).getBlock() instanceof BlockFalling && (MovementHelper.canWalkThrough(context, x, y + 1, z) || !(srcUp2.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

View File

@ -101,7 +101,7 @@ public class MovementDescend extends Movement {
//C, D, etc determine the length of the fall
IBlockState below = context.get(destX, y - 2, destZ);
if (!context.precomputedData.canWalkOn(context.bsi, destX, y - 2, destZ, below)) {
if (!MovementHelper.canWalkOn(context, destX, y - 2, destZ, below)) {
dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below, res);
return;
}
@ -146,7 +146,7 @@ public class MovementDescend extends Movement {
int unprotectedFallHeight = fallHeight - (y - effectiveStartHeight); // equal to fallHeight - y + effectiveFallHeight, which is equal to -newY + effectiveFallHeight, which is equal to effectiveFallHeight - newY
double tentativeCost = WALK_OFF_BLOCK_COST + FALL_N_BLOCKS_COST[unprotectedFallHeight] + frontBreak + costSoFar;
if (MovementHelper.isWater(ontoBlock.getBlock())) {
if (!context.precomputedData.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) {
if (!MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) {
return false;
}
if (context.assumeWalkOnWater) {
@ -155,7 +155,7 @@ public class MovementDescend extends Movement {
if (MovementHelper.isFlowing(destX, newY, destZ, ontoBlock, context.bsi)) {
return false; // TODO flowing check required here?
}
if (!context.precomputedData.canWalkOn(context.bsi, destX, newY - 1, destZ)) {
if (!MovementHelper.canWalkOn(context, destX, newY - 1, destZ)) {
// we could punch right through the water into something else
return false;
}
@ -174,10 +174,10 @@ public class MovementDescend extends Movement {
effectiveStartHeight = newY;
continue;
}
if (context.precomputedData.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) {
if (MovementHelper.canWalkThrough(context, destX, newY, destZ, ontoBlock)) {
continue;
}
if (!context.precomputedData.canWalkOn(context.bsi, destX, newY, destZ, ontoBlock)) {
if (!MovementHelper.canWalkOn(context, destX, newY, destZ, ontoBlock)) {
return false;
}
if (MovementHelper.isBottomSlab(ontoBlock)) {

View File

@ -110,24 +110,24 @@ public class MovementDiagonal extends Movement {
}
public static void cost(CalculationContext context, int x, int y, int z, int destX, int destZ, MutableMoveResult res) {
if (!context.precomputedData.canWalkThrough(context.bsi, destX, y + 1, destZ)) {
if (!MovementHelper.canWalkThrough(context, destX, y + 1, destZ)) {
return;
}
IBlockState destInto = context.get(destX, y, destZ);
boolean ascend = false;
IBlockState destWalkOn;
boolean descend = false;
if (!context.precomputedData.canWalkThrough(context.bsi, destX, y, destZ, destInto)) {
if (!MovementHelper.canWalkThrough(context, destX, y, destZ, destInto)) {
ascend = true;
if (!context.allowDiagonalAscend || !context.precomputedData.canWalkThrough(context.bsi, x, y + 2, z) || !context.precomputedData.canWalkOn(context.bsi, destX, y, destZ, destInto) || !context.precomputedData.canWalkThrough(context.bsi, destX, y + 2, destZ)) {
if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context, x, y + 2, z) || !MovementHelper.canWalkOn(context, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context, destX, y + 2, destZ)) {
return;
}
destWalkOn = destInto;
} else {
destWalkOn = context.get(destX, y - 1, destZ);
if (!context.precomputedData.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn)) {
if (!MovementHelper.canWalkOn(context, destX, y - 1, destZ, destWalkOn)) {
descend = true;
if (!context.allowDiagonalDescend || !context.precomputedData.canWalkOn(context.bsi, destX, y - 2, destZ) || !context.precomputedData.canWalkThrough(context.bsi, destX, y - 1, destZ, destWalkOn)) {
if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context, destX, y - 1, destZ, destWalkOn)) {
return;
}
}
@ -169,17 +169,17 @@ public class MovementDiagonal extends Movement {
IBlockState pb0 = context.get(x, y, destZ);
IBlockState pb2 = context.get(destX, y, z);
if (ascend) {
boolean ATop = context.precomputedData.canWalkThrough(context.bsi, x, y + 2, destZ);
boolean AMid = context.precomputedData.canWalkThrough(context.bsi, x, y + 1, destZ);
boolean ALow = context.precomputedData.canWalkThrough(context.bsi, x, y, destZ, pb0);
boolean BTop = context.precomputedData.canWalkThrough(context.bsi, destX, y + 2, z);
boolean BMid = context.precomputedData.canWalkThrough(context.bsi, destX, y + 1, z);
boolean BLow = context.precomputedData.canWalkThrough(context.bsi, destX, y, z, pb2);
boolean ATop = MovementHelper.canWalkThrough(context, x, y + 2, destZ);
boolean AMid = MovementHelper.canWalkThrough(context, x, y + 1, destZ);
boolean ALow = MovementHelper.canWalkThrough(context, x, y, destZ, pb0);
boolean BTop = MovementHelper.canWalkThrough(context, destX, y + 2, z);
boolean BMid = MovementHelper.canWalkThrough(context, destX, y + 1, z);
boolean BLow = MovementHelper.canWalkThrough(context, destX, y, z, pb2);
if ((!(ATop && AMid && ALow) && !(BTop && BMid && BLow)) // no option
|| MovementHelper.avoidWalkingInto(pb0.getBlock()) // bad
|| MovementHelper.avoidWalkingInto(pb2.getBlock()) // bad
|| (ATop && AMid && context.precomputedData.canWalkOn(context.bsi, x, y, destZ, pb0)) // we could just ascend
|| (BTop && BMid && context.precomputedData.canWalkOn(context.bsi, destX, y, z, pb2)) // we could just ascend
|| (ATop && AMid && MovementHelper.canWalkOn(context, x, y, destZ, pb0)) // we could just ascend
|| (BTop && BMid && MovementHelper.canWalkOn(context, destX, y, z, pb2)) // we could just ascend
|| (!ATop && AMid && ALow) // head bonk A
|| (!BTop && BMid && BLow)) { // head bonk B
return;

View File

@ -59,7 +59,7 @@ public class MovementDownward extends Movement {
if (!context.allowDownward) {
return COST_INF;
}
if (!context.precomputedData.canWalkOn(context.bsi, x, y - 2, z)) {
if (!MovementHelper.canWalkOn(context, x, y - 2, z)) {
return COST_INF;
}
IBlockState down = context.get(x, y - 1, z);

View File

@ -74,7 +74,7 @@ public class MovementParkour extends Movement {
return;
}
IBlockState adj = context.get(x + xDiff, y - 1, z + zDiff);
if (context.precomputedData.canWalkOn(context.bsi, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now)
if (MovementHelper.canWalkOn(context, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now)
// second most common case -- we could just traverse not parkour
return;
}
@ -122,7 +122,7 @@ public class MovementParkour extends Movement {
// 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 (i <= 3 && context.allowParkourAscend && context.canSprint && context.precomputedData.canWalkOn(context.bsi, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) {
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;
res.z = destZ;
@ -135,7 +135,7 @@ public class MovementParkour extends Movement {
// 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
if (landingOn.getBlock() != Blocks.FARMLAND && context.precomputedData.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) {
if (landingOn.getBlock() != Blocks.FARMLAND && MovementHelper.canWalkOn(context, destX, y - 1, destZ, landingOn)) {
if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) {
res.x = destX;
res.y = y;

View File

@ -72,7 +72,7 @@ public class MovementTraverse extends Movement {
IBlockState pb1 = context.get(destX, y, destZ);
IBlockState destOn = context.get(destX, y - 1, destZ);
Block srcDown = context.getBlock(x, y - 1, z);
if (context.precomputedData.canWalkOn(context.bsi, destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge
if (MovementHelper.canWalkOn(context, destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge
double WC = WALK_ONE_BLOCK_COST;
boolean water = false;
if (MovementHelper.isWater(pb0.getBlock()) || MovementHelper.isWater(pb1.getBlock())) {

View File

@ -37,190 +37,17 @@ public class PrecomputedData { // TODO add isFullyPassable
PrecomputedDataForBlockState canWalkOn;
PrecomputedDataForBlockState canWalkThrough;
public PrecomputedData() { // currently designed for this to be remade on setting change, however that could always be changed
long startTime = System.nanoTime();
canWalkOn = new PrecomputedDataForBlockState((state) -> { // this is just copied from the old MovementHelperFunction
Block block = state.getBlock();
if (block == Blocks.AIR || block == Blocks.MAGMA) {
return Optional.of(false);
}
if (state.isBlockNormalCube()) {
return Optional.of(true);
}
if (block == Blocks.LADDER || (block == Blocks.VINE && Baritone.settings().allowVines.value)) { // TODO reconsider this
return Optional.of(true);
}
if (block == Blocks.FARMLAND || block == Blocks.GRASS_PATH) {
return Optional.of(true);
}
if (block == Blocks.ENDER_CHEST || block == Blocks.CHEST || block == Blocks.TRAPPED_CHEST) {
return Optional.of(true);
}
if (isWater(block)) {
return Optional.empty();
}
if (Baritone.settings().assumeWalkOnLava.value && MovementHelper.isLava(block)) {
return Optional.empty();
}
if (block instanceof BlockSlab) {
if (!Baritone.settings().allowWalkOnBottomSlab.value) {
if (((BlockSlab) block).isDouble()) {
return Optional.of(true);
}
return Optional.of(state.getValue(BlockSlab.HALF) != BlockSlab.EnumBlockHalf.BOTTOM);
}
return Optional.of(true);
}
return Optional.of(block instanceof BlockStairs);
}, (bsi, x, y, z, blockState) -> { // This should just be water or lava, and could probably be made more efficient
Block block = blockState.getBlock();
if (isWater(block)) {
// since this is called literally millions of times per second, the benefit of not allocating millions of useless "pos.up()"
// BlockPos s that we'd just garbage collect immediately is actually noticeable. I don't even think it's a decrease in readability
Block up = bsi.get0(x, y + 1, z).getBlock();
if (up == Blocks.WATERLILY || up == Blocks.CARPET) {
return true;
}
if (MovementHelper.isFlowing(x, y, z, blockState, bsi) || block == Blocks.FLOWING_WATER) {
// the only scenario in which we can walk on flowing water is if it's under still water with jesus off
return isWater(up) && !Baritone.settings().assumeWalkOnWater.value;
}
// if assumeWalkOnWater is on, we can only walk on water if there isn't water above it
// if assumeWalkOnWater is off, we can only walk on water if there is water above it
return isWater(up) ^ Baritone.settings().assumeWalkOnWater.value;
}
if (Baritone.settings().assumeWalkOnLava.value && MovementHelper.isLava(block) && !MovementHelper.isFlowing(x, y, z, blockState, bsi)) {
return true;
}
return false; // If we don't recognise it then we want to just return false to be safe.
});
canWalkThrough = new PrecomputedDataForBlockState((blockState) -> {
Block block = blockState.getBlock();
if (block == Blocks.AIR) {
return Optional.of(true);
}
if (block == Blocks.FIRE || block == Blocks.TRIPWIRE || block == Blocks.WEB || block == Blocks.END_PORTAL || block == Blocks.COCOA || block instanceof BlockSkull || block instanceof BlockTrapDoor || block == Blocks.END_ROD) {
return Optional.of(false);
}
if (Baritone.settings().blocksToAvoid.value.contains(block)) {
return Optional.of(false);
}
if (block instanceof BlockDoor || block instanceof BlockFenceGate) {
// Because there's no nice method in vanilla to check if a door is openable or not, we just have to assume
// that anything that isn't an iron door isn't openable, ignoring that some doors introduced in mods can't
// be opened by just interacting.
return Optional.of(block != Blocks.IRON_DOOR);
}
if (block == Blocks.CARPET) {
return Optional.empty();
}
if (block instanceof BlockSnow) {
if (blockState.getValue(BlockSnow.LAYERS) >= 3) {
return Optional.of(false);
}
return Optional.empty();
}
if (block instanceof BlockLiquid) {
if (blockState.getValue(BlockLiquid.LEVEL) != 0) {
return Optional.of(false);
} else {
return Optional.empty();
}
}
if (block instanceof BlockCauldron) {
return Optional.of(false);
}
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
return Optional.of(block.isPassable(null, null));
} catch (NullPointerException exception) {
System.out.println("Error");
return Optional.empty();
}
}, (bsi, x, y, z, blockState) -> {
Block block = blockState.getBlock();
if (block == Blocks.CARPET) {
return canWalkOn(bsi, x, y - 1, z);
}
if (block instanceof BlockSnow) { // TODO see if this case is necessary, shouldn't it also check this somewhere else?
return canWalkOn(bsi, x, y - 1, z);
}
if (block instanceof BlockLiquid) {
if (isFlowing(x, y, z, blockState, bsi)) {
return false;
}
// Everything after this point has to be a special case as it relies on the water not being flowing, which means a special case is needed.
if (Baritone.settings().assumeWalkOnWater.value) {
return false;
}
IBlockState up = bsi.get0(x, y + 1, z);
if (up.getBlock() instanceof BlockLiquid || up.getBlock() instanceof BlockLilyPad) {
return false;
}
return block == Blocks.WATER || block == Blocks.FLOWING_WATER;
}
return block.isPassable(bsi.access, bsi.isPassableBlockPos.setPos(x, y, z));
});
public PrecomputedData() {
canWalkOn = new PrecomputedDataForBlockState(MovementHelper::canWalkOnBlockState, MovementHelper::canWalkOnPosition);
canWalkThrough = new PrecomputedDataForBlockState(MovementHelper::canWalkThroughBlockState, MovementHelper::canWalkThroughPosition);
}
public boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
return canWalkOn.get(bsi, x, y, z, state);
}
public boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos, IBlockState state) {
return canWalkOn(new BlockStateInterface(ctx), pos.x, pos.y, pos.z, state);
}
public boolean canWalkOn(IPlayerContext ctx, BlockPos pos) {
return canWalkOn(new BlockStateInterface(ctx), pos.getX(), pos.getY(), pos.getZ());
}
public boolean canWalkOn(IPlayerContext ctx, BetterBlockPos pos) {
return canWalkOn(new BlockStateInterface(ctx), pos.x, pos.y, pos.z);
}
public boolean canWalkOn(BlockStateInterface bsi, int x, int y, int z) {
return canWalkOn(bsi, x, y, z, bsi.get0(x, y, z));
}
public boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
return false;
}
public boolean canWalkThrough(IPlayerContext ctx, BetterBlockPos pos) {
return canWalkThrough(new BlockStateInterface(ctx), pos.x, pos.y, pos.z);
}
public boolean canWalkThrough(BlockStateInterface bsi, int x, int y, int z) {
return canWalkThrough(bsi, x, y, z, bsi.get0(x, y, z));
}
/**
* Refresh the precomputed data, for use when settings have changed etc.
*/
public void refresh() {
canWalkThrough.refresh();
canWalkOn.refresh();
return canWalkThrough.get(bsi, x, y, z, state);
}
}