Implement Leijurv's precomputed data idea

This commit is contained in:
scorbett123 2022-05-31 14:43:58 +01:00
parent 17a2aa42e9
commit af1eb58bb8
23 changed files with 422 additions and 42 deletions

View File

@ -17,6 +17,7 @@
package baritone.api;
import baritone.api.event.events.SettingChangedEvent;
import baritone.api.utils.NotificationHelper;
import baritone.api.utils.SettingsUtil;
import baritone.api.utils.TypeUtils;
@ -1302,6 +1303,11 @@ public final class Settings {
return value;
}
public void set(T value) {
this.value = value;
BaritoneAPI.getProvider().getAllBaritones().forEach(iBaritone -> iBaritone.getGameEventHandler().onSettingChanged(new SettingChangedEvent(this)));
}
public final String getName() {
return name;
}

View File

@ -0,0 +1,33 @@
/*
* 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.api.event.events;
import baritone.api.Settings;
public class SettingChangedEvent {
private final Settings.Setting<?> setting;
public SettingChangedEvent(Settings.Setting<?> setting) {
this.setting = setting;
}
public Settings.Setting<?> getSetting() {
return setting;
}
}

View File

@ -71,4 +71,7 @@ public interface AbstractGameEventListener extends IGameEventListener {
@Override
default void onPathEvent(PathEvent event) {}
@Override
default void onSettingChanged(SettingChangedEvent event) {}
}

View File

@ -144,4 +144,11 @@ public interface IGameEventListener {
* @param event The event
*/
void onPathEvent(PathEvent event);
/**
* When the player changes a setting
*
* @param event The event
*/
void onSettingChanged(SettingChangedEvent event);
}

View File

