Merge pull request #180 from cabaletta/sanik

Gotta go fast, fixes #179, fixes #168
This commit is contained in:
Brady 2018-09-23 09:36:56 -05:00 committed by GitHub
commit b2cbce28b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 613 additions and 222 deletions

View File

@ -3,8 +3,8 @@
[![License](https://img.shields.io/github/license/cabaletta/baritone.svg)](LICENSE) [![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) [![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/), 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. 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).
<a href="https://github.com/cabaletta/baritone/blob/master/FEATURES.md">Features</a> <a href="https://github.com/cabaletta/baritone/blob/master/FEATURES.md">Features</a>

View File

@ -24,16 +24,12 @@ import baritone.pathing.calc.openset.BinaryHeapOpenSet;
import baritone.pathing.goals.Goal; import baritone.pathing.goals.Goal;
import baritone.pathing.movement.ActionCosts; import baritone.pathing.movement.ActionCosts;
import baritone.pathing.movement.CalculationContext; 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.pathing.path.IPath;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
import baritone.utils.Helper; import baritone.utils.Helper;
import baritone.utils.pathing.BetterBlockPos; import baritone.utils.pathing.BetterBlockPos;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.client.multiplayer.ChunkProviderClient;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import java.util.Collection; import java.util.Collection;
@ -45,7 +41,7 @@ import java.util.Optional;
* *
* @author leijurv * @author leijurv
*/ */
public class AStarPathFinder extends AbstractNodeCostSearch implements Helper { public final class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
private final Optional<HashSet<BetterBlockPos>> favoredPositions; private final Optional<HashSet<BetterBlockPos>> favoredPositions;
@ -104,12 +100,14 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
logDebug("Took " + (System.nanoTime() / 1000000L - startTime) + "ms, " + numMovementsConsidered + " movements considered"); logDebug("Took " + (System.nanoTime() / 1000000L - startTime) + "ms, " + numMovementsConsidered + " movements considered");
return Optional.of(new Path(startNode, currentNode, numNodes, goal)); return Optional.of(new Path(startNode, currentNode, numNodes, goal));
} }
Movement[] possibleMovements = getConnectedPositions(currentNodePos, calcContext);//movement that we could take that start at currentNodePos for (Moves moves : Moves.values()) {
for (Movement movementToGetToNeighbor : possibleMovements) { MoveResult res = moves.apply(calcContext, currentNodePos.x, currentNodePos.y, currentNodePos.z);
if (movementToGetToNeighbor == null) { numMovementsConsidered++;
double actionCost = res.cost;
if (actionCost >= ActionCosts.COST_INF) {
continue; continue;
} }
BetterBlockPos dest = movementToGetToNeighbor.getDest(); BetterBlockPos dest = new BetterBlockPos(res.destX, res.destY, res.destZ);
int chunkX = currentNodePos.x >> 4; int chunkX = currentNodePos.x >> 4;
int chunkZ = currentNodePos.z >> 4; int chunkZ = currentNodePos.z >> 4;
if (dest.x >> 4 != chunkX || dest.z >> 4 != chunkZ) { if (dest.x >> 4 != chunkX || dest.z >> 4 != chunkZ) {
@ -122,14 +120,11 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
} }
} }
} }
// TODO cache cost
double actionCost = movementToGetToNeighbor.getCost(calcContext);
numMovementsConsidered++;
if (actionCost >= ActionCosts.COST_INF) { if (actionCost >= ActionCosts.COST_INF) {
continue; continue;
} }
if (actionCost <= 0) { 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)) { if (favoring && favored.contains(dest)) {
// see issue #18 // see issue #18
@ -139,7 +134,7 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
double tentativeCost = currentNode.cost + actionCost; double tentativeCost = currentNode.cost + actionCost;
if (tentativeCost < neighbor.cost) { if (tentativeCost < neighbor.cost) {
if (tentativeCost < 0) { 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; double improvementBy = neighbor.cost - tentativeCost;
// there are floating point errors caused by random combinations of traverse and diagonal over a flat area // there are floating point errors caused by random combinations of traverse and diagonal over a flat area
@ -150,7 +145,6 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
continue; continue;
} }
neighbor.previous = currentNode; neighbor.previous = currentNode;
neighbor.previousMovement = movementToGetToNeighbor;
neighbor.cost = tentativeCost; neighbor.cost = tentativeCost;
neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal; neighbor.combinedCost = tentativeCost + neighbor.estimatedCostToGoal;
if (neighbor.isOpen) { if (neighbor.isOpen) {
@ -203,45 +197,4 @@ public class AStarPathFinder extends AbstractNodeCostSearch implements Helper {
logDebug("No path found =("); logDebug("No path found =(");
return Optional.empty(); 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),
};
}
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Integer, Double> 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<Integer, Double> 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<Integer, Double> 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<Integer, Double> 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);
}

View File

@ -89,7 +89,7 @@ class Path implements IPath {
LinkedList<Movement> tempMovements = new LinkedList<>(); // Instead, do it into a linked list, then convert at the end LinkedList<Movement> tempMovements = new LinkedList<>(); // Instead, do it into a linked list, then convert at the end
while (!current.equals(start)) { while (!current.equals(start)) {
tempPath.addFirst(current.pos); tempPath.addFirst(current.pos);
tempMovements.addFirst(current.previousMovement); tempMovements.addFirst(runBackwards(current.previous.pos, current.pos));
current = current.previous; current = current.previous;
} }
tempPath.addFirst(start.pos); tempPath.addFirst(start.pos);
@ -100,6 +100,16 @@ class Path implements IPath {
movements.addAll(tempMovements); 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. * Performs a series of checks to ensure that the assembly of the path went as expected.
*/ */

View File

@ -19,7 +19,6 @@ package baritone.pathing.calc;
import baritone.pathing.goals.Goal; import baritone.pathing.goals.Goal;
import baritone.pathing.movement.ActionCosts; import baritone.pathing.movement.ActionCosts;
import baritone.pathing.movement.Movement;
import baritone.utils.pathing.BetterBlockPos; import baritone.utils.pathing.BetterBlockPos;
/** /**
@ -62,12 +61,6 @@ public final class PathNode {
*/ */
PathNode previous; 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) * 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. * Instead of doing a costly member check in the open set, cache membership in each node individually too.
@ -85,7 +78,6 @@ public final class PathNode {
this.cost = ActionCosts.COST_INF; this.cost = ActionCosts.COST_INF;
this.goal = goal; this.goal = goal;
this.estimatedCostToGoal = goal.heuristic(pos); this.estimatedCostToGoal = goal.heuristic(pos);
this.previousMovement = null;
this.isOpen = false; this.isOpen = false;
} }

View File

@ -47,11 +47,12 @@ import java.util.Optional;
*/ */
public interface MovementHelper extends ActionCosts, Helper { 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(); 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 return b == Blocks.ICE // ice becomes water, and water can mess up the path
|| b instanceof BlockSilverfish // obvious reasons || b instanceof BlockSilverfish // obvious reasons
// call BlockStateInterface.get directly with x,y,z. no need to make 5 new BlockPos for no reason // call BlockStateInterface.get directly with x,y,z. no need to make 5 new BlockPos for no reason
@ -165,6 +166,10 @@ public interface MovementHelper extends ActionCosts, Helper {
} }
static boolean isReplacable(BlockPos pos, 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 // for MovementTraverse and MovementAscend
// block double plant defaults to true when the block doesn't match, so don't need to check that case // 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 // all other overrides just return true or false
@ -175,13 +180,19 @@ public interface MovementHelper extends ActionCosts, Helper {
* return ((Integer)worldIn.getBlockState(pos).getValue(LAYERS)).intValue() == 1; * 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) // 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 true;
} }
return state.getValue(BlockSnow.LAYERS) == 1;
} }
return state.getBlock().isReplaceable(mc.world, pos); if (block instanceof BlockDoublePlant) {
BlockDoublePlant.EnumPlantType kek = state.getValue(BlockDoublePlant.VARIANT);
return kek == BlockDoublePlant.EnumPlantType.FERN || kek == BlockDoublePlant.EnumPlantType.GRASS;
}
return state.getBlock().isReplaceable(null, null);
} }
static boolean isDoorPassable(BlockPos doorPos, BlockPos playerPos) { static boolean isDoorPassable(BlockPos doorPos, BlockPos playerPos) {
@ -314,24 +325,39 @@ public interface MovementHelper extends ActionCosts, Helper {
return canWalkOn(x, y, z, BlockStateInterface.get(x, y, 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) { 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 // 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(); return state.isBlockNormalCube();
} }
static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, boolean includeFalling) { static double getMiningDurationTicks(CalculationContext context, BetterBlockPos position, boolean includeFalling) {
IBlockState state = BlockStateInterface.get(position); 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) { 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, 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(); Block block = state.getBlock();
if (!canWalkThrough(position, state)) { if (!canWalkThrough(x, y, z, state)) {
if (!context.allowBreak()) { if (!context.allowBreak()) {
return COST_INF; return COST_INF;
} }
if (avoidBreaking(position, state)) { if (avoidBreaking(x, y, z, state)) {
return COST_INF; 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 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 +368,9 @@ public interface MovementHelper extends ActionCosts, Helper {
double result = m / strVsBlock; double result = m / strVsBlock;
if (includeFalling) { if (includeFalling) {
BetterBlockPos up = position.up(); IBlockState above = BlockStateInterface.get(x, y + 1, z);
IBlockState above = BlockStateInterface.get(up);
if (above.getBlock() instanceof BlockFalling) { if (above.getBlock() instanceof BlockFalling) {
result += getMiningDurationTicks(context, up, above, true); result += getMiningDurationTicks(context, x, y + 1, z, above, true);
} }
} }
return result; return result;
@ -437,15 +462,7 @@ public interface MovementHelper extends ActionCosts, Helper {
} }
static Movement generateMovementFallOrDescend(BetterBlockPos pos, BetterBlockPos dest, CalculationContext calcContext) { 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 x = dest.x;
int y = dest.y; int y = dest.y;

View File

@ -28,7 +28,6 @@ import baritone.utils.BlockStateInterface;
import baritone.utils.InputOverrideHandler; import baritone.utils.InputOverrideHandler;
import baritone.utils.Utils; import baritone.utils.Utils;
import baritone.utils.pathing.BetterBlockPos; import baritone.utils.pathing.BetterBlockPos;
import net.minecraft.block.Block;
import net.minecraft.block.BlockFalling; import net.minecraft.block.BlockFalling;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@ -55,46 +54,54 @@ public class MovementAscend extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { 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) { if (srcDown.getBlock() == Blocks.LADDER || srcDown.getBlock() == Blocks.VINE) {
return COST_INF; return COST_INF;
} }
// we can jump from soul sand, but not from a bottom slab // we can jump from soul sand, but not from a bottom slab
boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown); boolean jumpingFromBottomSlab = MovementHelper.isBottomSlab(srcDown);
IBlockState toPlace = BlockStateInterface.get(positionToPlace); IBlockState toPlace = BlockStateInterface.get(destX, y, destZ);
boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace); boolean jumpingToBottomSlab = MovementHelper.isBottomSlab(toPlace);
if (jumpingFromBottomSlab && !jumpingToBottomSlab) { if (jumpingFromBottomSlab && !jumpingToBottomSlab) {
return COST_INF;// the only thing we can ascend onto from a bottom slab is another bottom slab 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()) { if (!context.hasThrowaway()) {
return COST_INF; 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; return COST_INF;
} }
// TODO: add ability to place against .down() as well as the cardinal directions // 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 // 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 // Counterpoint to the above TODO ^ you should move then pillar instead of ascend
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
BlockPos against1 = positionToPlace.offset(HORIZONTALS[i]); int againstX = destX + HORIZONTALS[i].getXOffset();
if (against1.equals(src)) { int againstZ = destZ + HORIZONTALS[i].getZOffset();
if (againstX == x && againstZ == z) {
continue; continue;
} }
if (MovementHelper.canPlaceAgainst(against1)) { if (MovementHelper.canPlaceAgainst(againstX, y, againstZ)) {
return JUMP_ONE_BLOCK_COST + WALK_ONE_BLOCK_COST + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context); hasToPlace = true;
break;
} }
} }
if (!hasToPlace) { // didn't find a valid place =(
return COST_INF; return COST_INF;
} }
if (BlockStateInterface.get(src.up(3)).getBlock() instanceof BlockFalling) {//it would fall on us and possibly suffocate us }
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 // HOWEVER, we assume that we're standing in the start position
// that means that src and src.up(1) are both air // 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 // maybe they aren't now, but they will be by the time this starts
Block srcUp = BlockStateInterface.get(src.up(1)).getBlock(); if (!(BlockStateInterface.getBlock(x, y + 1, z) instanceof BlockFalling) || !((srcUp2 = BlockStateInterface.get(x, y + 2, z)).getBlock() instanceof BlockFalling)) {
Block srcUp2 = BlockStateInterface.get(src.up(2)).getBlock();
if (!(srcUp instanceof BlockFalling) || !(srcUp2 instanceof BlockFalling)) {
// if both of those are BlockFalling, that means that by standing on src // if both of those are BlockFalling, that means that by standing on src
// (the presupposition of this Movement) // (the presupposition of this Movement)
// we have necessarily already cleared the entire BlockFalling stack // we have necessarily already cleared the entire BlockFalling stack
@ -109,15 +116,41 @@ public class MovementAscend extends Movement {
// it's possible srcUp is AIR from the start, and srcUp2 is falling // 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 // 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; double walk;
if (jumpingToBottomSlab && !jumpingFromBottomSlab) { if (jumpingToBottomSlab) {
return walk + getTotalHardnessOfBlocksToBreak(context); // we don't hit space we just walk into the slab 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
} }
if (!jumpingToBottomSlab && toPlace.getBlock().equals(Blocks.SOUL_SAND)) { } else {
walk *= WALK_ONE_OVER_SOUL_SAND_COST / WALK_ONE_BLOCK_COST; if (toPlace.getBlock() == Blocks.SOUL_SAND) {
walk = WALK_ONE_OVER_SOUL_SAND_COST;
} else {
walk = WALK_ONE_BLOCK_COST;
} }
// we hit space immediately on entering this action }
return Math.max(JUMP_ONE_BLOCK_COST, walk) + getTotalHardnessOfBlocksToBreak(context);
// cracks knuckles
double totalCost = 0;
totalCost += walk;
if (hasToPlace) {
totalCost += context.placeBlockCost();
}
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;
}
totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 1, destZ, false);
if (totalCost >= COST_INF) {
return COST_INF;
}
totalCost += MovementHelper.getMiningDurationTicks(context, destX, y + 2, destZ, true);
return totalCost;
} }
@Override @Override

View File

@ -17,6 +17,7 @@
package baritone.pathing.movement.movements; package baritone.pathing.movement.movements;
import baritone.Baritone;
import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.CalculationContext;
import baritone.pathing.movement.Movement; import baritone.pathing.movement.Movement;
import baritone.pathing.movement.MovementHelper; import baritone.pathing.movement.MovementHelper;
@ -26,11 +27,16 @@ import baritone.utils.BlockStateInterface;
import baritone.utils.InputOverrideHandler; import baritone.utils.InputOverrideHandler;
import baritone.utils.pathing.BetterBlockPos; import baritone.utils.pathing.BetterBlockPos;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockFalling;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
public class MovementDescend extends Movement { public class MovementDescend extends Movement {
private static final Tuple<Integer, Double> IMPOSSIBLE = new Tuple<>(0, COST_INF);
private int numTicks = 0; private int numTicks = 0;
public MovementDescend(BetterBlockPos start, BetterBlockPos end) { public MovementDescend(BetterBlockPos start, BetterBlockPos end) {
@ -45,24 +51,103 @@ public class MovementDescend extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { protected double calculateCost(CalculationContext context) {
Block fromDown = BlockStateInterface.get(src.down()).getBlock(); Tuple<Integer, Double> 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 Tuple<Integer, 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) { if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) {
return COST_INF; return IMPOSSIBLE;
} }
if (!MovementHelper.canWalkOn(positionToPlace)) {
return COST_INF; double totalCost = 0;
totalCost += MovementHelper.getMiningDurationTicks(context, destX, y - 1, destZ, false);
if (totalCost >= COST_INF) {
return IMPOSSIBLE;
} }
Block tmp1 = BlockStateInterface.get(dest).getBlock(); 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 dynamicFallCost(context, x, y, z, destX, destZ, totalCost);
}
Block tmp1 = BlockStateInterface.get(destX, y - 1, destZ).getBlock();
if (tmp1 == Blocks.LADDER || tmp1 == Blocks.VINE) { 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) // 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; double walk = WALK_OFF_BLOCK_COST;
if (fromDown == Blocks.SOUL_SAND) { if (fromDown == Blocks.SOUL_SAND) {
// use this ratio to apply the soul sand speed penalty to our 0.8 block distance // use this ratio to apply the soul sand speed penalty to our 0.8 block distance
walk = WALK_ONE_OVER_SOUL_SAND_COST; walk = WALK_ONE_OVER_SOUL_SAND_COST;
} }
return walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST) + getTotalHardnessOfBlocksToBreak(context); totalCost += walk + Math.max(FALL_N_BLOCKS_COST[1], CENTER_AFTER_FALL_COST);
return new Tuple<>(y - 1, totalCost);
}
public static Tuple<Integer, Double> 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;
}
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;
}
}
} }
@Override @Override

