From af1eb58bb87cbb7ce38e01a6fcf96def3222850c Mon Sep 17 00:00:00 2001 From: scorbett123 <50634068+scorbett123@users.noreply.github.com> Date: Tue, 31 May 2022 14:43:58 +0100 Subject: [PATCH] Implement Leijurv's precomputed data idea --- src/api/java/baritone/api/Settings.java | 6 + .../api/event/events/SettingChangedEvent.java | 33 +++ .../listener/AbstractGameEventListener.java | 3 + .../event/listener/IGameEventListener.java | 7 + .../java/baritone/api/utils/SettingsUtil.java | 4 +- .../baritone/behavior/PathingBehavior.java | 17 +- .../baritone/command/defaults/SetCommand.java | 3 +- .../java/baritone/event/GameEventHandler.java | 5 + .../pathing/movement/CalculationContext.java | 8 +- .../pathing/movement/MovementHelper.java | 3 + .../movement/movements/MovementAscend.java | 4 +- .../movement/movements/MovementDescend.java | 12 +- .../movement/movements/MovementDiagonal.java | 30 +-- .../movement/movements/MovementDownward.java | 2 +- .../movement/movements/MovementParkour.java | 6 +- .../movement/movements/MovementPillar.java | 2 +- .../movement/movements/MovementTraverse.java | 2 +- .../baritone/pathing/path/PathExecutor.java | 2 +- .../pathing/precompute/PrecomputedData.java | 237 ++++++++++++++++++ .../PrecomputedDataForBlockState.java | 71 ++++++ .../java/baritone/process/BuilderProcess.java | 3 +- .../baritone/process/GetToBlockProcess.java | 2 +- .../java/baritone/process/MineProcess.java | 2 +- 23 files changed, 422 insertions(+), 42 deletions(-) create mode 100644 src/api/java/baritone/api/event/events/SettingChangedEvent.java create mode 100644 src/main/java/baritone/pathing/precompute/PrecomputedData.java create mode 100644 src/main/java/baritone/pathing/precompute/PrecomputedDataForBlockState.java diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 897182898..1ce87c681 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -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; } diff --git a/src/api/java/baritone/api/event/events/SettingChangedEvent.java b/src/api/java/baritone/api/event/events/SettingChangedEvent.java new file mode 100644 index 000000000..d92384e03 --- /dev/null +++ b/src/api/java/baritone/api/event/events/SettingChangedEvent.java @@ -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 . + */ + +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; + } +} diff --git a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java index 9eac8de46..9f1d2418a 100644 --- a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java @@ -71,4 +71,7 @@ public interface AbstractGameEventListener extends IGameEventListener { @Override default void onPathEvent(PathEvent event) {} + + @Override + default void onSettingChanged(SettingChangedEvent event) {} } diff --git a/src/api/java/baritone/api/event/listener/IGameEventListener.java b/src/api/java/baritone/api/event/listener/IGameEventListener.java index b074e978b..28afdfffe 100644 --- a/src/api/java/baritone/api/event/listener/IGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/IGameEventListener.java @@ -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); } diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index 0e95965f9..b098c1e0f 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -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 { diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index f9c56c5a4..50e2a0c2e 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -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 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 * diff --git a/src/main/java/baritone/command/defaults/SetCommand.java b/src/main/java/baritone/command/defaults/SetCommand.java index e9e35d413..1b395f5a9 100644 --- a/src/main/java/baritone/command/defaults/SetCommand.java +++ b/src/main/java/baritone/command/defaults/SetCommand.java @@ -147,7 +147,8 @@ public class SetCommand extends Command { throw new CommandInvalidTypeException(args.consumed(), "a toggleable setting", "some other setting"); } //noinspection unchecked - ((Settings.Setting) setting).value ^= true; + Settings.Setting asBoolSetting = (Settings.Setting) setting; + asBoolSetting.set(!asBoolSetting.value); logDirect(String.format( "Toggled setting %s to %s", setting.getName(), diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index 8916f7f37..1bdba6b9a 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -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); diff --git a/src/main/java/baritone/pathing/movement/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index d5d6c93c8..b4782fb59 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -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(); diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index dd0ddff48..197f10576 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -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 diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index 161ffdacd..bbc09f521 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -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 diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index 128d1bf99..d0f7d9b3f 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -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)) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java index 933c86092..45d319eb6 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDiagonal.java @@ -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; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java index da5e3f893..d10a7d269 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDownward.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDownward.java @@ -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); diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index e5d17b9ac..07140cf12 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -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; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index b9d599334..bcd420ecc 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -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 diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 1ecf0f92c..2afce2f44 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -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())) { diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index 7e4f76a3b..b28abb618 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -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); diff --git a/src/main/java/baritone/pathing/precompute/PrecomputedData.java b/src/main/java/baritone/pathing/precompute/PrecomputedData.java new file mode 100644 index 000000000..7531c21ac --- /dev/null +++ b/src/main/java/baritone/pathing/precompute/PrecomputedData.java @@ -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 . + */ + +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); + } +} diff --git a/src/main/java/baritone/pathing/precompute/PrecomputedDataForBlockState.java b/src/main/java/baritone/pathing/precompute/PrecomputedDataForBlockState.java new file mode 100644 index 000000000..c7a2f67df --- /dev/null +++ b/src/main/java/baritone/pathing/precompute/PrecomputedDataForBlockState.java @@ -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 . + */ + +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> precomputer; + + public PrecomputedDataForBlockState(Function> 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 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); + } +} diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 1766af623..39c222488 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -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(); diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index 16fc3dda5..2d1e0e985 100644 --- a/src/main/java/baritone/process/GetToBlockProcess.java +++ b/src/main/java/baritone/process/GetToBlockProcess.java @@ -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 diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index 1ec47cd92..988089347 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -111,7 +111,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro int mineGoalUpdateInterval = Baritone.settings().mineGoalUpdateInterval.value; List 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) {