@ -176,7 +176,7 @@ public class SettingsUtil {
/**
* This should always be the same as whether the setting can be parsed from or serialized to a string
*
* @param the setting
* @param setting The Setting
* @return true if the setting can not be set or read by the user
*/
public static boolean javaOnlySetting(Settings.Setting setting) {
@ -199,7 +199,7 @@ public class SettingsUtil {
if (!intendedType.isInstance(parsed)) {
throw new IllegalStateException(ioMethod + " parser returned incorrect type, expected " + intendedType + " got " + parsed + " which is " + parsed.getClass());
}
setting.value = parsed;
setting.set(parsed);
}
private interface ISettingParser<T> {

View File

@ -33,6 +33,7 @@ import baritone.pathing.calc.AbstractNodeCostSearch;
import baritone.pathing.movement.CalculationContext;
import baritone.pathing.movement.MovementHelper;
import baritone.pathing.path.PathExecutor;
import baritone.pathing.precompute.PrecomputedData;
import baritone.utils.PathRenderer;
import baritone.utils.PathingCommandContext;
import baritone.utils.pathing.Favoring;
@ -74,8 +75,11 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
private final LinkedBlockingQueue<PathEvent> toDispatch = new LinkedBlockingQueue<>();
public PrecomputedData precomputedData;
public PathingBehavior(Baritone baritone) {
super(baritone);
precomputedData = new PrecomputedData();
}
private void queuePathEvent(PathEvent event) {
@ -259,7 +263,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
if (command instanceof PathingCommandContext) {
context = ((PathingCommandContext) command).desiredCalcContext;
} else {
context = new CalculationContext(baritone, true);
context = new CalculationContext(baritone, true, precomputedData);
}
if (goal == null) {
return false;
@ -418,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 (!MovementHelper.canWalkOn(ctx, feet.down())) {
if (!precomputedData.canWalkOn(ctx, feet.down())) {
if (ctx.player().onGround) {
double playerX = ctx.player().posX;
double playerZ = ctx.player().posZ;
@ -437,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 (MovementHelper.canWalkOn(ctx, possibleSupport.down()) && MovementHelper.canWalkThrough(ctx, possibleSupport) && MovementHelper.canWalkThrough(ctx, possibleSupport.up())) {
if (precomputedData.canWalkOn(ctx, possibleSupport.down()) && precomputedData.canWalkThrough(ctx, possibleSupport) && context.precomputedData.canWalkThrough(ctx, possibleSupport.up())) {
// this is plausible
//logDebug("Faking path start assuming player is standing off the edge of a block");
return possibleSupport;
@ -447,7 +451,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
} else {
// !onGround
// we're in the middle of a jump
if (MovementHelper.canWalkOn(ctx, feet.down().down())) {
if (precomputedData.canWalkOn(ctx, feet.down().down())) {
//logDebug("Faking path start assuming player is midair and falling");
return feet.down();
}
@ -456,6 +460,11 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
return feet;
}
@Override
public void onSettingChanged(SettingChangedEvent event) {
this.precomputedData.refresh();
}
/**
* In a new thread, pathfind to target blockpos
*

View File

@ -147,7 +147,8 @@ public class SetCommand extends Command {
throw new CommandInvalidTypeException(args.consumed(), "a toggleable setting", "some other setting");
}
//noinspection unchecked
((Settings.Setting<Boolean>) setting).value ^= true;
Settings.Setting<Boolean> asBoolSetting = (Settings.Setting<Boolean>) setting;
asBoolSetting.set(!asBoolSetting.value);
logDirect(String.format(
"Toggled setting %s to %s",
setting.getName(),

View File

@ -156,6 +156,11 @@ public final class GameEventHandler implements IEventBus, Helper {
listeners.forEach(l -> l.onPathEvent(event));
}
@Override
public void onSettingChanged(SettingChangedEvent event) {
listeners.forEach(l -> l.onSettingChanged(event));
}
@Override
public final void registerEventListener(IGameEventListener listener) {
this.listeners.add(listener);

View File

@ -21,6 +21,7 @@ import baritone.Baritone;
import baritone.api.IBaritone;
import baritone.api.pathing.movement.ActionCosts;
import baritone.cache.WorldData;
import baritone.pathing.precompute.PrecomputedData;
import baritone.utils.BlockStateInterface;
import baritone.utils.ToolSet;
import baritone.utils.pathing.BetterWorldBorder;
@ -76,11 +77,14 @@ public class CalculationContext {
public final double walkOnWaterOnePenalty;
public final BetterWorldBorder worldBorder;
public final PrecomputedData precomputedData;
public CalculationContext(IBaritone baritone) {
this(baritone, false);
this(baritone, false, new PrecomputedData());
}
public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) {
public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread, PrecomputedData precomputedData) {
this.precomputedData = precomputedData;
this.safeForThreadedUse = forUseOnAnotherThread;
this.baritone = baritone;
EntityPlayerSP player = baritone.getPlayerContext().player();

View File

@ -88,6 +88,7 @@ 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(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
Block block = state.getBlock();
if (block == Blocks.AIR) { // early return for most common case
@ -285,6 +286,8 @@ public interface MovementHelper extends ActionCosts, Helper {
* through? Includes water because we know that we automatically jump on
* water
*
* If changing something in this function remember to also change it in precomputed data
*
* @param bsi Block state provider
* @param x The block's x position
* @param y The block's y position

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 (!MovementHelper.canWalkOn(context.bsi, destX, y, destZ, toPlace)) {
if (!context.precomputedData.canWalkOn(context.bsi, x, y, z, 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 && (MovementHelper.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 && (context.precomputedData.canWalkThrough(context.bsi, 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 (!MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ, below)) {
if (!context.precomputedData.canWalkOn(context.bsi, destX, y - 2, destZ, below)) {
dynamicFallCost(context, x, y, z, destX, destZ, totalCost, below, res);
return;
}
@ -130,7 +130,7 @@ public class MovementDescend extends Movement {
// and potentially replace the water we're going to fall into
return false;
}
if (!MovementHelper.canWalkThrough(context.bsi, destX, y - 2, destZ, below)) {
if (!context.precomputedData.canWalkThrough(context.bsi, destX, y - 2, destZ, below)) {
return false;
}
double costSoFar = 0;
@ -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 (!MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) {
if (!context.precomputedData.canWalkThrough(context.bsi, 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 (!MovementHelper.canWalkOn(context.bsi, destX, newY - 1, destZ)) {
if (!context.precomputedData.canWalkOn(context.bsi, 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 (MovementHelper.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) {
if (context.precomputedData.canWalkThrough(context.bsi, destX, newY, destZ, ontoBlock)) {
continue;
}
if (!MovementHelper.canWalkOn(context.bsi, destX, newY, destZ, ontoBlock)) {
if (!context.precomputedData.canWalkOn(context.bsi, destX, newY, destZ, ontoBlock)) {
return false;
}
if (MovementHelper.isBottomSlab(ontoBlock)) {

View File

@ -58,9 +58,9 @@ public class MovementDiagonal extends Movement {
}
@Override
protected boolean safeToCancel(MovementState state) {
protected boolean safeToCancel(MovementState state) { // TODO move this function to use precomputed data, not urgent as it only runs once a tick
//too simple. backfill does not work after cornering with this
//return MovementHelper.canWalkOn(ctx, ctx.playerFeet().down());
//return context.precomputedData.canWalkOn(ctx, ctx.playerFeet().down());
EntityPlayerSP player = ctx.player();
double offset = 0.25;
double x = player.posX;
@ -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 (!MovementHelper.canWalkThrough(context.bsi, destX, y + 1, destZ)) {
if (!context.precomputedData.canWalkThrough(context.bsi, destX, y + 1, destZ)) {
return;
}
IBlockState destInto = context.get(destX, y, destZ);
boolean ascend = false;
IBlockState destWalkOn;
boolean descend = false;
if (!MovementHelper.canWalkThrough(context.bsi, destX, y, destZ, destInto)) {
if (!context.precomputedData.canWalkThrough(context.bsi, destX, y, destZ, destInto)) {
ascend = true;
if (!context.allowDiagonalAscend || !MovementHelper.canWalkThrough(context.bsi, x, y + 2, z) || !MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) || !MovementHelper.canWalkThrough(context.bsi, destX, y + 2, destZ)) {
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)) {
return;
}
destWalkOn = destInto;
} else {
destWalkOn = context.get(destX, y - 1, destZ);
if (!MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn)) {
if (!context.precomputedData.canWalkOn(context.bsi, destX, y - 1, destZ, destWalkOn)) {
descend = true;
if (!context.allowDiagonalDescend || !MovementHelper.canWalkOn(context.bsi, destX, y - 2, destZ) || !MovementHelper.canWalkThrough(context.bsi, destX, y - 1, destZ, destWalkOn)) {
if (!context.allowDiagonalDescend || !context.precomputedData.canWalkOn(context.bsi, destX, y - 2, destZ) || !context.precomputedData.canWalkThrough(context.bsi, 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 = MovementHelper.canWalkThrough(context.bsi, x, y + 2, destZ);
boolean AMid = MovementHelper.canWalkThrough(context.bsi, x, y + 1, destZ);
boolean ALow = MovementHelper.canWalkThrough(context.bsi, x, y, destZ, pb0);
boolean BTop = MovementHelper.canWalkThrough(context.bsi, destX, y + 2, z);
boolean BMid = MovementHelper.canWalkThrough(context.bsi, destX, y + 1, z);
boolean BLow = MovementHelper.canWalkThrough(context.bsi, destX, y, z, pb2);
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);
if ((!(ATop && AMid && ALow) && !(BTop && BMid && BLow)) // no option
|| MovementHelper.avoidWalkingInto(pb0.getBlock()) // bad
|| MovementHelper.avoidWalkingInto(pb2.getBlock()) // bad
|| (ATop && AMid && MovementHelper.canWalkOn(context.bsi, x, y, destZ, pb0)) // we could just ascend
|| (BTop && BMid && MovementHelper.canWalkOn(context.bsi, destX, y, z, pb2)) // we could just ascend
|| (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 && 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 (!MovementHelper.canWalkOn(context.bsi, x, y - 2, z)) {
if (!context.precomputedData.canWalkOn(context.bsi, 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 (MovementHelper.canWalkOn(context.bsi, x + xDiff, y - 1, z + zDiff, adj)) { // don't parkour if we could just traverse (for now)
if (context.precomputedData.canWalkOn(context.bsi, 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 && MovementHelper.canWalkOn(context.bsi, destX, y, destZ, destInto) && checkOvershootSafety(context.bsi, destX + xDiff, y + 1, destZ + zDiff)) {
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)) {
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 && MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) {
if (landingOn.getBlock() != Blocks.FARMLAND && context.precomputedData.canWalkOn(context.bsi, destX, y - 1, destZ, landingOn)) {
if (checkOvershootSafety(context.bsi, destX + xDiff, y, destZ + zDiff)) {
res.x = destX;
res.y = y;

View File

@ -120,7 +120,7 @@ public class MovementPillar extends Movement {
}
}
// this is commented because it may have had a purpose, but it's very unclear what it was. it's from the minebot era.
//if (!MovementHelper.canWalkOn(chkPos, check) || MovementHelper.canWalkThrough(chkPos, check)) {//if the block above where we want to break is not a full block, don't do it
//if (!context.precomputedData.canWalkOn(chkPos, check) || context.precomputedData.canWalkThrough(chkPos, check)) {//if the block above where we want to break is not a full block, don't do it
// TODO why does canWalkThrough mean this action is COST_INF?
// BlockFalling makes sense, and !canWalkOn deals with weird cases like if it were lava
// but I don't understand why canWalkThrough makes it impossible

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 (MovementHelper.canWalkOn(context.bsi, destX, y - 1, destZ, destOn)) {//this is a walk, not a bridge
if (context.precomputedData.canWalkOn(context.bsi, 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

@ -350,7 +350,7 @@ public class PathExecutor implements IPathExecutor, Helper {
behavior.baritone.getInputOverrideHandler().setInputForceState(Input.SPRINT, false);
// first and foremost, if allowSprint is off, or if we don't have enough hunger, don't try and sprint
if (!new CalculationContext(behavior.baritone).canSprint) {
if (!new CalculationContext(behavior.baritone, false, null).canSprint) {
return false;
}
IMovement current = path.movements().get(pathPosition);

View File

@ -0,0 +1,237 @@
/*
* 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.precompute;
import baritone.Baritone;
import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.IPlayerContext;
import baritone.pathing.movement.MovementHelper;
import baritone.utils.BlockStateInterface;
import net.minecraft.block.*;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import java.util.Optional;
import static baritone.pathing.movement.MovementHelper.isFlowing;
import static baritone.pathing.movement.MovementHelper.isWater;
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) {
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));
});
long endTime = System.nanoTime();
System.out.println(endTime - startTime);
Thread.dumpStack();
}
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() {
long startTime = System.nanoTime();
for (int i = 0; i < 10; i++) {
canWalkThrough.refresh();
canWalkOn.refresh();
}
long endTime = System.nanoTime();
System.out.println(endTime - startTime);
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.precompute;
import baritone.utils.BlockStateInterface;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.world.IBlockAccess;
import java.lang.reflect.Array;
import java.util.Optional;
import java.util.function.Function;
public class PrecomputedDataForBlockState {
boolean[] dataPerBlockState = new boolean[Block.BLOCK_STATE_IDS.size()]; // Has to be of type boolean due to otherwise it has a generic type
boolean[] specialCases = new boolean[Block.BLOCK_STATE_IDS.size()]; // We can also be certain that size will return the highest as it fills in all positions with null until we get to the highest block state
private final SpecialCaseFunction specialCaseHandler;
private final Function<IBlockState, Optional<Boolean>> precomputer;
public PrecomputedDataForBlockState(Function<IBlockState, Optional<Boolean>> precomputer, SpecialCaseFunction specialCaseHandler) {
this.specialCaseHandler = specialCaseHandler;
this.precomputer = precomputer;
this.refresh();
}
public void refresh() {
for (IBlockState state : Block.BLOCK_STATE_IDS) { // state should never be null
Optional<Boolean> applied = precomputer.apply(state);
int id = Block.BLOCK_STATE_IDS.get(state);
if (applied.isPresent()) {
dataPerBlockState[id] = applied.get();
specialCases[id] = false;
} else {
dataPerBlockState[id] = false;
specialCases[id] = true;
}
}
}
public boolean get(BlockStateInterface bsi, int x, int y, int z, IBlockState state) {
int id = Block.BLOCK_STATE_IDS.get(state);
if (specialCases[id]) {
return specialCaseHandler.apply(bsi, x, y, z, state);
} else {
return dataPerBlockState[id];
}
}
interface SpecialCaseFunction {
public boolean apply(BlockStateInterface bsi, int x, int y, int z, IBlockState blockState);
}
}

View File

@ -38,6 +38,7 @@ import baritone.api.utils.input.Input;
import baritone.pathing.movement.CalculationContext;
import baritone.pathing.movement.Movement;
import baritone.pathing.movement.MovementHelper;
import baritone.pathing.precompute.PrecomputedData;
import baritone.utils.BaritoneProcessHelper;
import baritone.utils.BlockStateInterface;
import baritone.utils.PathingCommandContext;
@ -893,7 +894,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil
private final int originZ;
public BuilderCalculationContext() {
super(BuilderProcess.this.baritone, true); // wew lad
super(BuilderProcess.this.baritone, true, new PrecomputedData()); // wew lad
this.placeable = approxPlaceable(9);
this.schematic = BuilderProcess.this.schematic;
this.originX = origin.getX();

View File

@ -158,7 +158,7 @@ public final class GetToBlockProcess extends BaritoneProcessHelper implements IG
public class GetToBlockCalculationContext extends CalculationContext {
public GetToBlockCalculationContext(boolean forUseOnAnotherThread) {
super(GetToBlockProcess.super.baritone, forUseOnAnotherThread);
super(GetToBlockProcess.super.baritone, forUseOnAnotherThread, null);
}
@Override

View File

@ -111,7 +111,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro
int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value;
List<BlockPos> curr = new ArrayList<>(knownOreLocations);
if (mineGoalUpdateInterval != 0 && tickCount++ % mineGoalUpdateInterval == 0) { // big brain
CalculationContext context = new CalculationContext(baritone, true);
CalculationContext context = new CalculationContext(baritone, true, null); // Precomputed data should never be used on this calculation context
Baritone.getExecutor().execute(() -> rescan(curr, context));
}
if (Baritone.settings().legitMine.value) {