View File

@ -53,40 +53,43 @@ public class MovementDiagonal extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { 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) { if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) {
return COST_INF; 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; return COST_INF;
} }
BetterBlockPos destDown = dest.down(); IBlockState destWalkOn = BlockStateInterface.get(destX, y - 1, destZ);
IBlockState destWalkOn = BlockStateInterface.get(destDown); if (!MovementHelper.canWalkOn(destX, y - 1, destZ, destWalkOn)) {
if (!MovementHelper.canWalkOn(destDown, destWalkOn)) {
return COST_INF; return COST_INF;
} }
double multiplier = WALK_ONE_BLOCK_COST; double multiplier = WALK_ONE_BLOCK_COST;
// For either possible soul sand, that affects half of our walking // 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; multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2;
} }
if (fromDown == Blocks.SOUL_SAND) { if (fromDown == Blocks.SOUL_SAND) {
multiplier += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; 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)) { if (cuttingOver1 instanceof BlockMagma || BlockStateInterface.isLava(cuttingOver1)) {
return COST_INF; 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)) { if (cuttingOver2 instanceof BlockMagma || BlockStateInterface.isLava(cuttingOver2)) {
return COST_INF; return COST_INF;
} }
IBlockState pb0 = BlockStateInterface.get(positionsToBreak[0]); IBlockState pb0 = BlockStateInterface.get(x, y, destZ);
IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); IBlockState pb1 = BlockStateInterface.get(x, y + 1, destZ);
IBlockState pb2 = BlockStateInterface.get(positionsToBreak[2]); IBlockState pb2 = BlockStateInterface.get(destX, y, z);
IBlockState pb3 = BlockStateInterface.get(positionsToBreak[3]); IBlockState pb3 = BlockStateInterface.get(destX, y + 1, z);
double optionA = MovementHelper.getMiningDurationTicks(context, positionsToBreak[0], pb0, false) + MovementHelper.getMiningDurationTicks(context, positionsToBreak[1], pb1, true); double optionA = MovementHelper.getMiningDurationTicks(context, x, y, destZ, pb0, false) + MovementHelper.getMiningDurationTicks(context, x, y + 1, destZ, pb1, true);
double optionB = MovementHelper.getMiningDurationTicks(context, positionsToBreak[2], pb2, false) + MovementHelper.getMiningDurationTicks(context, positionsToBreak[3], pb3, 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) { if (optionA != 0 && optionB != 0) {
return COST_INF; return COST_INF;
} }
@ -100,7 +103,7 @@ public class MovementDiagonal extends Movement {
return COST_INF; 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 // Ignore previous multiplier
// Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water // Whatever we were walking on (possibly soul sand) doesn't matter as we're actually floating on water
// Not even touching the blocks below // Not even touching the blocks below

View File

@ -43,17 +43,21 @@ public class MovementDownward extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { 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; return COST_INF;
} }
IBlockState d = BlockStateInterface.get(dest); IBlockState d = BlockStateInterface.get(x, y - 1, z);
Block td = d.getBlock(); Block td = d.getBlock();
boolean ladder = td == Blocks.LADDER || td == Blocks.VINE; boolean ladder = td == Blocks.LADDER || td == Blocks.VINE;
if (ladder) { if (ladder) {
return LADDER_DOWN_ONE_COST; return LADDER_DOWN_ONE_COST;
} else { } 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 // 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);
} }
} }

