diff --git a/README.md b/README.md index d540cdd99..c18f381fa 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,8 @@ [![GitHub contributors](https://img.shields.io/github/contributors/cabaletta/baritone.svg)](https://github.com/cabaletta/baritone/graphs/contributors/) [![GitHub commits](https://img.shields.io/github/commits-since/cabaletta/baritone/v1.0.0.svg)](https://github.com/cabaletta/baritone/commit/) [![Impact integration](https://img.shields.io/badge/Impact%20integration-v1.2.3-brightgreen.svg)](https://impactdevelopment.github.io/) -[![Asuna integration](https://img.shields.io/badge/Asuna%20integration-builder%20branch-brightgreen.svg)](https://github.com/EmotionalLove/Asuna/) +[![WWE integration](https://img.shields.io/badge/WWE%20%22integration%22-master%3F-green.svg)](https://wweclient.com/) [![KAMI integration](https://img.shields.io/badge/KAMI%20integration-v1.0.0-red.svg)](https://github.com/zeroeightysix/KAMI/) -[![WWE integration](https://img.shields.io/badge/WWE%20%22integration%22-v1.0.0%3F%3F%20smh%20license%20violations-red.svg)](https://wweclient.com/) [![Future integration](https://img.shields.io/badge/Future%20integration-Soon™%3F%3F%3F-red.svg)](https://futureclient.net/) [![ForgeHax integration](https://img.shields.io/badge/ForgeHax%20integration-Soon™-red.svg)](https://github.com/fr1kin/ForgeHax) diff --git a/src/api/java/baritone/api/IBaritone.java b/src/api/java/baritone/api/IBaritone.java index e88f600e1..a12b206d2 100644 --- a/src/api/java/baritone/api/IBaritone.java +++ b/src/api/java/baritone/api/IBaritone.java @@ -65,6 +65,12 @@ public interface IBaritone { */ IMineProcess getMineProcess(); + /** + * @return The {@link IBuilderProcess} instance + * @see IBuilderProcess + */ + IBuilderProcess getBuilderProcess(); + /** * @return The {@link ICustomGoalProcess} instance * @see ICustomGoalProcess diff --git a/src/api/java/baritone/api/pathing/goals/GoalBlock.java b/src/api/java/baritone/api/pathing/goals/GoalBlock.java index e0a60b59a..89dd63048 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalBlock.java @@ -30,17 +30,17 @@ public class GoalBlock implements Goal, IGoalRenderPos { /** * The X block position of this goal */ - private final int x; + public final int x; /** * The Y block position of this goal */ - private final int y; + public final int y; /** * The Z block position of this goal */ - private final int z; + public final int z; public GoalBlock(BlockPos pos) { this(pos.getX(), pos.getY(), pos.getZ()); diff --git a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java index c4856cdd6..fb2a07aaf 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java +++ b/src/api/java/baritone/api/pathing/goals/GoalGetToBlock.java @@ -28,9 +28,9 @@ import net.minecraft.util.math.BlockPos; */ public class GoalGetToBlock implements Goal, IGoalRenderPos { - private final int x; - private final int y; - private final int z; + public final int x; + public final int y; + public final int z; public GoalGetToBlock(BlockPos pos) { this.x = pos.getX(); diff --git a/src/api/java/baritone/api/pathing/movement/IMovement.java b/src/api/java/baritone/api/pathing/movement/IMovement.java index c9b9ea4ba..dae8668d5 100644 --- a/src/api/java/baritone/api/pathing/movement/IMovement.java +++ b/src/api/java/baritone/api/pathing/movement/IMovement.java @@ -45,10 +45,6 @@ public interface IMovement { */ boolean safeToCancel(); - double recalculateCost(); - - double calculateCostWithoutCaching(); - boolean calculatedWhileLoaded(); BetterBlockPos getSrc(); diff --git a/src/api/java/baritone/api/process/IBaritoneProcess.java b/src/api/java/baritone/api/process/IBaritoneProcess.java index 0339179ca..726260d82 100644 --- a/src/api/java/baritone/api/process/IBaritoneProcess.java +++ b/src/api/java/baritone/api/process/IBaritoneProcess.java @@ -101,5 +101,14 @@ public interface IBaritoneProcess { * * @return A display name that's suitable for a HUD */ - String displayName(); + default String displayName() { + if (!isActive()) { + // i love it when impcat's scuffed HUD calls displayName for inactive processes for 1 tick too long + // causing NPEs when the displayname relies on fields that become null when inactive + return "INACTIVE"; + } + return displayName0(); + } + + String displayName0(); } diff --git a/src/api/java/baritone/api/process/IBuilderProcess.java b/src/api/java/baritone/api/process/IBuilderProcess.java new file mode 100644 index 000000000..694c8752b --- /dev/null +++ b/src/api/java/baritone/api/process/IBuilderProcess.java @@ -0,0 +1,49 @@ +/* + * 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.process; + +import baritone.api.utils.ISchematic; +import net.minecraft.util.math.Vec3i; + +import java.io.File; + +/** + * @author Brady + * @since 1/15/2019 + */ +public interface IBuilderProcess extends IBaritoneProcess { + + /** + * Requests a build for the specified schematic, labeled as specified, with the specified origin. + * + * @param name A user-friendly name for the schematic + * @param schematic The object representation of the schematic + * @param origin The origin position of the schematic being built + */ + void build(String name, ISchematic schematic, Vec3i origin); + + /** + * Requests a build for the specified schematic, labeled as specified, with the specified origin. + * + * @param name A user-friendly name for the schematic + * @param schematic The file path of the schematic + * @param origin The origin position of the schematic being built + * @return Whether or not the schematic was able to load from file + */ + boolean build(String name, File schematic, Vec3i origin); +} diff --git a/src/api/java/baritone/api/utils/ISchematic.java b/src/api/java/baritone/api/utils/ISchematic.java new file mode 100644 index 000000000..1f2cd8742 --- /dev/null +++ b/src/api/java/baritone/api/utils/ISchematic.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.api.utils; + +import net.minecraft.block.state.IBlockState; + +/** + * Basic representation of a schematic. Provides the dimensions and + * the desired statefor a given position relative to the origin. + * + * @author leijurv + */ +public interface ISchematic { + + /** + * Does the block at this coordinate matter to the schematic? + *

+ * Normally just a check for if the coordinate is in the cube. + *

+ * However, in the case of something like a map art, anything that's below the level of the map art doesn't matter, + * so this function should return false in that case. (i.e. it doesn't really have to be air below the art blocks) + * + * @param x The x position of the block, relative to the origin + * @param y The y position of the block, relative to the origin + * @param z The z position of the block, relative to the origin + * @return Whether or not the specified position is within the bounds of this schematic + */ + default boolean inSchematic(int x, int y, int z) { + return x >= 0 && x < widthX() && y >= 0 && y < heightY() && z >= 0 && z < lengthZ(); + } + + /** + * Returns the desired block state at a given (X, Y, Z) position relative to the origin (0, 0, 0). + * + * @param x The x position of the block, relative to the origin + * @param y The y position of the block, relative to the origin + * @param z The z position of the block, relative to the origin + * @return The desired block state at the specified position + */ + IBlockState desiredState(int x, int y, int z); + + /** + * @return The width (X axis length) of this schematic + */ + int widthX(); + + /** + * @return The height (Y axis length) of this schematic + */ + int heightY(); + + /** + * @return The length (Z axis length) of this schematic + */ + int lengthZ(); +} \ No newline at end of file diff --git a/src/api/java/baritone/api/utils/input/Input.java b/src/api/java/baritone/api/utils/input/Input.java index c7143db96..c44f33522 100644 --- a/src/api/java/baritone/api/utils/input/Input.java +++ b/src/api/java/baritone/api/utils/input/Input.java @@ -17,15 +17,6 @@ package baritone.api.utils.input; -import net.minecraft.client.GameSettings; -import net.minecraft.client.Minecraft; -import net.minecraft.client.settings.KeyBinding; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Function; - /** * An {@link Enum} representing the inputs that control the player's * behavior. This includes moving, interacting with blocks, jumping, @@ -36,84 +27,45 @@ public enum Input { /** * The move forward input */ - MOVE_FORWARD(s -> s.keyBindForward), + MOVE_FORWARD, /** * The move back input */ - MOVE_BACK(s -> s.keyBindBack), + MOVE_BACK, /** * The move left input */ - MOVE_LEFT(s -> s.keyBindLeft), + MOVE_LEFT, /** * The move right input */ - MOVE_RIGHT(s -> s.keyBindRight), + MOVE_RIGHT, /** * The attack input */ - CLICK_LEFT(s -> s.keyBindAttack), + CLICK_LEFT, /** * The use item input */ - CLICK_RIGHT(s -> s.keyBindUseItem), + CLICK_RIGHT, /** * The jump input */ - JUMP(s -> s.keyBindJump), + JUMP, /** * The sneak input */ - SNEAK(s -> s.keyBindSneak), + SNEAK, /** * The sprint input */ - SPRINT(s -> s.keyBindSprint); - - /** - * Map of {@link KeyBinding} to {@link Input}. Values should be queried through {@link #getInputForBind(KeyBinding)} - */ - private static final Map bindToInputMap = new HashMap<>(); - - /** - * The actual game {@link KeyBinding} being forced. - */ - private final KeyBinding keyBinding; - - Input(Function keyBindingMapper) { - /* - - Here, a Function is used because referring to a static field in this enum for the game instance, - as it was before, wouldn't be possible in an Enum constructor unless the static field was in an - interface that this class implemented. (Helper acted as this interface) I didn't feel like making - an interface with a game instance field just to not have to do this. - - */ - this.keyBinding = keyBindingMapper.apply(Minecraft.getInstance().gameSettings); - } - - /** - * @return The actual game {@link KeyBinding} being forced. - */ - public final KeyBinding getKeyBinding() { - return this.keyBinding; - } - - /** - * Finds the {@link Input} constant that is associated with the specified {@link KeyBinding}. - * - * @param binding The {@link KeyBinding} to find the associated {@link Input} for - * @return The {@link Input} associated with the specified {@link KeyBinding} - */ - public static Input getInputForBind(KeyBinding binding) { - return bindToInputMap.computeIfAbsent(binding, b -> Arrays.stream(values()).filter(input -> input.keyBinding == b).findFirst().orElse(null)); - } + SPRINT } diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index 0a51e13ed..f08b4fb16 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -71,12 +71,14 @@ public class Baritone implements IBaritone { private PathingBehavior pathingBehavior; private LookBehavior lookBehavior; private MemoryBehavior memoryBehavior; + private InventoryBehavior inventoryBehavior; private InputOverrideHandler inputOverrideHandler; private FollowProcess followProcess; private MineProcess mineProcess; private GetToBlockProcess getToBlockProcess; private CustomGoalProcess customGoalProcess; + private BuilderProcess builderProcess; private ExploreProcess exploreProcess; private PathingControlManager pathingControlManager; @@ -105,7 +107,7 @@ public class Baritone implements IBaritone { pathingBehavior = new PathingBehavior(this); lookBehavior = new LookBehavior(this); memoryBehavior = new MemoryBehavior(this); - new InventoryBehavior(this); + inventoryBehavior = new InventoryBehavior(this); inputOverrideHandler = new InputOverrideHandler(this); new ExampleBaritoneControl(this); } @@ -116,6 +118,7 @@ public class Baritone implements IBaritone { mineProcess = new MineProcess(this); customGoalProcess = new CustomGoalProcess(this); // very high iq getToBlockProcess = new GetToBlockProcess(this); + builderProcess = new BuilderProcess(this); exploreProcess = new ExploreProcess(this); } @@ -171,6 +174,15 @@ public class Baritone implements IBaritone { return this.followProcess; } + @Override + public BuilderProcess getBuilderProcess() { + return this.builderProcess; + } + + public InventoryBehavior getInventoryBehavior() { + return this.inventoryBehavior; + } + @Override public LookBehavior getLookBehavior() { return this.lookBehavior; @@ -211,4 +223,4 @@ public class Baritone implements IBaritone { public static Executor getExecutor() { return threadPool; } -} +} \ No newline at end of file diff --git a/src/main/java/baritone/behavior/InventoryBehavior.java b/src/main/java/baritone/behavior/InventoryBehavior.java index cb5eb612e..120bc7489 100644 --- a/src/main/java/baritone/behavior/InventoryBehavior.java +++ b/src/main/java/baritone/behavior/InventoryBehavior.java @@ -21,13 +21,18 @@ import baritone.Baritone; import baritone.api.event.events.TickEvent; import baritone.utils.ToolSet; import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.init.Blocks; import net.minecraft.inventory.ClickType; -import net.minecraft.item.ItemPickaxe; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemTool; +import net.minecraft.item.*; import net.minecraft.util.NonNullList; +import java.util.ArrayList; +import java.util.OptionalInt; +import java.util.Random; +import java.util.function.Predicate; + public class InventoryBehavior extends Behavior { public InventoryBehavior(Baritone baritone) { super(baritone); @@ -54,6 +59,34 @@ public class InventoryBehavior extends Behavior { } } + public void attemptToPutOnHotbar(int inMainInvy, Predicate disallowedHotbar) { + OptionalInt destination = getTempHotbarSlot(disallowedHotbar); + if (destination.isPresent()) { + swapWithHotBar(inMainInvy, destination.getAsInt()); + } + } + + public OptionalInt getTempHotbarSlot(Predicate disallowedHotbar) { + // we're using 0 and 8 for pickaxe and throwaway + ArrayList candidates = new ArrayList<>(); + for (int i = 1; i < 8; i++) { + if (ctx.player().inventory.mainInventory.get(i).isEmpty() && !disallowedHotbar.test(i)) { + candidates.add(i); + } + } + if (candidates.isEmpty()) { + for (int i = 1; i < 8; i++) { + if (!disallowedHotbar.test(i)) { + candidates.add(i); + } + } + } + if (candidates.isEmpty()) { + return OptionalInt.empty(); + } + return OptionalInt.of(candidates.get(new Random().nextInt(candidates.size()))); + } + private void swapWithHotBar(int inInventory, int inHotbar) { ctx.playerController().windowClick(ctx.player().inventoryContainer.windowId, inInventory < 9 ? inInventory + 36 : inInventory, inHotbar, ClickType.SWAP, ctx.player()); } @@ -87,4 +120,62 @@ public class InventoryBehavior extends Behavior { } return bestInd; } + + public boolean hasGenericThrowaway() { + for (Item item : Baritone.settings().acceptableThrowawayItems.value) { + if (throwaway(false, item::equals)) { + return true; + } + } + return false; + } + + public boolean selectThrowawayForLocation(int x, int y, int z) { + IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z); + if (maybe != null && throwaway(true, item -> item instanceof ItemBlock && ((ItemBlock) item).getBlock().equals(maybe.getBlock()))) { + return true; // gotem + } + for (Item item : Baritone.settings().acceptableThrowawayItems.value) { + if (throwaway(true, item::equals)) { + return true; + } + } + return false; + } + + private boolean throwaway(boolean select, Predicate desired) { + EntityPlayerSP p = ctx.player(); + NonNullList inv = p.inventory.mainInventory; + for (byte i = 0; i < 9; i++) { + ItemStack item = inv.get(i); + // this usage of settings() is okay because it's only called once during pathing + // (while creating the CalculationContext at the very beginning) + // and then it's called during execution + // since this function is never called during cost calculation, we don't need to migrate + // acceptableThrowawayItems to the CalculationContext + if (desired.test(item.getItem())) { + if (select) { + p.inventory.currentItem = i; + } + return true; + } + } + if (desired.test(p.inventory.offHandInventory.get(0).getItem())) { + // main hand takes precedence over off hand + // that means that if we have block A selected in main hand and block B in off hand, right clicking places block B + // we've already checked above ^ and the main hand can't possible have an acceptablethrowawayitem + // so we need to select in the main hand something that doesn't right click + // so not a shovel, not a hoe, not a block, etc + for (byte i = 0; i < 9; i++) { + ItemStack item = inv.get(i); + if (item.isEmpty() || item.getItem() instanceof ItemPickaxe) { + if (select) { + p.inventory.currentItem = i; + } + return true; + } + } + } + return false; + } } diff --git a/src/main/java/baritone/behavior/LookBehavior.java b/src/main/java/baritone/behavior/LookBehavior.java index adce0643d..3dab1d69f 100644 --- a/src/main/java/baritone/behavior/LookBehavior.java +++ b/src/main/java/baritone/behavior/LookBehavior.java @@ -70,7 +70,7 @@ public final class LookBehavior extends Behavior implements ILookBehavior { float desiredPitch = this.target.getPitch(); ctx.player().rotationPitch = desiredPitch; if (desiredPitch == oldPitch) { - nudgeToLevel(); + //nudgeToLevel(); } this.target = null; } diff --git a/src/main/java/baritone/behavior/MemoryBehavior.java b/src/main/java/baritone/behavior/MemoryBehavior.java index 8d3a0a8c3..50beda0f8 100644 --- a/src/main/java/baritone/behavior/MemoryBehavior.java +++ b/src/main/java/baritone/behavior/MemoryBehavior.java @@ -25,7 +25,6 @@ import baritone.api.event.events.TickEvent; import baritone.api.event.events.type.EventState; import baritone.cache.ContainerMemory; import baritone.cache.Waypoint; -import baritone.pathing.movement.CalculationContext; import baritone.utils.BlockStateInterface; import net.minecraft.block.Block; import net.minecraft.block.BlockBed; @@ -196,7 +195,7 @@ public final class MemoryBehavior extends Behavior { } private BlockPos neighboringConnectedBlock(BlockPos in) { - BlockStateInterface bsi = new CalculationContext(baritone).bsi; + BlockStateInterface bsi = baritone.bsi; Block block = bsi.get0(in).getBlock(); if (block != Blocks.TRAPPED_CHEST && block != Blocks.CHEST) { return null; // other things that have contents, but can be placed adjacent without combining diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 915e50663..cca7bdfc7 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -23,6 +23,7 @@ import baritone.api.event.events.*; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalXZ; +import baritone.api.process.PathingCommand; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.PathCalculationResult; import baritone.api.utils.interfaces.IGoalRenderPos; @@ -33,6 +34,7 @@ import baritone.pathing.movement.MovementHelper; import baritone.pathing.path.PathExecutor; import baritone.utils.Helper; import baritone.utils.PathRenderer; +import baritone.utils.PathingCommandContext; import baritone.utils.pathing.Favoring; import net.minecraft.util.math.BlockPos; @@ -48,9 +50,11 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private PathExecutor next; private Goal goal; + private CalculationContext context; private boolean safeToCancel; private boolean pauseRequestedLastTick; + private boolean unpausedLastTick; private boolean cancelRequested; private boolean calcFailedLastTick; @@ -106,10 +110,14 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private void tickPath() { if (pauseRequestedLastTick && safeToCancel) { pauseRequestedLastTick = false; - baritone.getInputOverrideHandler().clearAllKeys(); - baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); + if (unpausedLastTick) { + baritone.getInputOverrideHandler().clearAllKeys(); + baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); + } + unpausedLastTick = false; return; } + unpausedLastTick = true; if (cancelRequested) { cancelRequested = false; baritone.getInputOverrideHandler().clearAllKeys(); @@ -173,7 +181,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } // we aren't calculating queuePathEvent(PathEvent.CALC_STARTED); - findPathInNewThread(expectedSegmentStart, true); + findPathInNewThread(expectedSegmentStart, true, context); } return; } @@ -210,7 +218,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, // if we actually included current, it wouldn't start planning ahead until the last movement was done, if the last movement took more than 7.5 seconds on its own logDebug("Path almost over. Planning ahead..."); queuePathEvent(PathEvent.NEXT_SEGMENT_CALC_STARTED); - findPathInNewThread(current.getPath().getDest(), false); + findPathInNewThread(current.getPath().getDest(), false, context); } } } @@ -237,9 +245,32 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, this.goal = goal; } - public boolean secretInternalSetGoalAndPath(Goal goal) { - secretInternalSetGoal(goal); - return secretInternalPath(); + public boolean secretInternalSetGoalAndPath(PathingCommand command) { + secretInternalSetGoal(command.goal); + if (command instanceof PathingCommandContext) { + context = ((PathingCommandContext) command).desiredCalcContext; + } else { + context = new CalculationContext(baritone, true); + } + if (goal == null) { + return false; + } + if (goal.isInGoal(ctx.playerFeet()) || goal.isInGoal(expectedSegmentStart)) { + return false; + } + synchronized (pathPlanLock) { + if (current != null) { + return false; + } + synchronized (pathCalcLock) { + if (inProgress != null) { + return false; + } + queuePathEvent(PathEvent.CALC_STARTED); + findPathInNewThread(expectedSegmentStart, true, context); + return true; + } + } } @Override @@ -327,39 +358,16 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } } - /** - * Start calculating a path if we aren't already - * - * @return true if this call started path calculation, false if it was already calculating or executing a path - */ - public boolean secretInternalPath() { - if (goal == null) { - return false; - } - if (goal.isInGoal(ctx.playerFeet())) { - return false; - } - synchronized (pathPlanLock) { - if (current != null) { - return false; - } - synchronized (pathCalcLock) { - if (inProgress != null) { - return false; - } - queuePathEvent(PathEvent.CALC_STARTED); - findPathInNewThread(expectedSegmentStart, true); - return true; - } - } - } - public void secretCursedFunctionDoNotCall(IPath path) { synchronized (pathPlanLock) { current = new PathExecutor(this, path); } } + public CalculationContext secretInternalGetCalculationContext() { + return context; + } + /** * See issue #209 * @@ -411,7 +419,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, * @param start * @param talkAboutIt */ - private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt) { + private void findPathInNewThread(final BlockPos start, final boolean talkAboutIt, CalculationContext context) { // this must be called with synchronization on pathCalcLock! // actually, we can check this, muahaha if (!Thread.holdsLock(pathCalcLock)) { @@ -421,6 +429,9 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, if (inProgress != null) { throw new IllegalStateException("Already doing it"); // should have been checked by caller } + if (!context.safeForThreadedUse) { + throw new IllegalStateException("Improper context thread safety level"); + } Goal goal = this.goal; if (goal == null) { logDebug("no goal"); // TODO should this be an exception too? definitely should be checked by caller @@ -435,7 +446,6 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, primaryTimeout = Baritone.settings().planAheadPrimaryTimeoutMS.value; failureTimeout = Baritone.settings().planAheadFailureTimeoutMS.value; } - CalculationContext context = new CalculationContext(baritone, true); // not safe to create on the other thread, it looks up a lot of stuff in minecraft AbstractNodeCostSearch pathfinder = createPathfinder(start, goal, current == null ? null : current.getPath(), context); if (!Objects.equals(pathfinder.getGoal(), goal)) { // will return the exact same object if simplification didn't happen logDebug("Simplifying " + goal.getClass() + " to GoalXZ due to distance"); @@ -503,7 +513,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, transformed = new GoalXZ(pos.getX(), pos.getZ()); } } - Favoring favoring = new Favoring(context.getBaritone().getPlayerContext(), previous); + Favoring favoring = new Favoring(context.getBaritone().getPlayerContext(), previous, context); return new AStarPathFinder(start.getX(), start.getY(), start.getZ(), transformed, favoring, context); } diff --git a/src/main/java/baritone/pathing/movement/CalculationContext.java b/src/main/java/baritone/pathing/movement/CalculationContext.java index 2cf7eb6a4..ef305b708 100644 --- a/src/main/java/baritone/pathing/movement/CalculationContext.java +++ b/src/main/java/baritone/pathing/movement/CalculationContext.java @@ -34,6 +34,8 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; +import static baritone.api.pathing.movement.ActionCosts.COST_INF; + /** * @author Brady * @since 8/7/2018 @@ -42,6 +44,7 @@ public class CalculationContext { private static final ItemStack STACK_BUCKET_WATER = new ItemStack(Items.WATER_BUCKET); + public final boolean safeForThreadedUse; public final IBaritone baritone; public final World world; public final WorldData worldData; @@ -50,7 +53,7 @@ public class CalculationContext { public final boolean hasWaterBucket; public final boolean hasThrowaway; public final boolean canSprint; - public final double placeBlockCost; + protected final double placeBlockCost; // protected because you should call the function instead public final boolean allowBreak; public final boolean allowParkour; public final boolean allowParkourPlace; @@ -61,7 +64,8 @@ public class CalculationContext { public final int maxFallHeightBucket; public final double waterWalkSpeed; public final double breakBlockAdditionalCost; - public final double jumpPenalty; + public double backtrackCostFavoringCoefficient; + public double jumpPenalty; public final double walkOnWaterOnePenalty; public final BetterWorldBorder worldBorder; @@ -70,13 +74,14 @@ public class CalculationContext { } public CalculationContext(IBaritone baritone, boolean forUseOnAnotherThread) { + this.safeForThreadedUse = forUseOnAnotherThread; this.baritone = baritone; EntityPlayerSP player = baritone.getPlayerContext().player(); this.world = baritone.getPlayerContext().world(); this.worldData = (WorldData) baritone.getWorldProvider().getCurrentWorld(); - this.bsi = new BlockStateInterface(world, worldData, forUseOnAnotherThread); // TODO TODO TODO + this.bsi = new BlockStateInterface(world, worldData, forUseOnAnotherThread); this.toolSet = new ToolSet(player); - this.hasThrowaway = Baritone.settings().allowPlace.value && MovementHelper.throwaway(baritone.getPlayerContext(), false); + this.hasThrowaway = Baritone.settings().allowPlace.value && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway(); this.hasWaterBucket = Baritone.settings().allowWaterBucketFall.value && InventoryPlayer.isHotbar(player.inventory.getSlotFor(STACK_BUCKET_WATER)) && !world.getDimension().isNether(); this.canSprint = Baritone.settings().allowSprint.value && player.getFoodStats().getFoodLevel() > 6; this.placeBlockCost = Baritone.settings().blockPlacementPenalty.value; @@ -95,6 +100,7 @@ public class CalculationContext { float mult = depth / 3.0F; this.waterWalkSpeed = ActionCosts.WALK_ONE_IN_WATER_COST * (1 - mult) + ActionCosts.WALK_ONE_BLOCK_COST * mult; this.breakBlockAdditionalCost = Baritone.settings().blockBreakAdditionalPenalty.value; + this.backtrackCostFavoringCoefficient = Baritone.settings().backtrackCostFavoringCoefficient.value; this.jumpPenalty = Baritone.settings().jumpPenalty.value; this.walkOnWaterOnePenalty = Baritone.settings().walkOnWaterOnePenalty.value; // why cache these things here, why not let the movements just get directly from settings? @@ -123,21 +129,32 @@ public class CalculationContext { return get(x, y, z).getBlock(); } - public boolean canPlaceThrowawayAt(int x, int y, int z) { + public double costOfPlacingAt(int x, int y, int z) { if (!hasThrowaway) { // only true if allowPlace is true, see constructor - return false; + return COST_INF; } if (isPossiblyProtected(x, y, z)) { - return false; + return COST_INF; } - return worldBorder.canPlaceAt(x, z); // TODO perhaps MovementHelper.canPlaceAgainst could also use this? + if (!worldBorder.canPlaceAt(x, z)) { + // TODO perhaps MovementHelper.canPlaceAgainst could also use this? + return COST_INF; + } + return placeBlockCost; } - public boolean canBreakAt(int x, int y, int z) { + public double breakCostMultiplierAt(int x, int y, int z) { if (!allowBreak) { - return false; + return COST_INF; } - return !isPossiblyProtected(x, y, z); + if (isPossiblyProtected(x, y, z)) { + return COST_INF; + } + return 1; + } + + public double placeBucketCost() { + return placeBlockCost; // shrug } public boolean isPossiblyProtected(int x, int y, int z) { diff --git a/src/main/java/baritone/pathing/movement/Movement.java b/src/main/java/baritone/pathing/movement/Movement.java index 4fa1dc16f..5da08a3f6 100644 --- a/src/main/java/baritone/pathing/movement/Movement.java +++ b/src/main/java/baritone/pathing/movement/Movement.java @@ -33,7 +33,7 @@ import java.util.Optional; public abstract class Movement implements IMovement, MovementHelper { - protected static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; + public static final EnumFacing[] HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP = {EnumFacing.NORTH, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.WEST, EnumFacing.DOWN}; protected final IBaritone baritone; protected final IPlayerContext ctx; @@ -75,31 +75,28 @@ public abstract class Movement implements IMovement, MovementHelper { this(baritone, src, dest, toBreak, null); } - @Override - public double getCost() { + public double getCost() throws NullPointerException { + return cost; + } + + public double getCost(CalculationContext context) { if (cost == null) { - cost = calculateCost(new CalculationContext(baritone)); + cost = calculateCost(context); } return cost; } public abstract double calculateCost(CalculationContext context); - @Override - public double recalculateCost() { + public double recalculateCost(CalculationContext context) { cost = null; - return getCost(); + return getCost(context); } public void override(double cost) { this.cost = cost; } - @Override - public double calculateCostWithoutCaching() { - return calculateCost(new CalculationContext(baritone)); - } - /** * Handles the execution of the latest Movement * State, and offers a Status to the calling class. diff --git a/src/main/java/baritone/pathing/movement/MovementHelper.java b/src/main/java/baritone/pathing/movement/MovementHelper.java index f276b4588..3e2052bb5 100644 --- a/src/main/java/baritone/pathing/movement/MovementHelper.java +++ b/src/main/java/baritone/pathing/movement/MovementHelper.java @@ -29,20 +29,16 @@ import baritone.utils.Helper; import baritone.utils.ToolSet; import net.minecraft.block.*; import net.minecraft.block.state.IBlockState; -import net.minecraft.client.entity.EntityPlayerSP; import net.minecraft.fluid.FlowingFluid; import net.minecraft.fluid.Fluid; import net.minecraft.fluid.IFluidState; import net.minecraft.fluid.WaterFluid; import net.minecraft.init.Blocks; import net.minecraft.init.Fluids; -import net.minecraft.item.ItemPickaxe; -import net.minecraft.item.ItemStack; import net.minecraft.pathfinding.PathType; import net.minecraft.state.BooleanProperty; import net.minecraft.state.properties.SlabType; import net.minecraft.util.EnumFacing; -import net.minecraft.util.NonNullList; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; @@ -92,6 +88,9 @@ public interface MovementHelper extends ActionCosts, Helper { // be opened by just interacting. return block != Blocks.IRON_DOOR; } + if (block instanceof BlockCarpet) { + return canWalkOn(bsi, x, y - 1, z); + } boolean snow = block instanceof BlockSnowLayer; boolean trapdoor = block instanceof BlockTrapDoor; if (snow || trapdoor) { @@ -335,20 +334,22 @@ public interface MovementHelper extends ActionCosts, Helper { } static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z) { - return canPlaceAgainst(bsi.get0(x, y, z)); + return canPlaceAgainst(bsi, x, y, z, bsi.get0(x, y, z)); } static boolean canPlaceAgainst(BlockStateInterface bsi, BlockPos pos) { - return canPlaceAgainst(bsi.get0(pos.getX(), pos.getY(), pos.getZ())); + return canPlaceAgainst(bsi, pos.getX(), pos.getY(), pos.getZ()); } static boolean canPlaceAgainst(IPlayerContext ctx, BlockPos pos) { return canPlaceAgainst(new BlockStateInterface(ctx), 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 - return state.isBlockNormalCube(); + static boolean canPlaceAgainst(BlockStateInterface bsi, int x, int y, int z, IBlockState state) { + // can we look at the center of a side face of this block and likely be able to place? + // (thats how this check is used) + // therefore dont include weird things that we technically could place against (like carpet) but practically can't + return state.isBlockNormalCube() || state.isFullCube() || state.getBlock() == Blocks.GLASS || state.getBlock() instanceof BlockStainedGlass; } static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, boolean includeFalling) { @@ -358,7 +359,8 @@ public interface MovementHelper extends ActionCosts, Helper { static double getMiningDurationTicks(CalculationContext context, int x, int y, int z, IBlockState state, boolean includeFalling) { Block block = state.getBlock(); if (!canWalkThrough(context.bsi, x, y, z, state)) { - if (!context.canBreakAt(x, y, z)) { + double mult = context.breakCostMultiplierAt(x, y, z); + if (mult >= COST_INF) { return COST_INF; } if (avoidBreaking(context.bsi, x, y, z, state)) { @@ -375,6 +377,7 @@ public interface MovementHelper extends ActionCosts, Helper { double result = m / strVsBlock; result += context.breakBlockAdditionalCost; + result *= mult; if (includeFalling) { IBlockState above = context.get(x, y + 1, z); if (above.getBlock() instanceof BlockFalling) { @@ -416,42 +419,6 @@ public interface MovementHelper extends ActionCosts, Helper { ctx.player().inventory.currentItem = ts.getBestSlot(b.getBlock()); } - static boolean throwaway(IPlayerContext ctx, boolean select) { - EntityPlayerSP p = ctx.player(); - NonNullList inv = p.inventory.mainInventory; - for (byte i = 0; i < 9; i++) { - ItemStack item = inv.get(i); - // this usage of settings() is okay because it's only called once during pathing - // (while creating the CalculationContext at the very beginning) - // and then it's called during execution - // since this function is never called during cost calculation, we don't need to migrate - // acceptableThrowawayItems to the CalculationContext - if (Baritone.settings().acceptableThrowawayItems.value.contains(item.getItem())) { - if (select) { - p.inventory.currentItem = i; - } - return true; - } - } - if (Baritone.settings().acceptableThrowawayItems.value.contains(p.inventory.offHandInventory.get(0).getItem())) { - // main hand takes precedence over off hand - // that means that if we have block A selected in main hand and block B in off hand, right clicking places block B - // we've already checked above ^ and the main hand can't possible have an acceptablethrowawayitem - // so we need to select in the main hand something that doesn't right click - // so not a shovel, not a hoe, not a block, etc - for (byte i = 0; i < 9; i++) { - ItemStack item = inv.get(i); - if (item.isEmpty() || item.getItem() instanceof ItemPickaxe) { - if (select) { - p.inventory.currentItem = i; - } - return true; - } - } - } - return false; - } - static void moveTowards(IPlayerContext ctx, MovementState state, BlockPos pos) { state.setTarget(new MovementTarget( new Rotation(RotationUtils.calcRotationFromVec3d(ctx.playerHead(), @@ -537,8 +504,7 @@ public interface MovementHelper extends ActionCosts, Helper { for (int i = 0; i < 5; i++) { BlockPos against1 = placeAt.offset(HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i]); if (MovementHelper.canPlaceAgainst(ctx, against1)) { - //if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(placeAt.getX(), placeAt.getY(), placeAt.getZ())) { // get ready to place a throwaway block - if (!throwaway(ctx, true)) { + if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(placeAt.getX(), placeAt.getY(), placeAt.getZ())) { // get ready to place a throwaway block Helper.HELPER.logDebug("bb pls get me some blocks. dirt or cobble"); state.setStatus(MovementStatus.UNREACHABLE); return PlaceResult.NO_OPTION; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java index d3ef9defd..b7b7eb5f4 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementAscend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementAscend.java @@ -53,14 +53,16 @@ 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); - boolean hasToPlace = false; + double additionalPlacementCost = 0; if (!MovementHelper.canWalkOn(context.bsi, destX, y, destZ, toPlace)) { - if (!context.canPlaceThrowawayAt(destX, y, destZ)) { + additionalPlacementCost = context.costOfPlacingAt(destX, y, destZ); + if (additionalPlacementCost >= COST_INF) { return COST_INF; } if (!MovementHelper.isReplacable(destX, y, destZ, toPlace, context.bsi)) { return COST_INF; } + boolean foundPlaceOption = false; for (int i = 0; i < 5; i++) { int againstX = destX + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getXOffset(); int againstY = y + HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP[i].getYOffset(); @@ -69,11 +71,11 @@ public class MovementAscend extends Movement { continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { - hasToPlace = true; + foundPlaceOption = true; break; } } - if (!hasToPlace) { // didn't find a valid place =( + if (!foundPlaceOption) { // didn't find a valid place =( return COST_INF; } } @@ -124,10 +126,7 @@ public class MovementAscend extends Movement { walk += context.jumpPenalty; } - double totalCost = walk; - if (hasToPlace) { - totalCost += context.placeBlockCost; - } + double totalCost = walk + additionalPlacementCost; // start with srcUp2 since we already have its state // includeFalling isn't needed because of the falling check above -- if srcUp3 is falling we will have already exited with COST_INF if we'd actually have to break it totalCost += MovementHelper.getMiningDurationTicks(context, x, y + 2, z, srcUp2, false); @@ -222,4 +221,10 @@ public class MovementAscend extends Movement { } return true; } + + @Override + public boolean safeToCancel(MovementState state) { + // if we had to place, don't allow pause + return state.getStatus() != MovementStatus.RUNNING || ticksWithoutPlacement == 0; + } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java index c114dbf11..528300787 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementDescend.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementDescend.java @@ -187,7 +187,7 @@ public class MovementDescend extends Movement { res.x = destX; res.y = newY + 1;// this is the block we're falling onto, so dest is +1 res.z = destZ; - res.cost = tentativeCost + context.placeBlockCost; + res.cost = tentativeCost + context.placeBucketCost(); return true; } else { return false; diff --git a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java index de143e8d4..3df9e0448 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementParkour.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementParkour.java @@ -121,9 +121,11 @@ public class MovementParkour extends Movement { if (!context.allowParkourPlace) { return; } + // time 2 pop off with that dank skynet parkour place int destX = x + 4 * xDiff; int destZ = z + 4 * zDiff; - if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { + double placeCost = context.costOfPlacingAt(destX, y - 1, destZ); + if (placeCost >= COST_INF) { return; } IBlockState toReplace = context.get(destX, y - 1, destZ); @@ -141,7 +143,7 @@ public class MovementParkour extends Movement { res.x = destX; res.y = y; res.z = destZ; - res.cost = costFromJumpDistance(4) + context.placeBlockCost + context.jumpPenalty; + res.cost = costFromJumpDistance(4) + placeCost + context.jumpPenalty; return; } } diff --git a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java index b620dfe9d..d90e2a2c1 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementPillar.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementPillar.java @@ -17,6 +17,7 @@ package baritone.pathing.movement.movements; +import baritone.Baritone; import baritone.api.IBaritone; import baritone.api.pathing.movement.MovementStatus; import baritone.api.utils.BetterBlockPos; @@ -77,10 +78,18 @@ public class MovementPillar extends Movement { return LADDER_UP_ONE_COST; // allow ascending pillars of water, but only if we're already in one } } - if (!ladder && !context.canPlaceThrowawayAt(x, y, z)) { // we need to place a block where we started to jump on it - return COST_INF; + double placeCost = 0; + if (!ladder) { + // we need to place a block where we started to jump on it + placeCost = context.costOfPlacingAt(x, y, z); + if (placeCost >= COST_INF) { + return COST_INF; + } + if (fromDown.getBlock() == Blocks.AIR) { + placeCost += 0.1; // slightly (1/200th of a second) penalize pillaring on what's currently air + } } - if ((MovementHelper.isLiquid(fromState) && !MovementHelper.canPlaceAgainst(fromDown)) || (MovementHelper.isLiquid(fromDown) && context.assumeWalkOnWater)) { + if ((MovementHelper.isLiquid(fromState) && !MovementHelper.canPlaceAgainst(context.bsi, x, y - 1, z, fromDown)) || (MovementHelper.isLiquid(fromDown) && context.assumeWalkOnWater)) { // otherwise, if we're standing in water, we cannot pillar // if we're standing on water and assumeWalkOnWater is true, we cannot pillar // if we're standing on water and assumeWalkOnWater is false, we must have ascended to here, or sneak backplaced, so it is possible to pillar again @@ -116,7 +125,7 @@ public class MovementPillar extends Movement { if (ladder) { return LADDER_UP_ONE_COST + hardness * 5; } else { - return JUMP_ONE_BLOCK_COST + context.placeBlockCost + context.jumpPenalty + hardness; + return JUMP_ONE_BLOCK_COST + placeCost + context.jumpPenalty + hardness; } } @@ -200,7 +209,7 @@ public class MovementPillar extends Movement { return state; } else { // Get ready to place a throwaway block - if (!MovementHelper.throwaway(ctx, true)) { + if (!((Baritone) baritone).getInventoryBehavior().selectThrowawayForLocation(src.x, src.y, src.z)) { return state.setStatus(MovementStatus.UNREACHABLE); } @@ -236,6 +245,7 @@ public class MovementPillar extends Movement { if (reachable.isPresent()) { state.setTarget(new MovementState.MovementTarget(reachable.get(), true)); } + state.setInput(Input.JUMP, false); // breaking is like 5x slower when you're jumping state.setInput(Input.CLICK_LEFT, true); blockIsThere = false; } else if (ctx.player().isSneaking() && (Objects.equals(src.down(), ctx.objectMouseOver().getBlockPos()) || Objects.equals(src, ctx.objectMouseOver().getBlockPos())) && ctx.player().posY > dest.getY() + 0.1) { diff --git a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java index 87d764634..e3b8604b7 100644 --- a/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java +++ b/src/main/java/baritone/pathing/movement/movements/MovementTraverse.java @@ -116,7 +116,8 @@ public class MovementTraverse extends Movement { // this happens when assume walk on water is true and this is a traverse in water, which isn't allowed return COST_INF; } - if (!context.canPlaceThrowawayAt(destX, y - 1, destZ)) { + double placeCost = context.costOfPlacingAt(destX, y - 1, destZ); + if (placeCost >= COST_INF) { return COST_INF; } double hardness1 = MovementHelper.getMiningDurationTicks(context, destX, y, destZ, pb1, false); @@ -133,7 +134,7 @@ public class MovementTraverse extends Movement { continue; } if (MovementHelper.canPlaceAgainst(context.bsi, againstX, againstY, againstZ)) { // found a side place option - return WC + context.placeBlockCost + hardness1 + hardness2; + return WC + placeCost + hardness1 + hardness2; } } // now that we've checked all possible directions to side place, we actually need to backplace @@ -144,7 +145,7 @@ public class MovementTraverse extends Movement { return COST_INF; // this is obviously impossible } WC = WC * (SNEAK_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST);//since we are sneak backplacing, we are sneaking lol - return WC + context.placeBlockCost + hardness1 + hardness2; + return WC + placeCost + hardness1 + hardness2; } return COST_INF; } diff --git a/src/main/java/baritone/pathing/path/PathExecutor.java b/src/main/java/baritone/pathing/path/PathExecutor.java index e34f74edb..cc17ef8c2 100644 --- a/src/main/java/baritone/pathing/path/PathExecutor.java +++ b/src/main/java/baritone/pathing/path/PathExecutor.java @@ -230,14 +230,14 @@ public class PathExecutor implements IPathExecutor, Helper { // do this only once, when the movement starts, and deliberately get the cost as cached when this path was calculated, not the cost as it is right now currentMovementOriginalCostEstimate = movement.getCost(); for (int i = 1; i < Baritone.settings().costVerificationLookahead.value && pathPosition + i < path.length() - 1; i++) { - if (path.movements().get(pathPosition + i).calculateCostWithoutCaching() >= ActionCosts.COST_INF && canCancel) { + if (((Movement) path.movements().get(pathPosition + i)).calculateCost(behavior.secretInternalGetCalculationContext()) >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and a future movement has become impossible. Cancelling."); cancel(); return true; } } } - double currentCost = movement.recalculateCost(); + double currentCost = ((Movement) movement).recalculateCost(behavior.secretInternalGetCalculationContext()); if (currentCost >= ActionCosts.COST_INF && canCancel) { logDebug("Something has changed in the world and this movement has become impossible. Cancelling."); cancel(); diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java new file mode 100644 index 000000000..a9e083b4a --- /dev/null +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -0,0 +1,678 @@ +/* + * 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.process; + +import baritone.Baritone; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalComposite; +import baritone.api.pathing.goals.GoalGetToBlock; +import baritone.api.process.IBuilderProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.*; +import baritone.api.utils.input.Input; +import baritone.pathing.movement.CalculationContext; +import baritone.pathing.movement.Movement; +import baritone.pathing.movement.MovementHelper; +import baritone.utils.BaritoneProcessHelper; +import baritone.utils.BlockStateInterface; +import baritone.utils.PathingCommandContext; +import baritone.utils.schematic.AirSchematic; +import baritone.utils.schematic.Schematic; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.init.Blocks; +import net.minecraft.item.BlockItemUseContext; +import net.minecraft.item.ItemBlock; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUseContext; +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.Tuple; +import net.minecraft.util.math.*; +import net.minecraft.util.math.shapes.VoxelShape; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +import static baritone.api.pathing.movement.ActionCosts.COST_INF; + +public class BuilderProcess extends BaritoneProcessHelper implements IBuilderProcess { + + public BuilderProcess(Baritone baritone) { + super(baritone); + } + + private HashSet incorrectPositions; + private String name; + private ISchematic schematic; + private Vec3i origin; + private int ticks; + + public boolean build(String schematicFile, BlockPos origin) { + File file = new File(new File(Minecraft.getInstance().gameDir, "schematics"), schematicFile); + System.out.println(file + " " + file.exists()); + return build(schematicFile, file, origin); + } + + @Override + public void build(String name, ISchematic schematic, Vec3i origin) { + this.name = name; + this.schematic = schematic; + this.origin = origin; + } + + @Override + public boolean build(String name, File schematic, Vec3i origin) { + NBTTagCompound tag; + try (FileInputStream fileIn = new FileInputStream(schematic)) { + tag = CompressedStreamTools.readCompressed(fileIn); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + if (tag == null) { + return false; + } + build(name, parse(tag), origin); + return true; + } + + public void clearArea(BlockPos corner1, BlockPos corner2) { + BlockPos origin = new BlockPos(Math.min(corner1.getX(), corner2.getX()), Math.min(corner1.getY(), corner2.getY()), Math.min(corner1.getZ(), corner2.getZ())); + int widthX = Math.abs(corner1.getX() - corner2.getX()) + 1; + int heightY = Math.abs(corner1.getY() - corner2.getY()) + 1; + int lengthZ = Math.abs(corner1.getZ() - corner2.getZ()) + 1; + build("clear area", new AirSchematic(widthX, heightY, lengthZ), origin); + } + + private static ISchematic parse(NBTTagCompound schematic) { + return new Schematic(schematic); + } + + @Override + public boolean isActive() { + return schematic != null; + } + + public IBlockState placeAt(int x, int y, int z) { + if (!isActive()) { + return null; + } + if (!schematic.inSchematic(x - origin.getX(), y - origin.getY(), z - origin.getZ())) { + return null; + } + IBlockState state = schematic.desiredState(x - origin.getX(), y - origin.getY(), z - origin.getZ()); + if (state.getBlock() == Blocks.AIR) { + return null; + } + return state; + } + + + public Optional> toBreakNearPlayer(BuilderCalculationContext bcc) { + BetterBlockPos center = ctx.playerFeet(); + for (int dx = -5; dx <= 5; dx++) { + for (int dy = 0; dy <= 5; dy++) { + for (int dz = -5; dz <= 5; dz++) { + int x = center.x + dx; + int y = center.y + dy; + int z = center.z + dz; + IBlockState desired = bcc.getSchematic(x, y, z); + if (desired == null) { + continue; // irrelevant + } + IBlockState curr = bcc.bsi.get0(x, y, z); + if (curr.getBlock() != Blocks.AIR && !valid(curr, desired)) { + BetterBlockPos pos = new BetterBlockPos(x, y, z); + Optional rot = RotationUtils.reachable(ctx.player(), pos, ctx.playerController().getBlockReachDistance()); + if (rot.isPresent()) { + return Optional.of(new Tuple<>(pos, rot.get())); + } + } + } + } + } + return Optional.empty(); + } + + public class Placement { + final int hotbarSelection; + final BlockPos placeAgainst; + final EnumFacing side; + final Rotation rot; + + public Placement(int hotbarSelection, BlockPos placeAgainst, EnumFacing side, Rotation rot) { + this.hotbarSelection = hotbarSelection; + this.placeAgainst = placeAgainst; + this.side = side; + this.rot = rot; + } + } + + public Optional searchForPlacables(BuilderCalculationContext bcc, List desirableOnHotbar) { + BetterBlockPos center = ctx.playerFeet(); + for (int dx = -5; dx <= 5; dx++) { + for (int dy = -5; dy <= 1; dy++) { + for (int dz = -5; dz <= 5; dz++) { + int x = center.x + dx; + int y = center.y + dy; + int z = center.z + dz; + IBlockState desired = bcc.getSchematic(x, y, z); + if (desired == null) { + continue; // irrelevant + } + IBlockState curr = bcc.bsi.get0(x, y, z); + if (MovementHelper.isReplacable(x, y, z, curr, bcc.bsi) && !valid(curr, desired)) { + if (dy == 1 && bcc.bsi.get0(x, y + 1, z).getBlock() == Blocks.AIR) { + continue; + } + desirableOnHotbar.add(desired); + Optional opt = possibleToPlace(desired, x, y, z, bcc.bsi); + if (opt.isPresent()) { + return opt; + } + } + } + } + } + return Optional.empty(); + } + + public boolean placementPlausible(BlockPos pos, IBlockState state) { + VoxelShape voxelshape = state.getCollisionShape(ctx.world(), pos); + return voxelshape.isEmpty() || ctx.world().checkNoEntityCollision(null, voxelshape.withOffset(pos.getX(), pos.getY(), pos.getZ())); + } + + public Optional possibleToPlace(IBlockState toPlace, int x, int y, int z, BlockStateInterface bsi) { + for (EnumFacing against : EnumFacing.values()) { + BetterBlockPos placeAgainstPos = new BetterBlockPos(x, y, z).offset(against); + IBlockState placeAgainstState = bsi.get0(placeAgainstPos); + if (MovementHelper.isReplacable(placeAgainstPos.x, placeAgainstPos.y, placeAgainstPos.z, placeAgainstState, bsi)) { + continue; + } + if (!toPlace.isValidPosition(ctx.world(), new BetterBlockPos(x, y, z))) { + continue; + } + if (!placementPlausible(new BetterBlockPos(x, y, z), toPlace)) { + continue; + } + AxisAlignedBB aabb = placeAgainstState.getShape(ctx.world(), placeAgainstPos).getBoundingBox(); + for (Vec3d placementMultiplier : aabbSideMultipliers(against)) { + double placeX = placeAgainstPos.x + aabb.minX * placementMultiplier.x + aabb.maxX * (1 - placementMultiplier.x); + double placeY = placeAgainstPos.y + aabb.minY * placementMultiplier.y + aabb.maxY * (1 - placementMultiplier.y); + double placeZ = placeAgainstPos.z + aabb.minZ * placementMultiplier.z + aabb.maxZ * (1 - placementMultiplier.z); + Rotation rot = RotationUtils.calcRotationFromVec3d(ctx.playerHead(), new Vec3d(placeX, placeY, placeZ), ctx.playerRotations()); + RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot, ctx.playerController().getBlockReachDistance()); + if (result != null && result.type == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(placeAgainstPos) && result.sideHit == against.getOpposite()) { + OptionalInt hotbar = hasAnyItemThatWouldPlace(toPlace, result, rot); + if (hotbar.isPresent()) { + return Optional.of(new Placement(hotbar.getAsInt(), placeAgainstPos, against.getOpposite(), rot)); + } + } + } + } + return Optional.empty(); + } + + + public OptionalInt hasAnyItemThatWouldPlace(IBlockState desired, RayTraceResult result, Rotation rot) { + for (int i = 0; i < 9; i++) { + ItemStack stack = ctx.player().inventory.mainInventory.get(i); + if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) { + continue; + } + float originalYaw = ctx.player().rotationYaw; + float originalPitch = ctx.player().rotationPitch; + // the state depends on the facing of the player sometimes + ctx.player().rotationYaw = rot.getYaw(); + ctx.player().rotationPitch = rot.getPitch(); + BlockItemUseContext meme = new BlockItemUseContext(new ItemUseContext( + ctx.player(), + stack, + result.getBlockPos().offset(result.sideHit), + result.sideHit, + (float) result.hitVec.x - result.getBlockPos().getX(), + (float) result.hitVec.y - result.getBlockPos().getY(), + (float) result.hitVec.z - result.getBlockPos().getZ() + )); + IBlockState wouldBePlaced = ((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(meme); + ctx.player().rotationYaw = originalYaw; + ctx.player().rotationPitch = originalPitch; + if (wouldBePlaced == null) { + continue; + } + if (!meme.canPlace()) { + continue; + } + if (valid(wouldBePlaced, desired)) { + return OptionalInt.of(i); + } + } + return OptionalInt.empty(); + } + + private static Vec3d[] aabbSideMultipliers(EnumFacing side) { + switch (side) { + case UP: + return new Vec3d[]{new Vec3d(0.5, 1, 0.5), new Vec3d(0.1, 1, 0.5), new Vec3d(0.9, 1, 0.5), new Vec3d(0.5, 1, 0.1), new Vec3d(0.5, 1, 0.9)}; + case DOWN: + return new Vec3d[]{new Vec3d(0.5, 0, 0.5), new Vec3d(0.1, 0, 0.5), new Vec3d(0.9, 0, 0.5), new Vec3d(0.5, 0, 0.1), new Vec3d(0.5, 0, 0.9)}; + case NORTH: + case SOUTH: + case EAST: + case WEST: + double x = side.getXOffset() == 0 ? 0.5 : (1 + side.getXOffset()) / 2D; + double z = side.getZOffset() == 0 ? 0.5 : (1 + side.getZOffset()) / 2D; + return new Vec3d[]{new Vec3d(x, 0.25, z), new Vec3d(x, 0.75, z)}; + default: // null + throw new NullPointerException(); + } + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + // TODO somehow tell inventorybehavior what we'd like to have on the hotbar + // perhaps take the 16 closest positions in incorrectPositions to ctx.playerFeet that aren't desired to be air, and then snag the top 4 most common block states, then request those on the hotbar + + + // this will work as is, but it'll be trashy + // need to iterate over incorrectPositions and see which ones we can "correct" from our current standing position + + baritone.getInputOverrideHandler().clearAllKeys(); + BuilderCalculationContext bcc = new BuilderCalculationContext(); + if (!recalc(bcc)) { + logDirect("Done building"); + onLostControl(); + return null; + } + trim(bcc); + if (baritone.getInputOverrideHandler().isInputForcedDown(Input.CLICK_LEFT)) { + ticks = 5; + } else { + ticks--; + } + Optional> toBreak = toBreakNearPlayer(bcc); + baritone.getInputOverrideHandler().clearAllKeys(); + if (toBreak.isPresent() && isSafeToCancel && ctx.player().onGround) { + // we'd like to pause to break this block + // only change look direction if it's safe (don't want to fuck up an in progress parkour for example + Rotation rot = toBreak.get().getB(); + BetterBlockPos pos = toBreak.get().getA(); + baritone.getLookBehavior().updateTarget(rot, true); + MovementHelper.switchToBestToolFor(ctx, bcc.get(pos)); + if (ctx.player().isSneaking()) { + // really horrible bug where a block is visible for breaking while sneaking but not otherwise + // so you can't see it, it goes to place something else, sneaks, then the next tick it tries to break + // and is unable since it's unsneaked in the intermediary tick + baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); + } + if (Objects.equals(ctx.objectMouseOver().getBlockPos(), pos) || ctx.playerRotations().isReallyCloseTo(rot)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_LEFT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + List desirableOnHotbar = new ArrayList<>(); + Optional toPlace = searchForPlacables(bcc, desirableOnHotbar); + if (toPlace.isPresent() && isSafeToCancel && ctx.player().onGround && ticks <= 0) { + Rotation rot = toPlace.get().rot; + baritone.getLookBehavior().updateTarget(rot, true); + ctx.player().inventory.currentItem = toPlace.get().hotbarSelection; + baritone.getInputOverrideHandler().setInputForceState(Input.SNEAK, true); + if ((Objects.equals(ctx.objectMouseOver().getBlockPos(), toPlace.get().placeAgainst) && ctx.objectMouseOver().sideHit.equals(toPlace.get().side)) || ctx.playerRotations().isReallyCloseTo(rot)) { + baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); + } + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + + List approxPlacable = placable(36); + if (Baritone.settings().allowInventory.value) { + ArrayList usefulSlots = new ArrayList<>(); + List noValidHotbarOption = new ArrayList<>(); + outer: + for (IBlockState desired : desirableOnHotbar) { + for (int i = 0; i < 9; i++) { + if (valid(approxPlacable.get(i), desired)) { + usefulSlots.add(i); + continue outer; + } + } + noValidHotbarOption.add(desired); + } + + outer: + for (int i = 9; i < 36; i++) { + for (IBlockState desired : noValidHotbarOption) { + if (valid(approxPlacable.get(i), desired)) { + baritone.getInventoryBehavior().attemptToPutOnHotbar(i, usefulSlots::contains); + break outer; + } + } + } + } + + + Goal goal = assemble(bcc, approxPlacable.subList(0, 9)); + if (goal == null) { + goal = assemble(bcc, approxPlacable); // we're far away, so assume that we have our whole inventory to recalculate placable properly + if (goal == null) { + logDirect("Unable to do it =("); + onLostControl(); + return null; + } + } + return new PathingCommandContext(goal, PathingCommandType.FORCE_REVALIDATE_GOAL_AND_PATH, bcc); + } + + public boolean recalc(BuilderCalculationContext bcc) { + if (incorrectPositions == null) { + incorrectPositions = new HashSet<>(); + fullRecalc(bcc); + if (incorrectPositions.isEmpty()) { + return false; + } + } + recalcNearby(bcc); + if (incorrectPositions.isEmpty()) { + fullRecalc(bcc); + } + return !incorrectPositions.isEmpty(); + } + + public void trim(BuilderCalculationContext bcc) { + HashSet copy = new HashSet<>(incorrectPositions); + copy.removeIf(pos -> pos.distanceSq(ctx.player().posX, ctx.player().posY, ctx.player().posZ) > 200); + if (!copy.isEmpty()) { + incorrectPositions = copy; + } + } + + public void recalcNearby(BuilderCalculationContext bcc) { + BetterBlockPos center = ctx.playerFeet(); + for (int dx = -5; dx <= 5; dx++) { + for (int dy = -5; dy <= 5; dy++) { + for (int dz = -5; dz <= 5; dz++) { + int x = center.x + dx; + int y = center.y + dy; + int z = center.z + dz; + IBlockState desired = bcc.getSchematic(x, y, z); + if (desired != null) { + // we care about this position + if (valid(bcc.bsi.get0(x, y, z), desired)) { + incorrectPositions.remove(new BetterBlockPos(x, y, z)); + } else { + incorrectPositions.add(new BetterBlockPos(x, y, z)); + } + } + } + } + } + } + + public void fullRecalc(BuilderCalculationContext bcc) { + incorrectPositions = new HashSet<>(); + for (int y = 0; y < schematic.heightY(); y++) { + for (int z = 0; z < schematic.lengthZ(); z++) { + for (int x = 0; x < schematic.widthX(); x++) { + if (schematic.inSchematic(x, y, z)) { + if (!valid(bcc.bsi.get0(x + origin.getX(), y + origin.getY(), z + origin.getZ()), schematic.desiredState(x, y, z))) { + incorrectPositions.add(new BetterBlockPos(x + origin.getX(), y + origin.getY(), z + origin.getZ())); + } + } + } + } + } + } + + private Goal assemble(BuilderCalculationContext bcc, List approxPlacable) { + List placable = incorrectPositions.stream().filter(pos -> bcc.bsi.get0(pos).getBlock() == Blocks.AIR && approxPlacable.contains(bcc.getSchematic(pos.x, pos.y, pos.z))).collect(Collectors.toList()); + Goal[] toBreak = incorrectPositions.stream().filter(pos -> bcc.bsi.get0(pos).getBlock() != Blocks.AIR).map(GoalBreak::new).toArray(Goal[]::new); + Goal[] toPlace = placable.stream().filter(pos -> !placable.contains(pos.down()) && !placable.contains(pos.down(2))).map(pos -> placementgoal(pos, bcc)).toArray(Goal[]::new); + + if (toPlace.length != 0) { + return new JankyGoalComposite(new GoalComposite(toPlace), new GoalComposite(toBreak)); + } + if (toBreak.length == 0) { + return null; + } + return new GoalComposite(toBreak); + } + + public static class JankyGoalComposite implements Goal { + private final Goal primary; + private final Goal fallback; + + public JankyGoalComposite(Goal primary, Goal fallback) { + this.primary = primary; + this.fallback = fallback; + } + + + @Override + public boolean isInGoal(int x, int y, int z) { + return primary.isInGoal(x, y, z) || fallback.isInGoal(x, y, z); + } + + @Override + public double heuristic(int x, int y, int z) { + return primary.heuristic(x, y, z); + } + + @Override + public String toString() { + return "JankyComposite Primary: " + primary + " Fallback: " + fallback; + } + } + + public static class GoalBreak extends GoalGetToBlock { + + public GoalBreak(BlockPos pos) { + super(pos); + } + + @Override + public boolean isInGoal(int x, int y, int z) { + // can't stand right on top of a block, that might not work (what if it's unsupported, can't break then) + if (y > this.y) { + return false; + } + // but any other adjacent works for breaking, including inside or below + return super.isInGoal(x, y, z); + } + } + + public Goal placementgoal(BlockPos pos, BuilderCalculationContext bcc) { + if (ctx.world().getBlockState(pos).getBlock() != Blocks.AIR) { + return new GoalPlace(pos); + } + boolean allowSameLevel = ctx.world().getBlockState(pos.up()).getBlock() != Blocks.AIR; + for (EnumFacing facing : Movement.HORIZONTALS_BUT_ALSO_DOWN____SO_EVERY_DIRECTION_EXCEPT_UP) { + if (MovementHelper.canPlaceAgainst(ctx, pos.offset(facing)) && placementPlausible(pos, bcc.getSchematic(pos.getX(), pos.getY(), pos.getZ()))) { + return new GoalAdjacent(pos, allowSameLevel); + } + } + return new GoalPlace(pos); + } + + public static class GoalAdjacent extends GoalGetToBlock { + boolean allowSameLevel; + + public GoalAdjacent(BlockPos pos, boolean allowSameLevel) { + super(pos); + this.allowSameLevel = allowSameLevel; + } + + public boolean isInGoal(int x, int y, int z) { + if (x == this.x && y == this.y && z == this.z) { + return false; + } + if (!allowSameLevel && y == this.y - 1) { + return false; + } + if (y < this.y - 1) { + return false; + } + return super.isInGoal(x, y, z); + } + + public double heuristic(int x, int y, int z) { + // prioritize lower y coordinates + return this.y * 100 + super.heuristic(x, y, z); + } + } + + public static class GoalPlace extends GoalBlock { + public GoalPlace(BlockPos placeAt) { + super(placeAt.up()); + } + + public double heuristic(int x, int y, int z) { + // prioritize lower y coordinates + return this.y * 100 + super.heuristic(x, y, z); + } + } + + @Override + public void onLostControl() { + incorrectPositions = null; + name = null; + schematic = null; + } + + @Override + public String displayName0() { + return "Building " + name; + } + + public List placable(int size) { + List result = new ArrayList<>(); + for (int i = 0; i < size; i++) { + ItemStack stack = ctx.player().inventory.mainInventory.get(i); + if (stack.isEmpty() || !(stack.getItem() instanceof ItemBlock)) { + result.add(Blocks.AIR.getDefaultState()); + continue; + } + // + result.add(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(new BlockItemUseContext(new ItemUseContext(ctx.player(), stack, ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ)))); + // + } + return result; + } + + public boolean valid(IBlockState current, IBlockState desired) { + // TODO more complicated comparison logic I guess + return desired == null || current.equals(desired); + } + + public class BuilderCalculationContext extends CalculationContext { + private final List placable; + private final ISchematic schematic; + private final int originX; + private final int originY; + private final int originZ; + + public BuilderCalculationContext() { + super(BuilderProcess.this.baritone, true); // wew lad + this.placable = placable(9); + this.schematic = BuilderProcess.this.schematic; + this.originX = origin.getX(); + this.originY = origin.getY(); + this.originZ = origin.getZ(); + + this.jumpPenalty += 10; + this.backtrackCostFavoringCoefficient = 1; + } + + private IBlockState getSchematic(int x, int y, int z) { + if (schematic.inSchematic(x - originX, y - originY, z - originZ)) { + return schematic.desiredState(x - originX, y - originY, z - originZ); + } else { + return null; + } + } + + @Override + public double costOfPlacingAt(int x, int y, int z) { + if (isPossiblyProtected(x, y, z) || !worldBorder.canPlaceAt(x, z)) { // make calculation fail properly if we can't build + return COST_INF; + } + IBlockState sch = getSchematic(x, y, z); + if (sch != null) { + // TODO this can return true even when allowPlace is off.... is that an issue? + if (sch.getBlock() == Blocks.AIR) { + // we want this to be air, but they're asking if they can place here + // this won't be a schematic block, this will be a throwaway + return placeBlockCost * 2; // we're going to have to break it eventually + } + if (placable.contains(sch)) { + return 0; // thats right we gonna make it FREE to place a block where it should go in a structure + // no place block penalty at all 😎 + // i'm such an idiot that i just tried to copy and paste the epic gamer moment emoji too + // get added to unicode when? + } + if (!hasThrowaway) { + return COST_INF; + } + // we want it to be something that we don't have + // even more of a pain to place something wrong + return placeBlockCost * 3; + } else { + if (hasThrowaway) { + return placeBlockCost; + } else { + return COST_INF; + } + } + } + + @Override + public double breakCostMultiplierAt(int x, int y, int z) { + if (!allowBreak || isPossiblyProtected(x, y, z)) { + return COST_INF; + } + IBlockState sch = getSchematic(x, y, z); + if (sch != null) { + if (sch.getBlock() == Blocks.AIR) { + // it should be air + // regardless of current contents, we can break it + return 1; + } + // it should be a real block + // is it already that block? + if (valid(bsi.get0(x, y, z), sch)) { + return 3; + } else { + // can break if it's wrong + // would be great to return less than 1 here, but that would actually make the cost calculation messed up + // since we're breaking a block, if we underestimate the cost, then it'll fail when it really takes the correct amount of time + return 1; + + } + // TODO do blocks in render distace only? + // TODO allow breaking blocks that we have a tool to harvest and immediately place back? + } else { + return 1; // why not lol + } + } + } +} diff --git a/src/main/java/baritone/process/CustomGoalProcess.java b/src/main/java/baritone/process/CustomGoalProcess.java index fe1de0286..fe361ad6a 100644 --- a/src/main/java/baritone/process/CustomGoalProcess.java +++ b/src/main/java/baritone/process/CustomGoalProcess.java @@ -105,7 +105,7 @@ public class CustomGoalProcess extends BaritoneProcessHelper implements ICustomG } @Override - public String displayName() { + public String displayName0() { return "Custom Goal " + this.goal; } diff --git a/src/main/java/baritone/process/ExploreProcess.java b/src/main/java/baritone/process/ExploreProcess.java index 3343d8829..eb98cbde7 100644 --- a/src/main/java/baritone/process/ExploreProcess.java +++ b/src/main/java/baritone/process/ExploreProcess.java @@ -94,7 +94,7 @@ public class ExploreProcess extends BaritoneProcessHelper { } @Override - public String displayName() { + public String displayName0() { return "Exploring around " + explorationOrigin + ", currently going to " + closestUncachedChunk(explorationOrigin); } } diff --git a/src/main/java/baritone/process/FollowProcess.java b/src/main/java/baritone/process/FollowProcess.java index cc5346fe1..60776e8fd 100644 --- a/src/main/java/baritone/process/FollowProcess.java +++ b/src/main/java/baritone/process/FollowProcess.java @@ -101,7 +101,7 @@ public final class FollowProcess extends BaritoneProcessHelper implements IFollo } @Override - public String displayName() { + public String displayName0() { return "Follow " + cache; } diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index 8ca30abbf..5664bebba 100644 --- a/src/main/java/baritone/process/GetToBlockProcess.java +++ b/src/main/java/baritone/process/GetToBlockProcess.java @@ -160,11 +160,11 @@ public class GetToBlockProcess extends BaritoneProcessHelper implements IGetToBl } @Override - public String displayName() { + public String displayName0() { if (knownLocations.isEmpty()) { return "Exploring randomly to find " + gettingTo + ", no known locations"; } - return "Get To Block " + gettingTo + ", " + knownLocations.size() + " known locations"; + return "Get To " + gettingTo + ", " + knownLocations.size() + " known locations"; } private synchronized void rescan(List known, CalculationContext context) { diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index c05dcd203..598ecbba5 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -117,7 +117,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } @Override - public String displayName() { + public String displayName0() { return "Mine " + mining; } diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index d81fbf6ca..bf2d52022 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -266,6 +266,22 @@ public class ExampleBaritoneControl extends Behavior implements Helper { logDirect("Queued " + count + " chunks for repacking"); return true; } + if (msg.startsWith("build")) { + String file; + BlockPos origin; + try { + String[] coords = msg.substring("build".length()).trim().split(" "); + file = coords[0] + ".schematic"; + origin = new BlockPos(parseOrDefault(coords[1], ctx.playerFeet().x), parseOrDefault(coords[2], ctx.playerFeet().y), parseOrDefault(coords[3], ctx.playerFeet().z)); + } catch (Exception ex) { + file = msg.substring(5).trim() + ".schematic"; + origin = ctx.playerFeet(); + } + logDirect("Loading '" + file + "' to build from origin " + origin); + boolean success = baritone.getBuilderProcess().build(file, origin); + logDirect(success ? "Loaded" : "Unable to load"); + return true; + } if (msg.equals("come")) { customGoalProcess.setGoalAndPath(new GoalBlock(new BlockPos(mc.getRenderViewEntity()))); logDirect("Coming"); @@ -311,6 +327,32 @@ public class ExampleBaritoneControl extends Behavior implements Helper { }); return true; } + if (msg.startsWith("cleararea")) { + String suffix = msg.substring("cleararea".length()); + BlockPos corner1; + BlockPos corner2; + if (suffix.isEmpty()) { + // clear the area from the current goal to here + Goal goal = baritone.getPathingBehavior().getGoal(); + if (goal == null || !(goal instanceof GoalBlock)) { + logDirect("Need to specify goal of opposite corner"); + return true; + } + corner1 = ((GoalBlock) goal).getGoalPos(); + corner2 = ctx.playerFeet(); + } else { + try { + String[] spl = suffix.split(" "); + corner1 = ctx.playerFeet(); + corner2 = new BlockPos(Integer.parseInt(spl[0]), Integer.parseInt(spl[1]), Integer.parseInt(spl[2])); + } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) { + logDirect("unable to parse"); + return true; + } + } + baritone.getBuilderProcess().clearArea(corner1, corner2); + return true; + } if (msg.equals("reset")) { for (Settings.Setting setting : Baritone.settings().allSettings) { setting.reset(); @@ -441,7 +483,7 @@ public class ExampleBaritoneControl extends Behavior implements Helper { new Thread(() -> { try { Thread.sleep(100); - mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiClickMeme())); + mc.addScheduledTask(() -> mc.displayGuiScreen(new GuiClick())); } catch (Exception ignored) {} }).start(); logDirect("aight dude"); @@ -573,7 +615,7 @@ public class ExampleBaritoneControl extends Behavior implements Helper { while (moves.contains(null)) { moves.remove(null); } - moves.sort(Comparator.comparingDouble(Movement::getCost)); + moves.sort(Comparator.comparingDouble(move -> move.getCost(new CalculationContext(baritone)))); for (Movement move : moves) { String[] parts = move.getClass().toString().split("\\."); double cost = move.getCost(); @@ -591,6 +633,10 @@ public class ExampleBaritoneControl extends Behavior implements Helper { return false; } + private int parseOrDefault(String str, int i) { + return str.equals("~") ? i : str.startsWith("~") ? Integer.parseInt(str.substring(1)) + i : Integer.parseInt(str); + } + private void log(List stacks) { for (ItemStack stack : stacks) { if (!stack.isEmpty()) { diff --git a/src/main/java/baritone/utils/GuiClick.java b/src/main/java/baritone/utils/GuiClick.java new file mode 100644 index 000000000..f0a3eb9a6 --- /dev/null +++ b/src/main/java/baritone/utils/GuiClick.java @@ -0,0 +1,320 @@ +/* + * 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.utils; + +import baritone.Baritone; +import baritone.api.BaritoneAPI; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalTwoBlocks; +import baritone.api.utils.BetterBlockPos; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.*; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; + +import java.awt.*; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.Collections; + +import static org.lwjgl.opengl.GL11.*; + +public class GuiClick extends GuiScreen { + + // My name is Brady and I grant leijurv permission to use this pasted code + private final FloatBuffer MODELVIEW = BufferUtils.createFloatBuffer(16); + private final FloatBuffer PROJECTION = BufferUtils.createFloatBuffer(16); + private final IntBuffer VIEWPORT = BufferUtils.createIntBuffer(16); + private final FloatBuffer TO_WORLD_BUFFER = BufferUtils.createFloatBuffer(3); + + private BlockPos clickStart; + private BlockPos currentMouseOver; + + @Override + public boolean doesGuiPauseGame() { + return false; + } + + @Override + + public void render(int mouseX, int mouseY, float partialTicks) { + double mx = mc.mouseHelper.getMouseX(); + double my = mc.mouseHelper.getMouseY(); + my = mc.mainWindow.getHeight() - my; + my *= 2; + mx *= 2; + Vec3d near = toWorld(mx, my, 0); + Vec3d far = toWorld(mx, my, 1); // "Use 0.945 that's what stack overflow says" - leijurv + if (near != null && far != null) { + Vec3d viewerPos = new Vec3d(mc.getRenderManager().viewerPosX, mc.getRenderManager().viewerPosY, mc.getRenderManager().viewerPosZ); + RayTraceResult result = mc.world.rayTraceBlocks(near.add(viewerPos), far.add(viewerPos), RayTraceFluidMode.NEVER, false, true); + if (result != null && result.type == RayTraceResult.Type.BLOCK) { + currentMouseOver = result.getBlockPos(); + } + } + + } + + @Override + public boolean mouseReleased(double mouseX, double mouseY, int mouseButton) { + if (mouseButton == 0) { + if (clickStart != null && !clickStart.equals(currentMouseOver)) { + ((Baritone) BaritoneAPI.getProvider().getPrimaryBaritone()).getBuilderProcess().clearArea(clickStart, currentMouseOver); + clickStart = null; + } else { + BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalTwoBlocks(currentMouseOver)); + } + } else if (mouseButton == 1) { + BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalBlock(currentMouseOver.up())); + } + clickStart = null; + return super.mouseReleased(mouseX, mouseY, mouseButton); + } + + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + clickStart = currentMouseOver; + return super.mouseClicked(mouseX, mouseY, mouseButton); + } + + public void onRender() { + GlStateManager.getFloatv(GL_MODELVIEW_MATRIX, (FloatBuffer) MODELVIEW.clear()); + GlStateManager.getFloatv(GL_PROJECTION_MATRIX, (FloatBuffer) PROJECTION.clear()); + GL11.glGetIntegerv(GL_VIEWPORT, (IntBuffer) VIEWPORT.clear()); + + if (currentMouseOver != null) { + Entity e = mc.getRenderViewEntity(); + // drawSingleSelectionBox WHEN? + PathRenderer.drawManySelectionBoxes(e, Collections.singletonList(currentMouseOver), Color.CYAN); + if (clickStart != null && !clickStart.equals(currentMouseOver)) { + GlStateManager.enableBlend(); + GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); + GlStateManager.color4f(Color.RED.getColorComponents(null)[0], Color.RED.getColorComponents(null)[1], Color.RED.getColorComponents(null)[2], 0.4F); + GlStateManager.lineWidth(Baritone.settings().pathRenderLineWidthPixels.value); + GlStateManager.disableTexture2D(); + GlStateManager.depthMask(false); + GlStateManager.disableDepthTest(); + BetterBlockPos a = new BetterBlockPos(currentMouseOver); + BetterBlockPos b = new BetterBlockPos(clickStart); + PathRenderer.drawAABB(new AxisAlignedBB(Math.min(a.x, b.x), Math.min(a.y, b.y), Math.min(a.z, b.z), Math.max(a.x, b.x) + 1, Math.max(a.y, b.y) + 1, Math.max(a.z, b.z) + 1)); + GlStateManager.enableDepthTest(); + + GlStateManager.depthMask(true); + GlStateManager.enableTexture2D(); + GlStateManager.disableBlend(); + } + } + + + } + + public Vec3d toWorld ( double x, double y, double z){ + boolean result = gluUnProject((float) x, (float) y, (float) z, MODELVIEW, PROJECTION, VIEWPORT, (FloatBuffer) TO_WORLD_BUFFER.clear()); + if (result) { + return new Vec3d(TO_WORLD_BUFFER.get(0), TO_WORLD_BUFFER.get(1), TO_WORLD_BUFFER.get(2)); + } + return null; + } + + // skidded from lwjgl2 :ok_hand: + // its uhhhhh mit license so its ok + // here is the uhh license + /* + * Copyright (c) 2002-2007 Lightweight Java Game Library Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'Light Weight Java Game Library' nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + public static boolean gluUnProject ( + float winx, + float winy, + float winz, + FloatBuffer modelMatrix, + FloatBuffer projMatrix, + IntBuffer viewport, + FloatBuffer obj_pos){ + FloatBuffer finalMatrix = BufferUtils.createFloatBuffer(16); + float[] in = new float[4]; + float[] out = new float[4]; + + __gluMultMatricesf(modelMatrix, projMatrix, finalMatrix); + + if (!__gluInvertMatrixf(finalMatrix, finalMatrix)) + return false; + + in[0] = winx; + in[1] = winy; + in[2] = winz; + in[3] = 1.0f; + + // Map x and y from window coordinates + in[0] = (in[0] - viewport.get(viewport.position() + 0)) / viewport.get(viewport.position() + 2); + in[1] = (in[1] - viewport.get(viewport.position() + 1)) / viewport.get(viewport.position() + 3); + + // Map to range -1 to 1 + in[0] = in[0] * 2 - 1; + in[1] = in[1] * 2 - 1; + in[2] = in[2] * 2 - 1; + + __gluMultMatrixVecf(finalMatrix, in, out); + + if (out[3] == 0.0) + return false; + + out[3] = 1.0f / out[3]; + + obj_pos.put(obj_pos.position() + 0, out[0] * out[3]); + obj_pos.put(obj_pos.position() + 1, out[1] * out[3]); + obj_pos.put(obj_pos.position() + 2, out[2] * out[3]); + + return true; + } + + private static void __gluMultMatrixVecf (FloatBuffer m,float[] in, float[] out){ + for (int i = 0; i < 4; i++) { + out[i] = + in[0] * m.get(m.position() + 0 * 4 + i) + + in[1] * m.get(m.position() + 1 * 4 + i) + + in[2] * m.get(m.position() + 2 * 4 + i) + + in[3] * m.get(m.position() + 3 * 4 + i); + + } + } + + private static void __gluMultMatricesf (FloatBuffer a, FloatBuffer b, FloatBuffer r){ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + r.put(r.position() + i * 4 + j, + a.get(a.position() + i * 4 + 0) * b.get(b.position() + 0 * 4 + j) + a.get(a.position() + i * 4 + 1) * b.get(b.position() + 1 * 4 + j) + a.get(a.position() + i * 4 + 2) * b.get(b.position() + 2 * 4 + j) + a.get(a.position() + i * 4 + 3) * b.get(b.position() + 3 * 4 + j)); + } + } + } + + private static boolean __gluInvertMatrixf (FloatBuffer src, FloatBuffer inverse){ + int i, j, k, swap; + float t; + FloatBuffer temp = BufferUtils.createFloatBuffer(16); + + + for (i = 0; i < 16; i++) { + temp.put(i, src.get(i + src.position())); + } + __gluMakeIdentityf(inverse); + + for (i = 0; i < 4; i++) { + /* + * * Look for largest element in column + */ + swap = i; + for (j = i + 1; j < 4; j++) { + /* + * if (fabs(temp[j][i]) > fabs(temp[i][i])) { swap = j; + */ + if (Math.abs(temp.get(j * 4 + i)) > Math.abs(temp.get(i * 4 + i))) { + swap = j; + } + } + + if (swap != i) { + /* + * * Swap rows. + */ + for (k = 0; k < 4; k++) { + t = temp.get(i * 4 + k); + temp.put(i * 4 + k, temp.get(swap * 4 + k)); + temp.put(swap * 4 + k, t); + + t = inverse.get(i * 4 + k); + inverse.put(i * 4 + k, inverse.get(swap * 4 + k)); + //inverse.put((i << 2) + k, inverse.get((swap << 2) + k)); + inverse.put(swap * 4 + k, t); + //inverse.put((swap << 2) + k, t); + } + } + + if (temp.get(i * 4 + i) == 0) { + /* + * * No non-zero pivot. The matrix is singular, which shouldn't * + * happen. This means the user gave us a bad matrix. + */ + return false; + } + + t = temp.get(i * 4 + i); + for (k = 0; k < 4; k++) { + temp.put(i * 4 + k, temp.get(i * 4 + k) / t); + inverse.put(i * 4 + k, inverse.get(i * 4 + k) / t); + } + for (j = 0; j < 4; j++) { + if (j != i) { + t = temp.get(j * 4 + i); + for (k = 0; k < 4; k++) { + temp.put(j * 4 + k, temp.get(j * 4 + k) - temp.get(i * 4 + k) * t); + inverse.put(j * 4 + k, inverse.get(j * 4 + k) - inverse.get(i * 4 + k) * t); + /*inverse.put( + (j << 2) + k, + inverse.get((j << 2) + k) - inverse.get((i << 2) + k) * t);*/ + } + } + } + } + return true; + } + + private static final float[] IDENTITY_MATRIX = + new float[]{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + + private static void __gluMakeIdentityf (FloatBuffer m){ + int oldPos = m.position(); + m.put(IDENTITY_MATRIX); + m.position(oldPos); + } + } diff --git a/src/main/java/baritone/utils/GuiClickMeme.java b/src/main/java/baritone/utils/GuiClickMeme.java deleted file mode 100644 index 07bf5aa17..000000000 --- a/src/main/java/baritone/utils/GuiClickMeme.java +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 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.utils; - -import baritone.api.BaritoneAPI; -import baritone.api.pathing.goals.GoalBlock; -import baritone.api.pathing.goals.GoalTwoBlocks; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.entity.Entity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceFluidMode; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import org.lwjgl.BufferUtils; -import org.lwjgl.opengl.GL11; - -import java.awt.*; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.util.Collections; - -import static org.lwjgl.opengl.GL11.*; - -public class GuiClickMeme extends GuiScreen { - - // My name is Brady and I grant leijurv permission to use this pasted code - private final FloatBuffer MODELVIEW = BufferUtils.createFloatBuffer(16); - private final FloatBuffer PROJECTION = BufferUtils.createFloatBuffer(16); - private final IntBuffer VIEWPORT = BufferUtils.createIntBuffer(16); - private final FloatBuffer TO_WORLD_BUFFER = BufferUtils.createFloatBuffer(3); - - private BlockPos meme; - - @Override - public boolean doesGuiPauseGame() { - return false; - } - - @Override - - public void render(int mouseX, int mouseY, float partialTicks) { - double mx = mc.mouseHelper.getMouseX(); - double my = mc.mouseHelper.getMouseY(); - my = mc.mainWindow.getHeight() - my; - my *= 2; - mx *= 2; - Vec3d near = toWorld(mx, my, 0); - Vec3d far = toWorld(mx, my, 1); // "Use 0.945 that's what stack overflow says" - leijurv - if (near != null && far != null) { - Vec3d viewerPos = new Vec3d(mc.getRenderManager().viewerPosX, mc.getRenderManager().viewerPosY, mc.getRenderManager().viewerPosZ); - RayTraceResult result = mc.world.rayTraceBlocks(near.add(viewerPos), far.add(viewerPos), RayTraceFluidMode.NEVER, false, true); - if (result != null && result.type == RayTraceResult.Type.BLOCK) { - meme = result.getBlockPos(); - } - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { - super.mouseClicked(mouseX, mouseY, mouseButton); - if (mouseButton == 0) { - BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalTwoBlocks(meme)); - } else if (mouseButton == 1) { - BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalBlock(meme.up())); - } - return false; - } - - public void onRender() { - GlStateManager.getFloatv(GL_MODELVIEW_MATRIX, (FloatBuffer) MODELVIEW.clear()); - GlStateManager.getFloatv(GL_PROJECTION_MATRIX, (FloatBuffer) PROJECTION.clear()); - GL11.glGetIntegerv(GL_VIEWPORT, (IntBuffer) VIEWPORT.clear()); - - if (meme != null) { - Entity e = mc.getRenderViewEntity(); - // drawSingleSelectionBox WHEN? - PathRenderer.drawManySelectionBoxes(e, Collections.singletonList(meme), Color.CYAN); - } - } - - public Vec3d toWorld(double x, double y, double z) { - boolean result = gluUnProject((float) x, (float) y, (float) z, MODELVIEW, PROJECTION, VIEWPORT, (FloatBuffer) TO_WORLD_BUFFER.clear()); - if (result) { - return new Vec3d(TO_WORLD_BUFFER.get(0), TO_WORLD_BUFFER.get(1), TO_WORLD_BUFFER.get(2)); - } - return null; - } - - // skidded from lwjgl2 :ok_hand: - // its uhhhhh mit license so its ok - // here is the uhh license - /* - * Copyright (c) 2002-2007 Lightweight Java Game Library Project - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'Light Weight Java Game Library' nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - public static boolean gluUnProject( - float winx, - float winy, - float winz, - FloatBuffer modelMatrix, - FloatBuffer projMatrix, - IntBuffer viewport, - FloatBuffer obj_pos) { - FloatBuffer finalMatrix = BufferUtils.createFloatBuffer(16); - float[] in = new float[4]; - float[] out = new float[4]; - - __gluMultMatricesf(modelMatrix, projMatrix, finalMatrix); - - if (!__gluInvertMatrixf(finalMatrix, finalMatrix)) - return false; - - in[0] = winx; - in[1] = winy; - in[2] = winz; - in[3] = 1.0f; - - // Map x and y from window coordinates - in[0] = (in[0] - viewport.get(viewport.position() + 0)) / viewport.get(viewport.position() + 2); - in[1] = (in[1] - viewport.get(viewport.position() + 1)) / viewport.get(viewport.position() + 3); - - // Map to range -1 to 1 - in[0] = in[0] * 2 - 1; - in[1] = in[1] * 2 - 1; - in[2] = in[2] * 2 - 1; - - __gluMultMatrixVecf(finalMatrix, in, out); - - if (out[3] == 0.0) - return false; - - out[3] = 1.0f / out[3]; - - obj_pos.put(obj_pos.position() + 0, out[0] * out[3]); - obj_pos.put(obj_pos.position() + 1, out[1] * out[3]); - obj_pos.put(obj_pos.position() + 2, out[2] * out[3]); - - return true; - } - - private static void __gluMultMatrixVecf(FloatBuffer m, float[] in, float[] out) { - for (int i = 0; i < 4; i++) { - out[i] = - in[0] * m.get(m.position() + 0 * 4 + i) - + in[1] * m.get(m.position() + 1 * 4 + i) - + in[2] * m.get(m.position() + 2 * 4 + i) - + in[3] * m.get(m.position() + 3 * 4 + i); - - } - } - - private static void __gluMultMatricesf(FloatBuffer a, FloatBuffer b, FloatBuffer r) { - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - r.put(r.position() + i * 4 + j, - a.get(a.position() + i * 4 + 0) * b.get(b.position() + 0 * 4 + j) + a.get(a.position() + i * 4 + 1) * b.get(b.position() + 1 * 4 + j) + a.get(a.position() + i * 4 + 2) * b.get(b.position() + 2 * 4 + j) + a.get(a.position() + i * 4 + 3) * b.get(b.position() + 3 * 4 + j)); - } - } - } - - private static boolean __gluInvertMatrixf(FloatBuffer src, FloatBuffer inverse) { - int i, j, k, swap; - float t; - FloatBuffer temp = BufferUtils.createFloatBuffer(16); - - - for (i = 0; i < 16; i++) { - temp.put(i, src.get(i + src.position())); - } - __gluMakeIdentityf(inverse); - - for (i = 0; i < 4; i++) { - /* - * * Look for largest element in column - */ - swap = i; - for (j = i + 1; j < 4; j++) { - /* - * if (fabs(temp[j][i]) > fabs(temp[i][i])) { swap = j; - */ - if (Math.abs(temp.get(j * 4 + i)) > Math.abs(temp.get(i * 4 + i))) { - swap = j; - } - } - - if (swap != i) { - /* - * * Swap rows. - */ - for (k = 0; k < 4; k++) { - t = temp.get(i * 4 + k); - temp.put(i * 4 + k, temp.get(swap * 4 + k)); - temp.put(swap * 4 + k, t); - - t = inverse.get(i * 4 + k); - inverse.put(i * 4 + k, inverse.get(swap * 4 + k)); - //inverse.put((i << 2) + k, inverse.get((swap << 2) + k)); - inverse.put(swap * 4 + k, t); - //inverse.put((swap << 2) + k, t); - } - } - - if (temp.get(i * 4 + i) == 0) { - /* - * * No non-zero pivot. The matrix is singular, which shouldn't * - * happen. This means the user gave us a bad matrix. - */ - return false; - } - - t = temp.get(i * 4 + i); - for (k = 0; k < 4; k++) { - temp.put(i * 4 + k, temp.get(i * 4 + k) / t); - inverse.put(i * 4 + k, inverse.get(i * 4 + k) / t); - } - for (j = 0; j < 4; j++) { - if (j != i) { - t = temp.get(j * 4 + i); - for (k = 0; k < 4; k++) { - temp.put(j * 4 + k, temp.get(j * 4 + k) - temp.get(i * 4 + k) * t); - inverse.put(j * 4 + k, inverse.get(j * 4 + k) - inverse.get(i * 4 + k) * t); - /*inverse.put( - (j << 2) + k, - inverse.get((j << 2) + k) - inverse.get((i << 2) + k) * t);*/ - } - } - } - } - return true; - } - - private static final float[] IDENTITY_MATRIX = - new float[]{ - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - - private static void __gluMakeIdentityf(FloatBuffer m) { - int oldPos = m.position(); - m.put(IDENTITY_MATRIX); - m.position(oldPos); - } -} diff --git a/src/main/java/baritone/utils/InputOverrideHandler.java b/src/main/java/baritone/utils/InputOverrideHandler.java index 5f393059f..4bd7982b3 100755 --- a/src/main/java/baritone/utils/InputOverrideHandler.java +++ b/src/main/java/baritone/utils/InputOverrideHandler.java @@ -115,4 +115,4 @@ public final class InputOverrideHandler extends Behavior implements IInputOverri public BlockBreakHelper getBlockBreakHelper() { return blockBreakHelper; } -} +} \ No newline at end of file diff --git a/src/main/java/baritone/utils/PathRenderer.java b/src/main/java/baritone/utils/PathRenderer.java index 283220059..1725f0c66 100644 --- a/src/main/java/baritone/utils/PathRenderer.java +++ b/src/main/java/baritone/utils/PathRenderer.java @@ -62,8 +62,8 @@ public final class PathRenderer implements Helper { public static void render(RenderEvent event, PathingBehavior behavior) { float partialTicks = event.getPartialTicks(); Goal goal = behavior.getGoal(); - if (mc.currentScreen instanceof GuiClickMeme) { - ((GuiClickMeme) mc.currentScreen).onRender(); + if (mc.currentScreen instanceof GuiClick) { + ((GuiClick) mc.currentScreen).onRender(); } int thisPlayerDimension = behavior.baritone.getPlayerContext().world().getDimension().getType().getId(); @@ -204,7 +204,7 @@ public final class PathRenderer implements Helper { public static void drawManySelectionBoxes(Entity player, Collection positions, Color color) { GlStateManager.enableBlend(); - GlStateManager.blendFuncSeparate(770, 771, 1, 0); + GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); GlStateManager.color4f(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.4F); GlStateManager.lineWidth(Baritone.settings().pathRenderLineWidthPixels.value); GlStateManager.disableTexture2D(); @@ -214,40 +214,15 @@ public final class PathRenderer implements Helper { GlStateManager.disableDepthTest(); } - float expand = 0.002F; + //BlockPos blockpos = movingObjectPositionIn.getBlockPos(); BlockStateInterface bsi = new BlockStateInterface(BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext()); // TODO this assumes same dimension between primary baritone and render view? is this safe? positions.forEach(pos -> { IBlockState state = bsi.get0(pos); - VoxelShape shape = state.getShape(player.world, pos); AxisAlignedBB toDraw = shape.isEmpty() ? VoxelShapes.fullCube().getBoundingBox() : shape.getBoundingBox(); toDraw = toDraw.offset(pos); - toDraw = toDraw.expand(expand, expand, expand).offset(-mc.getRenderManager().viewerPosX, -mc.getRenderManager().viewerPosY, -mc.getRenderManager().viewerPosZ); - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); - TESSELLATOR.draw(); - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); - TESSELLATOR.draw(); - BUFFER.begin(GL_LINES, DefaultVertexFormats.POSITION); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); - BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); - TESSELLATOR.draw(); + drawAABB(toDraw); }); if (Baritone.settings().renderSelectionBoxesIgnoreDepth.value) { @@ -259,6 +234,35 @@ public final class PathRenderer implements Helper { GlStateManager.disableBlend(); } + public static void drawAABB(AxisAlignedBB aabb) { + float expand = 0.002F; + AxisAlignedBB toDraw = aabb.expand(expand, expand, expand).offset(-mc.getRenderManager().viewerPosX, -mc.getRenderManager().viewerPosY, -mc.getRenderManager().viewerPosZ); + BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); + BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + TESSELLATOR.draw(); + BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); + BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + TESSELLATOR.draw(); + BUFFER.begin(GL_LINES, DefaultVertexFormats.POSITION); + BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.minZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.minY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.maxX, toDraw.maxY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.minY, toDraw.maxZ).endVertex(); + BUFFER.pos(toDraw.minX, toDraw.maxY, toDraw.maxZ).endVertex(); + TESSELLATOR.draw(); + } + public static void drawDankLitGoalBox(Entity player, Goal goal, float partialTicks, Color color) { double renderPosX = mc.getRenderManager().viewerPosX; double renderPosY = mc.getRenderManager().viewerPosY; @@ -345,7 +349,7 @@ public final class PathRenderer implements Helper { } GlStateManager.enableBlend(); - GlStateManager.blendFuncSeparate(770, 771, 1, 0); + GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO); GlStateManager.color4f(color.getColorComponents(null)[0], color.getColorComponents(null)[1], color.getColorComponents(null)[2], 0.6F); GlStateManager.lineWidth(Baritone.settings().goalRenderLineWidthPixels.value); GlStateManager.disableTexture2D(); diff --git a/src/main/java/baritone/utils/PathingCommandContext.java b/src/main/java/baritone/utils/PathingCommandContext.java new file mode 100644 index 000000000..1e8bfe6a4 --- /dev/null +++ b/src/main/java/baritone/utils/PathingCommandContext.java @@ -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 . + */ + +package baritone.utils; + +import baritone.api.pathing.goals.Goal; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.pathing.movement.CalculationContext; + +public class PathingCommandContext extends PathingCommand { + public final CalculationContext desiredCalcContext; + + public PathingCommandContext(Goal goal, PathingCommandType commandType, CalculationContext context) { + super(goal, commandType); + this.desiredCalcContext = context; + } +} diff --git a/src/main/java/baritone/utils/PathingControlManager.java b/src/main/java/baritone/utils/PathingControlManager.java index addcc7911..f6a6954d2 100644 --- a/src/main/java/baritone/utils/PathingControlManager.java +++ b/src/main/java/baritone/utils/PathingControlManager.java @@ -107,18 +107,18 @@ public class PathingControlManager implements IPathingControlManager { break; case FORCE_REVALIDATE_GOAL_AND_PATH: if (!p.isPathing() && !p.getInProgress().isPresent()) { - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); } break; case REVALIDATE_GOAL_AND_PATH: if (!p.isPathing() && !p.getInProgress().isPresent()) { - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); } break; case SET_GOAL_AND_PATH: // now this i can do if (command.goal != null) { - baritone.getPathingBehavior().secretInternalSetGoalAndPath(command.goal); + baritone.getPathingBehavior().secretInternalSetGoalAndPath(command); } break; default: @@ -141,13 +141,13 @@ public class PathingControlManager implements IPathingControlManager { // pwnage p.softCancelIfSafe(); } - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); break; case REVALIDATE_GOAL_AND_PATH: if (Baritone.settings().cancelOnGoalInvalidation.value && (command.goal == null || revalidateGoal(command.goal))) { p.softCancelIfSafe(); } - p.secretInternalSetGoalAndPath(command.goal); + p.secretInternalSetGoalAndPath(command); break; default: } diff --git a/src/main/java/baritone/utils/pathing/Favoring.java b/src/main/java/baritone/utils/pathing/Favoring.java index 45bb8a09e..8a6017582 100644 --- a/src/main/java/baritone/utils/pathing/Favoring.java +++ b/src/main/java/baritone/utils/pathing/Favoring.java @@ -17,27 +17,27 @@ package baritone.utils.pathing; -import baritone.Baritone; import baritone.api.pathing.calc.IPath; import baritone.api.utils.BetterBlockPos; import baritone.api.utils.IPlayerContext; +import baritone.pathing.movement.CalculationContext; import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap; public final class Favoring { private final Long2DoubleOpenHashMap favorings; - public Favoring(IPlayerContext ctx, IPath previous) { - this(previous); + public Favoring(IPlayerContext ctx, IPath previous, CalculationContext context) { + this(previous, context); for (Avoidance avoid : Avoidance.create(ctx)) { avoid.applySpherical(favorings); } System.out.println("Favoring size: " + favorings.size()); } - public Favoring(IPath previous) { // create one just from previous path, no mob avoidances + public Favoring(IPath previous, CalculationContext context) { // create one just from previous path, no mob avoidances favorings = new Long2DoubleOpenHashMap(); favorings.defaultReturnValue(1.0D); - double coeff = Baritone.settings().backtrackCostFavoringCoefficient.value; + double coeff = context.backtrackCostFavoringCoefficient; if (coeff != 1D && previous != null) { previous.positions().forEach(pos -> favorings.put(BetterBlockPos.longHash(pos), coeff)); } diff --git a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java index 489d560d8..757ea015d 100644 --- a/src/main/java/baritone/utils/pathing/SegmentedCalculator.java +++ b/src/main/java/baritone/utils/pathing/SegmentedCalculator.java @@ -87,7 +87,7 @@ public class SegmentedCalculator { private PathCalculationResult segment(Optional previous) { BetterBlockPos segmentStart = previous.map(IPath::getDest).orElse(start); // <-- e p i c - AbstractNodeCostSearch search = new AStarPathFinder(segmentStart.x, segmentStart.y, segmentStart.z, goal, new Favoring(previous.orElse(null)), context); // this is on another thread, so cannot include mob avoidances. + AbstractNodeCostSearch search = new AStarPathFinder(segmentStart.x, segmentStart.y, segmentStart.z, goal, new Favoring(previous.orElse(null), context), context); // this is on another thread, so cannot include mob avoidances. return search.calculate(Baritone.settings().primaryTimeoutMS.value, Baritone.settings().failureTimeoutMS.value); // use normal time settings, not the plan ahead settings, so as to not overwhelm the computer } diff --git a/src/main/java/baritone/utils/schematic/AirSchematic.java b/src/main/java/baritone/utils/schematic/AirSchematic.java new file mode 100644 index 000000000..fd253105e --- /dev/null +++ b/src/main/java/baritone/utils/schematic/AirSchematic.java @@ -0,0 +1,55 @@ +/* + * 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.utils.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; + +public class AirSchematic implements ISchematic { + + private final int widthX; + private final int heightY; + private final int lengthZ; + + public AirSchematic(int widthX, int heightY, int lengthZ) { + this.widthX = widthX; + this.heightY = heightY; + this.lengthZ = lengthZ; + } + + @Override + public IBlockState desiredState(int x, int y, int z) { + return Blocks.AIR.getDefaultState(); + } + + @Override + public int widthX() { + return widthX; + } + + @Override + public int heightY() { + return heightY; + } + + @Override + public int lengthZ() { + return lengthZ; + } +} diff --git a/src/main/java/baritone/utils/schematic/MapArtSchematic.java b/src/main/java/baritone/utils/schematic/MapArtSchematic.java new file mode 100644 index 000000000..1be131952 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/MapArtSchematic.java @@ -0,0 +1,66 @@ +/* + * 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.utils.schematic; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.nbt.NBTTagCompound; + +import java.util.OptionalInt; +import java.util.function.Predicate; + +public class MapArtSchematic extends Schematic { + + private final int[][] heightMap; + + public MapArtSchematic(NBTTagCompound schematic) { + super(schematic); + heightMap = new int[widthX][lengthZ]; + + for (int x = 0; x < widthX; x++) { + for (int z = 0; z < lengthZ; z++) { + IBlockState[] column = states[x][z]; + + OptionalInt lowestBlockY = lastIndexMatching(column, block -> block != Blocks.AIR); + if (lowestBlockY.isPresent()) { + heightMap[x][z] = lowestBlockY.getAsInt(); + } else { + System.out.println("Column " + x + "," + z + " has no blocks, but it's apparently map art? wtf"); + System.out.println("Letting it be whatever"); + heightMap[x][z] = 256; + } + + } + } + } + + private static OptionalInt lastIndexMatching(T[] arr, Predicate predicate) { + for (int y = arr.length - 1; y >= 0; y--) { + if (predicate.test(arr[y])) { + return OptionalInt.of(y); + } + } + return OptionalInt.empty(); + } + + @Override + public boolean inSchematic(int x, int y, int z) { + // in map art, we only care about coordinates in or above the art + return super.inSchematic(x, y, z) && y >= heightMap[x][z]; + } +} diff --git a/src/main/java/baritone/utils/schematic/Schematic.java b/src/main/java/baritone/utils/schematic/Schematic.java new file mode 100644 index 000000000..9e4b67cd5 --- /dev/null +++ b/src/main/java/baritone/utils/schematic/Schematic.java @@ -0,0 +1,89 @@ +/* + * 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.utils.schematic; + +import baritone.api.utils.ISchematic; +import net.minecraft.block.state.IBlockState; +import net.minecraft.nbt.NBTTagCompound; + +public class Schematic implements ISchematic { + public final int widthX; + public final int heightY; + public final int lengthZ; + protected final IBlockState[][][] states; + + public Schematic(NBTTagCompound schematic) { + /*String type = schematic.getString("Materials"); + if (!type.equals("Alpha")) { + throw new IllegalStateException("bad schematic " + type); + } + widthX = schematic.getInteger("Width"); + heightY = schematic.getInteger("Height"); + lengthZ = schematic.getInteger("Length"); + byte[] blocks = schematic.getByteArray("Blocks"); + byte[] metadata = schematic.getByteArray("Data"); + + byte[] additional = null; + if (schematic.hasKey("AddBlocks")) { + byte[] addBlocks = schematic.getByteArray("AddBlocks"); + additional = new byte[addBlocks.length * 2]; + for (int i = 0; i < addBlocks.length; i++) { + additional[i * 2 + 0] = (byte) ((addBlocks[i] >> 4) & 0xF); // lower nibble + additional[i * 2 + 1] = (byte) ((addBlocks[i] >> 0) & 0xF); // upper nibble + } + } + states = new IBlockState[widthX][lengthZ][heightY]; + for (int y = 0; y < heightY; y++) { + for (int z = 0; z < lengthZ; z++) { + for (int x = 0; x < widthX; x++) { + int blockInd = (y * lengthZ + z) * widthX + x; + + int blockID = blocks[blockInd] & 0xFF; + if (additional != null) { + // additional is 0 through 15 inclusive since it's & 0xF above + blockID |= additional[blockInd] << 8; + } + Block block = Block.REGISTRY.getObjectById(blockID); + int meta = metadata[blockInd] & 0xFF; + states[x][z][y] = block.getStateFromMeta(meta); + } + } + }*/ + throw new UnsupportedOperationException("1.13 be like: numeric IDs btfo"); + } + + @Override + public IBlockState desiredState(int x, int y, int z) { + return states[x][z][y]; + } + + @Override + public int widthX() { + return widthX; + } + + @Override + public int heightY() { + return heightY; + } + + @Override + public int lengthZ() { + return lengthZ; + } +}