View File

@ -26,13 +26,10 @@ import baritone.pathing.movement.MovementState.MovementStatus;
import baritone.pathing.movement.MovementState.MovementTarget; import baritone.pathing.movement.MovementState.MovementTarget;
import baritone.utils.*; import baritone.utils.*;
import baritone.utils.pathing.BetterBlockPos; 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.entity.player.InventoryPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items; import net.minecraft.init.Items;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.Tuple;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -48,58 +45,11 @@ public class MovementFall extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { protected double calculateCost(CalculationContext context) {
Block fromDown = BlockStateInterface.get(src.down()).getBlock(); Tuple<Integer, Double> result = MovementDescend.cost(context, src.x, src.y, src.z, dest.x, dest.z);
if (fromDown == Blocks.LADDER || fromDown == Blocks.VINE) { if (result.getFirst() != dest.y) {
return COST_INF; return COST_INF; // doesn't apply to us, this position is a descend not a fall
} }
IBlockState fallOnto = BlockStateInterface.get(dest.down()); return result.getSecond();
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;
} }
@Override @Override

View File

@ -48,9 +48,13 @@ public class MovementPillar extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { 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; 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 (!ladder) {
if (fromDownDown.getBlock() instanceof BlockLadder || fromDownDown.getBlock() instanceof BlockVine) { if (fromDownDown.getBlock() instanceof BlockLadder || fromDownDown.getBlock() instanceof BlockVine) {
return COST_INF; return COST_INF;
@ -65,24 +69,23 @@ public class MovementPillar extends Movement {
return COST_INF; return COST_INF;
} }
if (fromDown instanceof BlockVine) { if (fromDown instanceof BlockVine) {
if (getAgainst(src) == null) { if (!hasAgainst(x, y, z)) {
return COST_INF; return COST_INF;
} }
} }
BetterBlockPos toBreakPos = src.up(2); IBlockState toBreak = BlockStateInterface.get(x, y + 2, z);
IBlockState toBreak = BlockStateInterface.get(toBreakPos);
Block toBreakBlock = toBreak.getBlock(); Block toBreakBlock = toBreak.getBlock();
if (toBreakBlock instanceof BlockFenceGate) { if (toBreakBlock instanceof BlockFenceGate) {
return COST_INF; return COST_INF;
} }
Block srcUp = null; Block srcUp = null;
if (BlockStateInterface.isWater(toBreakBlock) && BlockStateInterface.isWater(fromDown)) { if (BlockStateInterface.isWater(toBreakBlock) && BlockStateInterface.isWater(fromDown)) {
srcUp = BlockStateInterface.get(dest).getBlock(); srcUp = BlockStateInterface.get(x, y + 1, z).getBlock();
if (BlockStateInterface.isWater(srcUp)) { if (BlockStateInterface.isWater(srcUp)) {
return LADDER_UP_ONE_COST; 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) { if (hardness >= COST_INF) {
return COST_INF; return COST_INF;
} }
@ -90,12 +93,11 @@ public class MovementPillar extends Movement {
if (toBreakBlock instanceof BlockLadder || toBreakBlock instanceof BlockVine) { 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 hardness = 0; // we won't actually need to break the ladder / vine because we're going to use it
} else { } else {
BlockPos chkPos = src.up(3); IBlockState check = BlockStateInterface.get(x, y + 3, z);
IBlockState check = BlockStateInterface.get(chkPos);
if (check.getBlock() instanceof BlockFalling) { if (check.getBlock() instanceof BlockFalling) {
// see MovementAscend's identical check for breaking a falling block above our head // see MovementAscend's identical check for breaking a falling block above our head
if (srcUp == null) { if (srcUp == null) {
srcUp = BlockStateInterface.get(dest).getBlock(); srcUp = BlockStateInterface.get(x, y + 1, z).getBlock();
} }
if (!(toBreakBlock instanceof BlockFalling) || !(srcUp instanceof BlockFalling)) { if (!(toBreakBlock instanceof BlockFalling) || !(srcUp instanceof BlockFalling)) {
return COST_INF; return COST_INF;
@ -120,6 +122,13 @@ public class MovementPillar extends Movement {
} }
} }
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) { public static BlockPos getAgainst(BlockPos vine) {
if (BlockStateInterface.get(vine.north()).isBlockNormalCube()) { if (BlockStateInterface.get(vine.north()).isBlockNormalCube()) {
return vine.north(); return vine.north();

View File

@ -57,11 +57,15 @@ public class MovementTraverse extends Movement {
@Override @Override
protected double calculateCost(CalculationContext context) { protected double calculateCost(CalculationContext context) {
IBlockState pb0 = BlockStateInterface.get(positionsToBreak[0]); return cost(context, src.x, src.y, src.z, dest.x, dest.z);
IBlockState pb1 = BlockStateInterface.get(positionsToBreak[1]); }
IBlockState destOn = BlockStateInterface.get(positionToPlace);
Block srcDown = BlockStateInterface.getBlock(src.down()); public static double cost(CalculationContext context, int x, int y, int z, int destX, int destZ) {
if (MovementHelper.canWalkOn(positionToPlace, destOn)) {//this is a walk, not a bridge 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; double WC = WALK_ONE_BLOCK_COST;
if (BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock())) { if (BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock())) {
WC = WALK_ONE_IN_WATER_COST; WC = WALK_ONE_IN_WATER_COST;
@ -73,11 +77,11 @@ public class MovementTraverse extends Movement {
WC += (WALK_ONE_OVER_SOUL_SAND_COST - WALK_ONE_BLOCK_COST) / 2; 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) { if (hardness1 >= COST_INF) {
return 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 (hardness1 == 0 && hardness2 == 0) {
if (WC == WALK_ONE_BLOCK_COST && context.canSprint()) { 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 // 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 @@ public class MovementTraverse extends Movement {
if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) { if (srcDown == Blocks.LADDER || srcDown == Blocks.VINE) {
return COST_INF; 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()); boolean throughWater = BlockStateInterface.isWater(pb0.getBlock()) || BlockStateInterface.isWater(pb1.getBlock());
if (BlockStateInterface.isWater(destOn.getBlock()) && throughWater) { if (BlockStateInterface.isWater(destOn.getBlock()) && throughWater) {
return COST_INF; return COST_INF;
@ -103,15 +107,21 @@ public class MovementTraverse extends Movement {
if (!context.hasThrowaway()) { if (!context.hasThrowaway()) {
return COST_INF; 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; double WC = throughWater ? WALK_ONE_IN_WATER_COST : WALK_ONE_BLOCK_COST;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
BlockPos against1 = dest.offset(HORIZONTALS[i]); int againstX = destX + HORIZONTALS[i].getXOffset();
if (against1.equals(src)) { int againstZ = destZ + HORIZONTALS[i].getZOffset();
if (againstX == x && againstZ == z) {
continue; continue;
} }
against1 = against1.down(); if (MovementHelper.canPlaceAgainst(againstX, y - 1, againstZ)) {
if (MovementHelper.canPlaceAgainst(against1)) { return WC + context.placeBlockCost() + hardness1 + hardness2;
return WC + context.placeBlockCost() + getTotalHardnessOfBlocksToBreak(context);
} }
} }
if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) { if (srcDown == Blocks.SOUL_SAND || (srcDown instanceof BlockSlab && !((BlockSlab) srcDown).isDouble())) {
@ -121,7 +131,7 @@ public class MovementTraverse extends Movement {
return COST_INF; // this is obviously impossible return COST_INF; // this is obviously impossible
} }
WC = WC * SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST;//since we are placing, we are sneaking 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; return COST_INF;
// Out.log("Can't walk on " + Baritone.get(positionsToPlace[0]).getBlock()); // Out.log("Can't walk on " + Baritone.get(positionsToPlace[0]).getBlock());

View File

@ -98,6 +98,9 @@ public class BlockStateInterface implements Helper {
return get(pos).getBlock(); 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 * Returns whether or not the specified block is

View File

@ -27,14 +27,9 @@ import baritone.behavior.PathingBehavior;
import baritone.cache.ChunkPacker; import baritone.cache.ChunkPacker;
import baritone.cache.Waypoint; import baritone.cache.Waypoint;
import baritone.cache.WorldProvider; import baritone.cache.WorldProvider;
import baritone.pathing.calc.AStarPathFinder;
import baritone.pathing.calc.AbstractNodeCostSearch; import baritone.pathing.calc.AbstractNodeCostSearch;
import baritone.pathing.goals.*; 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.pathing.movement.MovementHelper;
import baritone.utils.pathing.BetterBlockPos;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.client.multiplayer.ChunkProviderClient; import net.minecraft.client.multiplayer.ChunkProviderClient;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@ -433,7 +428,8 @@ public class ExampleBaritoneControl extends Behavior implements Helper {
event.cancel(); event.cancel();
return; return;
} }
if (msg.equals("costs")) { // TODO
/*if (msg.equals("costs")) {
Movement[] movements = AStarPathFinder.getConnectedPositions(new BetterBlockPos(playerFeet()), new CalculationContext()); Movement[] movements = AStarPathFinder.getConnectedPositions(new BetterBlockPos(playerFeet()), new CalculationContext());
List<Movement> moves = new ArrayList<>(Arrays.asList(movements)); List<Movement> moves = new ArrayList<>(Arrays.asList(movements));
while (moves.contains(null)) { while (moves.contains(null)) {
@ -451,6 +447,6 @@ public class ExampleBaritoneControl extends Behavior implements Helper {
} }
event.cancel(); event.cancel();
return; return;
} }*/
} }
} }

View File

@ -134,4 +134,24 @@ public final class BetterBlockPos extends BlockPos {
Vec3i vec = dir.getDirectionVec(); Vec3i vec = dir.getDirectionVec();
return new BetterBlockPos(x + vec.getX() * dist, y + vec.getY() * dist, z + vec.getZ() * dist); 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);
}
} }