Compare commits

...

21 Commits

Author SHA1 Message Date
Brady
6a6d0642be Utilize ToolSet for hotbar management's bestToolAgainst 2023-07-06 18:40:34 -07:00
Brady
dbf5058066 Move ItemInteractionHelper into its own file 2023-07-05 21:58:04 -07:00
Brady
bc18f0eabd Allow autoTool to pull tools from the inventory 2023-07-05 21:44:31 -07:00
Brady
827ce7e2a8 Modify ToolSet to accept an IPlayerContext 2023-07-05 19:39:08 -07:00
Brady
2e32c63bc2 Calculate break time based on block state 2023-07-05 18:52:53 -07:00
Brady
53651980ad Mitigate #3945 2023-07-05 11:45:04 -07:00
Brady
ec089cdeaa aha whoopsie 2023-07-05 11:24:34 -07:00
Brady
2408c9b6af Backport primitive computeIfAbsent 2023-07-05 11:14:55 -07:00
Brady
600d5e8a67 More IBaritoneInventory api methods 2023-07-05 10:13:38 -07:00
Brady
57c3e369b0 💚 appease codacy 2023-07-04 21:44:10 -07:00
Brady
827125d827 Inventory API thing and substitution of legacy code
Also stole `Pair` from elytra branch 😎
2023-07-04 21:34:57 -07:00
Brady
4d7eb003e6 Item interaction detection 2023-07-03 18:36:05 -05:00
Brady
1ef622c936 I LOVE ObjIntConsumer 2023-07-02 19:54:11 -05:00
Brady
c44701e689 Changes 2023-07-02 19:46:48 -05:00
Brady
7f27c9d85f Reorganize 2023-07-01 22:19:51 -05:00
Brady
15621ea763 Add allowHotbarManagement setting 2023-07-01 20:08:29 -05:00
Brady
4591fd7eb3 Use Reference2DoubleOpenHashMap in ToolSet 2023-07-01 17:16:51 -05:00
Brady
028405dd30 Only allow @PerformanceCritical on methods 2023-07-01 17:00:04 -05:00
Brady
2953b1cfaf Replace acceptableThrowawayItems references with isThrowawayItem method 2023-07-01 16:41:26 -05:00
Brady
b2cdd27ca2 Remove direct reference to allowInventory in BuilderProcess 2023-07-01 15:38:23 -05:00
Brady
4bf2dd6851 Some initial organization 2023-07-01 15:16:26 -05:00
15 changed files with 828 additions and 230 deletions

View File

@@ -73,6 +73,13 @@ public final class Settings {
*/ */
public final Setting<Boolean> allowInventory = new Setting<>(false); public final Setting<Boolean> allowInventory = new Setting<>(false);
/**
* Allow Baritone to automatically put useful items (such as tools and throwaway blocks) on the hotbar while
* pathing. This can reduce delays when retrieving items due settings like {@link #ticksBetweenInventoryMoves} and
* {@link #inventoryMoveOnlyIfStationary}. Requires {@link #allowInventory}.
*/
public final Setting<Boolean> allowHotbarManagement = new Setting<>(false);
/** /**
* Wait this many ticks between InventoryBehavior moving inventory items * Wait this many ticks between InventoryBehavior moving inventory items
*/ */

View File

@@ -0,0 +1,47 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.api.utils;
import net.minecraft.item.ItemStack;
import java.util.stream.Stream;
/**
* @author Brady
*/
public interface IBaritoneInventory {
/**
* Returns a stream containing all the player's regular inventory slots and items. The elements of the stream are in
* the order of hotbar, offhand, then main inventory, for a total of 37 slots. This explicitly does not contain the
* armor slots or crafting grid, which may otherwise be accessed with {@link #armorSlots()} and/or {@link #itemAt}.
*
* @return All the player's inventory slots and items
*/
Stream<Pair<InventorySlot, ItemStack>> allSlots();
Stream<Pair<InventorySlot, ItemStack>> hotbarSlots();
Stream<Pair<InventorySlot, ItemStack>> inventorySlots();
Pair<InventorySlot, ItemStack> offhand();
Stream<Pair<InventorySlot, ItemStack>> armorSlots();
ItemStack itemAt(InventorySlot slot);
}

View File

@@ -17,6 +17,7 @@
package baritone.api.utils; package baritone.api.utils;
import baritone.api.IBaritone;
import baritone.api.cache.IWorldData; import baritone.api.cache.IWorldData;
import net.minecraft.block.BlockSlab; import net.minecraft.block.BlockSlab;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -34,12 +35,16 @@ import java.util.Optional;
*/ */
public interface IPlayerContext { public interface IPlayerContext {
IBaritone baritone();
Minecraft minecraft(); Minecraft minecraft();
EntityPlayerSP player(); EntityPlayerSP player();
IPlayerController playerController(); IPlayerController playerController();
IBaritoneInventory inventory();
World world(); World world();
IWorldData worldData(); IWorldData worldData();

View File

@@ -0,0 +1,129 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.api.utils;
import java.util.function.ObjIntConsumer;
/**
* @author Brady
*/
public final class InventorySlot {
/**
* Maps directly to the slot ids of the player's inventory container.
* <table width="100%">
* <tr>
* <th width="10%">Index</th>
* <th>Description</th>
* </tr>
* <tr><td>0</td><td>crafting output</td></tr>
* <tr><td>1-4</td><td>crafting grid</td></tr>
* <tr><td>5-8</td><td>armor</td></tr>
* <tr><td>9-35</td><td>inventory (index 9-35)</td></tr>
* <tr><td>36-44</td><td>hotbar (index 0-8)</td></tr>
* <tr><td>45</td><td>off-hand</td></tr>
* </table>
*/
private static final InventorySlot[] SLOTS = new InventorySlot[46];
static {
final ObjIntConsumer<Type> populate = new ObjIntConsumer<Type>() {
private int index;
@Override
public void accept(Type type, int count) {
for (int i = 0; i < count; i++) {
SLOTS[this.index] = new InventorySlot(this.index, type);
this.index++;
}
}
};
populate.accept(Type.CRAFTING_OUTPUT, 1);
populate.accept(Type.CRAFTING_GRID, 4);
populate.accept(Type.ARMOR, 4);
populate.accept(Type.INVENTORY, 27);
populate.accept(Type.HOTBAR, 9);
populate.accept(Type.OFFHAND, 1);
}
private final int slotId;
private final Type type;
private InventorySlot(int slotId, Type type) {
this.slotId = slotId;
this.type = type;
}
/**
* @return The ID of this slot, as used by {@code ContainerPlayer}
*/
public int getSlotId() {
return this.slotId;
}
public Type getType() {
return this.type;
}
/**
* Returns the index of this slot in {@code mainInventory}. If this slot does not correspond to an index into
* {@code mainInventory}, then an {@link IllegalArgumentException} is thrown.
*
* @return The index of this slot in the player's {@code mainInventory}
* @throws IllegalArgumentException if type is not {@link Type#HOTBAR} or {@link Type#INVENTORY}
*/
public int getInventoryIndex() {
switch (this.getType()) {
case HOTBAR:
return this.slotId - 36;
case INVENTORY:
return this.slotId;
default:
throw new IllegalStateException("Slot type must be either HOTBAR or INVENTORY");
}
}
public static InventorySlot inventory(final int index) {
if (index >= 0 && index < 9) {
return SLOTS[index + 36]; // HOTBAR
} else if (index >= 9 && index < 36) {
return SLOTS[index]; // INVENTORY
}
throw new IllegalArgumentException();
}
public static InventorySlot armor(final int index) {
if (index < 0 || index >= 4) {
throw new IllegalArgumentException();
}
return SLOTS[index + 5];
}
public static InventorySlot offhand() {
return SLOTS[45];
}
public enum Type {
CRAFTING_OUTPUT,
CRAFTING_GRID,
ARMOR,
INVENTORY,
HOTBAR,
OFFHAND
}
}

View File

@@ -0,0 +1,59 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.api.utils;
import java.util.Objects;
/**
* @author Brady
*/
public final class Pair<A, B> {
private final A a;
private final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
public A first() {
return this.a;
}
public B second() {
return this.b;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || o.getClass() != Pair.class) {
return false;
}
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(this.a, pair.a) && Objects.equals(this.b, pair.b);
}
@Override
public int hashCode() {
return 31 * Objects.hashCode(this.a) + Objects.hashCode(this.b);
}
}

View File

@@ -0,0 +1,36 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone;
import baritone.pathing.movement.CalculationContext;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation that should be used on methods which are performance critical (i.e. called millions of times per second
* by the pathfinder) and should be modified with care. Particularly useful for methods for which this fact is not
* obvious, such as those which don't have a {@link CalculationContext} parameter.
*
* @author Brady
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface PerformanceCritical {}

View File

@@ -20,25 +20,29 @@ package baritone.behavior;
import baritone.Baritone; import baritone.Baritone;
import baritone.api.event.events.TickEvent; import baritone.api.event.events.TickEvent;
import baritone.api.utils.Helper; import baritone.api.utils.Helper;
import baritone.api.utils.InventorySlot;
import baritone.api.utils.Pair;
import baritone.utils.ItemInteractionHelper;
import baritone.utils.ToolSet; import baritone.utils.ToolSet;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
import net.minecraft.inventory.ClickType; import net.minecraft.inventory.ClickType;
import net.minecraft.item.*; import net.minecraft.item.*;
import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing;
import net.minecraft.util.NonNullList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.OptionalInt; import java.util.OptionalInt;
import java.util.Random; import java.util.Random;
import java.util.function.IntPredicate;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream;
public final class InventoryBehavior extends Behavior implements Helper { public final class InventoryBehavior extends Behavior implements Helper {
int ticksSinceLastInventoryMove; private int ticksSinceLastInventoryMove;
int[] lastTickRequestedMove; // not everything asks every tick, so remember the request while coming to a halt private int[] lastTickRequestedMove; // not everything asks every tick, so remember the request while coming to a halt
public InventoryBehavior(Baritone baritone) { public InventoryBehavior(Baritone baritone) {
super(baritone); super(baritone);
@@ -46,31 +50,51 @@ public final class InventoryBehavior extends Behavior implements Helper {
@Override @Override
public void onTick(TickEvent event) { public void onTick(TickEvent event) {
if (!Baritone.settings().allowInventory.value) { if (event.getType() == TickEvent.Type.OUT) {
return; return;
} }
if (event.getType() == TickEvent.Type.OUT) { ticksSinceLastInventoryMove++;
// TODO: Move these checks into "requestSwapWithHotBar" or whatever supersedes it
if (!this.canAccessInventory()) {
return; return;
} }
if (ctx.player().openContainer != ctx.player().inventoryContainer) { if (ctx.player().openContainer != ctx.player().inventoryContainer) {
// we have a crafting table or a chest or something open // we have a crafting table or a chest or something open
return; return;
} }
ticksSinceLastInventoryMove++;
if (firstValidThrowaway() >= 9) { // aka there are none on the hotbar, but there are some in main inventory if (Baritone.settings().allowHotbarManagement.value && baritone.getPathingBehavior().isPathing()) {
requestSwapWithHotBar(firstValidThrowaway(), 8); this.setupHotbar();
}
int pick = bestToolAgainst(Blocks.STONE, ItemPickaxe.class);
if (pick >= 9) {
requestSwapWithHotBar(pick, 0);
} }
if (lastTickRequestedMove != null) { if (lastTickRequestedMove != null) {
logDebug("Remembering to move " + lastTickRequestedMove[0] + " " + lastTickRequestedMove[1] + " from a previous tick"); logDebug("Remembering to move " + lastTickRequestedMove[0] + " " + lastTickRequestedMove[1] + " from a previous tick");
requestSwapWithHotBar(lastTickRequestedMove[0], lastTickRequestedMove[1]); requestSwapWithHotBar(lastTickRequestedMove[0], lastTickRequestedMove[1]);
} }
} }
public boolean attemptToPutOnHotbar(int inMainInvy, Predicate<Integer> disallowedHotbar) { private void setupHotbar() {
// TODO: Some way of indicating which slots are currently reserved by this setting
final InventorySlot throwaway = this.findSlotMatching(this::isThrowawayItem);
if (throwaway != null && throwaway.getType() == InventorySlot.Type.INVENTORY) {
// aka there are none on the hotbar, but there are some in main inventory
this.requestSwapWithHotBar(throwaway.getInventoryIndex(), 8);
return;
}
final InventorySlot pick = this.bestToolAgainst(Blocks.STONE, ItemPickaxe.class);
if (pick != null && pick.getType() == InventorySlot.Type.INVENTORY) {
this.requestSwapWithHotBar(pick.getInventoryIndex(), 0);
}
}
private InventorySlot bestToolAgainst(final Block against, final Class<? extends ItemTool> cla$$) {
return new ToolSet(ctx).getBestSlot(against.getDefaultState(), Baritone.settings().preferSilkTouch.value, stack -> cla$$.isInstance(stack.getItem()));
}
public boolean attemptToPutOnHotbar(int inMainInvy, IntPredicate disallowedHotbar) {
OptionalInt destination = getTempHotbarSlot(disallowedHotbar); OptionalInt destination = getTempHotbarSlot(disallowedHotbar);
if (destination.isPresent()) { if (destination.isPresent()) {
if (!requestSwapWithHotBar(inMainInvy, destination.getAsInt())) { if (!requestSwapWithHotBar(inMainInvy, destination.getAsInt())) {
@@ -80,7 +104,7 @@ public final class InventoryBehavior extends Behavior implements Helper {
return true; return true;
} }
public OptionalInt getTempHotbarSlot(Predicate<Integer> disallowedHotbar) { public OptionalInt getTempHotbarSlot(IntPredicate disallowedHotbar) {
// we're using 0 and 8 for pickaxe and throwaway // we're using 0 and 8 for pickaxe and throwaway
ArrayList<Integer> candidates = new ArrayList<>(); ArrayList<Integer> candidates = new ArrayList<>();
for (int i = 1; i < 8; i++) { for (int i = 1; i < 8; i++) {
@@ -117,114 +141,172 @@ public final class InventoryBehavior extends Behavior implements Helper {
return true; return true;
} }
private int firstValidThrowaway() { // TODO offhand idk
NonNullList<ItemStack> invy = ctx.player().inventory.mainInventory;
for (int i = 0; i < invy.size(); i++) {
if (Baritone.settings().acceptableThrowawayItems.value.contains(invy.get(i).getItem())) {
return i;
}
}
return -1;
}
private int bestToolAgainst(Block against, Class<? extends ItemTool> cla$$) {
NonNullList<ItemStack> invy = ctx.player().inventory.mainInventory;
int bestInd = -1;
double bestSpeed = -1;
for (int i = 0; i < invy.size(); i++) {
ItemStack stack = invy.get(i);
if (stack.isEmpty()) {
continue;
}
if (Baritone.settings().itemSaver.value && (stack.getItemDamage() + Baritone.settings().itemSaverThreshold.value) >= stack.getMaxDamage() && stack.getMaxDamage() > 1) {
continue;
}
if (cla$$.isInstance(stack.getItem())) {
double speed = ToolSet.calculateSpeedVsBlock(stack, against.getDefaultState()); // takes into account enchants
if (speed > bestSpeed) {
bestSpeed = speed;
bestInd = i;
}
}
}
return bestInd;
}
public boolean hasGenericThrowaway() { public boolean hasGenericThrowaway() {
for (Item item : Baritone.settings().acceptableThrowawayItems.value) { return this.canSelectItem(this::isThrowawayItem);
if (throwaway(false, stack -> item.equals(stack.getItem()))) {
return true;
}
}
return false;
} }
public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) { public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) {
IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z)); final Predicate<Predicate<? super ItemStack>> op = select ? this::trySelectItem : this::canSelectItem;
if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && maybe.equals(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player())))) {
return true; // gotem final IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z));
if (maybe != null) {
return op.test(stack -> {
if (!(stack.getItem() instanceof ItemBlock)) {
return false;
}
Block block = ((ItemBlock) stack.getItem()).getBlock();
return maybe.equals(block.getStateForPlacement(
ctx.world(),
ctx.playerFeet(),
EnumFacing.UP,
0.5f, 1.0f, 0.5f,
stack.getItem().getMetadata(stack.getMetadata()),
ctx.player()
));
}) || op.test(stack -> {
// Since a stack didn't match the desired block state, accept a match of just the block
return stack.getItem() instanceof ItemBlock
&& ((ItemBlock) stack.getItem()).getBlock().equals(maybe.getBlock());
});
} }
if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && ((ItemBlock) stack.getItem()).getBlock().equals(maybe.getBlock()))) { return op.test(this::isThrowawayItem);
}
public boolean canSelectItem(Predicate<? super ItemStack> desired) {
return this.resolveSelectionStrategy(this.findSlotMatching(desired)) != null;
}
public boolean trySelectItem(Predicate<? super ItemStack> desired) {
final SelectionStrategy strategy = this.resolveSelectionStrategy(this.findSlotMatching(desired));
if (strategy != null) {
strategy.run();
// TODO: Consider cases where returning the SelectionType is needed/useful to the caller
return true; return true;
} }
for (Item item : Baritone.settings().acceptableThrowawayItems.value) {
if (throwaway(select, stack -> item.equals(stack.getItem()))) {
return true;
}
}
return false; return false;
} }
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired) { public SelectionStrategy resolveSelectionStrategy(final InventorySlot slot) {
return throwaway(select, desired, Baritone.settings().allowInventory.value); if (slot != null) {
switch (slot.getType()) {
case HOTBAR:
return SelectionStrategy.of(SelectionType.IMMEDIATE, () ->
ctx.player().inventory.currentItem = slot.getInventoryIndex());
case OFFHAND:
// TODO: It's probably worth adding a parameter to this method so that the caller can indicate what
// the purpose of the item is. For example, attacks are always done using the main hand, so
// just switching to a non-interacting hotbar item isn't sufficient.
final ItemStack heldItem = ctx.player().inventory.getCurrentItem();
if (!ItemInteractionHelper.couldInteract(heldItem)) {
// Don't need to do anything, the item held in the main hand doesn't have any interaction.
return SelectionStrategy.of(SelectionType.IMMEDIATE, () -> {});
}
final InventorySlot hotbar = this.findHotbarMatching(item -> !ItemInteractionHelper.couldInteract(item));
if (hotbar != null) {
return SelectionStrategy.of(SelectionType.IMMEDIATE, () ->
ctx.player().inventory.currentItem = hotbar.getInventoryIndex());
}
// TODO: Swap offhand with an unimportant item
break;
case INVENTORY:
if (this.canAccessInventory()) {
return SelectionStrategy.of(SelectionType.ENQUEUED, () -> {
// TODO: Determine if hotbar swap can be immediate, and return type accordingly
// Also don't only swap into slot 7 that's silly
requestSwapWithHotBar(slot.getInventoryIndex(), 7);
ctx.player().inventory.currentItem = 7;
});
}
break;
default:
break;
}
}
return null;
} }
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired, boolean allowInventory) { public InventorySlot findBestAccessibleMatching(final Comparator<? super ItemStack> comparator,
EntityPlayerSP p = ctx.player(); final Predicate<? super ItemStack> filter) {
NonNullList<ItemStack> inv = p.inventory.mainInventory; final Stream<Pair<InventorySlot, ItemStack>> accessible = this.canAccessInventory()
for (int i = 0; i < 9; i++) { ? ctx.inventory().allSlots()
ItemStack item = inv.get(i); : Stream.concat(ctx.inventory().hotbarSlots(), Stream.of(ctx.inventory().offhand()));
// this usage of settings() is okay because it's only called once during pathing return this.findBestMatching0(accessible, comparator, filter);
// (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)) {
if (select) {
p.inventory.currentItem = i;
}
return true;
}
}
if (desired.test(p.inventory.offHandInventory.get(0))) {
// 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 (int 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;
}
}
}
if (allowInventory) { public InventorySlot findSlotMatching(final Predicate<? super ItemStack> filter) {
for (int i = 9; i < 36; i++) { return this.findBestSlotMatching(null, filter);
if (desired.test(inv.get(i))) { }
if (select) {
requestSwapWithHotBar(i, 7);
p.inventory.currentItem = 7;
}
return true;
}
}
}
return false; /**
* Returns an {@link InventorySlot} that contains a stack matching the given predicate. A comparator may be
* specified to prioritize slot selection. The comparator may be {@code null}, in which case, the first slot
* matching the predicate is returned. The considered slots are in the order of hotbar, offhand, and finally the
* main inventory (if {@link #canAccessInventory()} is {@code true}).
*
* @param comparator A comparator to find the best element, may be {@code null}
* @param filter The predicate to match
* @return A matching slot, or {@code null} if none.
*/
public InventorySlot findBestSlotMatching(final Comparator<? super ItemStack> comparator, final Predicate<? super ItemStack> filter) {
return this.findBestMatching0(ctx.inventory().allSlots(), comparator, filter);
}
public InventorySlot findHotbarMatching(final Predicate<? super ItemStack> filter) {
return this.findBestHotbarMatching(null, filter);
}
public InventorySlot findBestHotbarMatching(final Comparator<? super ItemStack> comparator, final Predicate<? super ItemStack> filter) {
return this.findBestMatching0(ctx.inventory().hotbarSlots(), comparator, filter);
}
private InventorySlot findBestMatching0(final Stream<Pair<InventorySlot, ItemStack>> slots,
final Comparator<? super ItemStack> comparator,
final Predicate<? super ItemStack> filter) {
final Stream<Pair<InventorySlot, ItemStack>> filtered = slots.filter(slot -> filter.test(slot.second()));
return (comparator != null
? filtered.max((a, b) -> comparator.compare(a.second(), b.second()))
: filtered.findFirst()
).map(Pair::first).orElse(null);
}
public boolean canAccessInventory() {
return Baritone.settings().allowInventory.value;
}
public boolean isThrowawayItem(ItemStack stack) {
return this.isThrowawayItem(stack.getItem());
}
public boolean isThrowawayItem(Item item) {
return Baritone.settings().acceptableThrowawayItems.value.contains(item);
}
public interface SelectionStrategy extends Runnable {
@Override
void run();
SelectionType getType();
static SelectionStrategy of(final SelectionType type, final Runnable runnable) {
return new SelectionStrategy() {
@Override
public void run() {
runnable.run();
}
@Override
public SelectionType getType() {
return type;
}
};
}
}
public enum SelectionType {
IMMEDIATE, ENQUEUED
} }
} }

View File

@@ -93,7 +93,7 @@ public class CalculationContext {
this.world = baritone.getPlayerContext().world(); this.world = baritone.getPlayerContext().world();
this.worldData = (WorldData) baritone.getPlayerContext().worldData(); this.worldData = (WorldData) baritone.getPlayerContext().worldData();
this.bsi = new BlockStateInterface(baritone.getPlayerContext(), forUseOnAnotherThread); this.bsi = new BlockStateInterface(baritone.getPlayerContext(), forUseOnAnotherThread);
this.toolSet = new ToolSet(player); this.toolSet = new ToolSet(baritone.getPlayerContext());
this.hasThrowaway = Baritone.settings().allowPlace.value && ((Baritone) baritone).getInventoryBehavior().hasGenericThrowaway(); 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.provider.isNether(); this.hasWaterBucket = Baritone.settings().allowWaterBucketFall.value && InventoryPlayer.isHotbar(player.inventory.getSlotFor(STACK_BUCKET_WATER)) && !world.provider.isNether();
this.canSprint = Baritone.settings().allowSprint.value && player.getFoodStats().getFoodLevel() > 6; this.canSprint = Baritone.settings().allowSprint.value && player.getFoodStats().getFoodLevel() > 6;

View File

@@ -18,12 +18,12 @@
package baritone.pathing.movement; package baritone.pathing.movement;
import baritone.Baritone; import baritone.Baritone;
import baritone.api.BaritoneAPI;
import baritone.api.IBaritone; import baritone.api.IBaritone;
import baritone.api.pathing.movement.ActionCosts; import baritone.api.pathing.movement.ActionCosts;
import baritone.api.pathing.movement.MovementStatus; import baritone.api.pathing.movement.MovementStatus;
import baritone.api.utils.*; import baritone.api.utils.*;
import baritone.api.utils.input.Input; import baritone.api.utils.input.Input;
import baritone.behavior.InventoryBehavior;
import baritone.pathing.movement.MovementState.MovementTarget; import baritone.pathing.movement.MovementState.MovementTarget;
import baritone.pathing.precompute.Ternary; import baritone.pathing.precompute.Ternary;
import baritone.utils.BlockStateInterface; import baritone.utils.BlockStateInterface;
@@ -577,23 +577,17 @@ public interface MovementHelper extends ActionCosts, Helper {
/** /**
* AutoTool for a specific block * AutoTool for a specific block
* *
* @param ctx The player context * @param ctx The player context
* @param b the blockstate to mine * @param state The blockstate to mine
*/ */
static void switchToBestToolFor(IPlayerContext ctx, IBlockState b) { static void switchToBestToolFor(IPlayerContext ctx, IBlockState state) {
switchToBestToolFor(ctx, b, new ToolSet(ctx.player()), BaritoneAPI.getSettings().preferSilkTouch.value); if (ToolSet.isAutoTool()) {
} // TODO: Submit through InventoryBehavior, instead of executing the strategy here
final InventorySlot slot = new ToolSet(ctx).getBestSlot(state, Baritone.settings().preferSilkTouch.value, null);
/** final InventoryBehavior.SelectionStrategy strategy = ((Baritone) ctx.baritone()).getInventoryBehavior().resolveSelectionStrategy(slot);
* AutoTool for a specific block with precomputed ToolSet data if (strategy != null) {
* strategy.run();
* @param ctx The player context }
* @param b the blockstate to mine
* @param ts previously calculated ToolSet
*/
static void switchToBestToolFor(IPlayerContext ctx, IBlockState b, ToolSet ts, boolean preferSilkTouch) {
if (Baritone.settings().autoTool.value && !Baritone.settings().assumeExternalAutoTool.value) {
ctx.player().inventory.currentItem = ts.getBestSlot(b.getBlock(), preferSilkTouch);
} }
} }

View File

@@ -542,7 +542,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil
return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL); return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL);
} }
if (Baritone.settings().allowInventory.value) { if (baritone.getInventoryBehavior().canAccessInventory()) {
ArrayList<Integer> usefulSlots = new ArrayList<>(); ArrayList<Integer> usefulSlots = new ArrayList<>();
List<IBlockState> noValidHotbarOption = new ArrayList<>(); List<IBlockState> noValidHotbarOption = new ArrayList<>();
outer: outer:

View File

@@ -269,7 +269,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
for (BlockPos pos : both) { for (BlockPos pos : both) {
boolean soulsand = openSoulsand.contains(pos); boolean soulsand = openSoulsand.contains(pos);
Optional<Rotation> rot = RotationUtils.reachableOffset(ctx, pos, new Vec3d(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance(), false); Optional<Rotation> rot = RotationUtils.reachableOffset(ctx, pos, new Vec3d(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5), ctx.playerController().getBlockReachDistance(), false);
if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, soulsand ? this::isNetherWart : this::isPlantable)) { if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().trySelectItem(soulsand ? this::isNetherWart : this::isPlantable)) {
RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance()); RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance());
if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == EnumFacing.UP) { if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == EnumFacing.UP) {
baritone.getLookBehavior().updateTarget(rot.get(), true); baritone.getLookBehavior().updateTarget(rot.get(), true);
@@ -287,7 +287,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
} }
Vec3d faceCenter = new Vec3d(pos).add(0.5, 0.5, 0.5).add(new Vec3d(dir.getDirectionVec()).scale(0.5)); Vec3d faceCenter = new Vec3d(pos).add(0.5, 0.5, 0.5).add(new Vec3d(dir.getDirectionVec()).scale(0.5));
Optional<Rotation> rot = RotationUtils.reachableOffset(ctx, pos, faceCenter, ctx.playerController().getBlockReachDistance(), false); Optional<Rotation> rot = RotationUtils.reachableOffset(ctx, pos, faceCenter, ctx.playerController().getBlockReachDistance(), false);
if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isCocoa)) { if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().trySelectItem(this::isCocoa)) {
RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance()); RayTraceResult result = RayTraceUtils.rayTraceTowards(ctx.player(), rot.get(), ctx.playerController().getBlockReachDistance());
if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == dir) { if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == dir) {
baritone.getLookBehavior().updateTarget(rot.get(), true); baritone.getLookBehavior().updateTarget(rot.get(), true);
@@ -301,7 +301,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
} }
for (BlockPos pos : bonemealable) { for (BlockPos pos : bonemealable) {
Optional<Rotation> rot = RotationUtils.reachable(ctx, pos); Optional<Rotation> rot = RotationUtils.reachable(ctx, pos);
if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().throwaway(true, this::isBoneMeal)) { if (rot.isPresent() && isSafeToCancel && baritone.getInventoryBehavior().trySelectItem(this::isBoneMeal)) {
baritone.getLookBehavior().updateTarget(rot.get(), true); baritone.getLookBehavior().updateTarget(rot.get(), true);
if (ctx.isLookingAt(pos)) { if (ctx.isLookingAt(pos)) {
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true); baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true);
@@ -323,17 +323,17 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
for (BlockPos pos : toBreak) { for (BlockPos pos : toBreak) {
goalz.add(new BuilderProcess.GoalBreak(pos)); goalz.add(new BuilderProcess.GoalBreak(pos));
} }
if (baritone.getInventoryBehavior().throwaway(false, this::isPlantable)) { if (baritone.getInventoryBehavior().canSelectItem(this::isPlantable)) {
for (BlockPos pos : openFarmland) { for (BlockPos pos : openFarmland) {
goalz.add(new GoalBlock(pos.up())); goalz.add(new GoalBlock(pos.up()));
} }
} }
if (baritone.getInventoryBehavior().throwaway(false, this::isNetherWart)) { if (baritone.getInventoryBehavior().canSelectItem(this::isNetherWart)) {
for (BlockPos pos : openSoulsand) { for (BlockPos pos : openSoulsand) {
goalz.add(new GoalBlock(pos.up())); goalz.add(new GoalBlock(pos.up()));
} }
} }
if (baritone.getInventoryBehavior().throwaway(false, this::isCocoa)) { if (baritone.getInventoryBehavior().canSelectItem(this::isCocoa)) {
for (BlockPos pos : openLog) { for (BlockPos pos : openLog) {
for (EnumFacing direction : EnumFacing.Plane.HORIZONTAL) { for (EnumFacing direction : EnumFacing.Plane.HORIZONTAL) {
if (ctx.world().getBlockState(pos.offset(direction)).getBlock() instanceof BlockAir) { if (ctx.world().getBlockState(pos.offset(direction)).getBlock() instanceof BlockAir) {
@@ -342,7 +342,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
} }
} }
} }
if (baritone.getInventoryBehavior().throwaway(false, this::isBoneMeal)) { if (baritone.getInventoryBehavior().canSelectItem(this::isBoneMeal)) {
for (BlockPos pos : bonemealable) { for (BlockPos pos : bonemealable) {
goalz.add(new GoalBlock(pos)); goalz.add(new GoalBlock(pos));
} }

View File

@@ -0,0 +1,100 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.utils;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.lang.reflect.Method;
/**
* @author Brady
*/
public final class ItemInteractionHelper {
private ItemInteractionHelper() {}
private static final Reference2BooleanMap<Class<? extends Item>> CACHE = new Reference2BooleanOpenHashMap<>();
public static boolean couldInteract(final ItemStack stack) {
if (stack.isEmpty()) {
return false;
}
return CACHE.computeIfAbsent(stack.getItem().getClass(), itemClass -> {
try {
final Method onItemUse = itemClass.getMethod(Helper1.name, Helper1.parameters);
final Method onItemRightClick = itemClass.getMethod(Helper2.name, Helper2.parameters);
// If the declaring class isn't Item, then the method is overridden
return onItemUse.getDeclaringClass() != Item.class
|| onItemRightClick.getDeclaringClass() != Item.class;
} catch (NoSuchMethodException ignored) {
// this shouldn't happen
return true;
}
});
}
private static final class Helper1 extends Item {
public static final String name;
public static final Class<?>[] parameters;
static {
final Method method = Helper1.class.getDeclaredMethods()[0];
name = method.getName();
parameters = method.getParameterTypes();
}
@Nonnull
@Override
public EnumActionResult onItemUse(@Nonnull EntityPlayer player, @Nonnull World worldIn,
@Nonnull BlockPos pos, @Nonnull EnumHand hand,
@Nonnull EnumFacing facing, float hitX, float hitY, float hitZ) {
return super.onItemUse(player, worldIn, pos, hand, facing, hitX, hitY, hitZ);
}
}
private static final class Helper2 extends Item {
public static final String name;
public static final Class<?>[] parameters;
static {
final Method method = Helper2.class.getDeclaredMethods()[0];
name = method.getName();
parameters = method.getParameterTypes();
}
@Nonnull
@Override
public ActionResult<ItemStack> onItemRightClick(@Nonnull World worldIn, @Nonnull EntityPlayer playerIn,
@Nonnull EnumHand handIn) {
return super.onItemRightClick(worldIn, playerIn, handIn);
}
}
}

View File

@@ -18,62 +18,78 @@
package baritone.utils; package baritone.utils;
import baritone.Baritone; import baritone.Baritone;
import baritone.PerformanceCritical;
import baritone.api.utils.IPlayerContext;
import baritone.api.utils.InventorySlot;
import baritone.utils.accessor.IItemTool; import baritone.utils.accessor.IItemTool;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.objects.Reference2DoubleOpenHashMap;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.init.Enchantments; import net.minecraft.init.Enchantments;
import net.minecraft.init.MobEffects; import net.minecraft.init.MobEffects;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemSword; import net.minecraft.item.ItemSword;
import net.minecraft.item.ItemTool;
import java.util.HashMap; import java.util.Comparator;
import java.util.Map; import java.util.function.Predicate;
import java.util.function.Function; import java.util.function.ToDoubleFunction;
/** /**
* A cached list of the best tools on the hotbar for any block * A cached list of the best tools on the hotbar for any block
* *
* @author Avery, Brady, leijurv * @author Avery, Brady, leijurv
*/ */
public class ToolSet { public final class ToolSet {
/** /**
* A cache mapping a {@link Block} to how long it will take to break * A cache mapping a {@link Block} to how long it will take to break
* with this toolset, given the optimum tool is used. * with this toolset, given the optimum tool is used.
*/ */
private final Map<Block, Double> breakStrengthCache; private final Cache breakStrengthCache;
/** /**
* My buddy leijurv owned me so we have this to not create a new lambda instance. * My buddy leijurv owned me so we have this to not create a new lambda instance.
*/ */
private final Function<Block, Double> backendCalculation; private final ToDoubleFunction<IBlockState> backendCalculation;
private final EntityPlayerSP player; private final IPlayerContext ctx;
public ToolSet(EntityPlayerSP player) { public ToolSet(IPlayerContext ctx) {
breakStrengthCache = new HashMap<>(); this.ctx = ctx;
this.player = player; this.breakStrengthCache = new Cache();
if (Baritone.settings().considerPotionEffects.value) { if (Baritone.settings().considerPotionEffects.value) {
double amplifier = potionAmplifier(); double amplifier = this.potionAmplifier();
Function<Double, Double> amplify = x -> amplifier * x; this.backendCalculation = block -> amplifier * this.getBestDestructionSpeed(block);
backendCalculation = amplify.compose(this::getBestDestructionTime);
} else { } else {
backendCalculation = this::getBestDestructionTime; this.backendCalculation = this::getBestDestructionSpeed;
} }
} }
/** /**
* Using the best tool on the hotbar, how fast we can mine this block * Using the best tool on the hotbar, how fast we can mine this block
* *
* @param state the blockstate to be mined * @param state the state to be mined
* @return the speed of how fast we'll mine it. 1/(time in ticks) * @return the speed of how fast we'll mine it. 1/(time in ticks)
*/ */
@PerformanceCritical
public double getStrVsBlock(IBlockState state) { public double getStrVsBlock(IBlockState state) {
return breakStrengthCache.computeIfAbsent(state.getBlock(), backendCalculation); return this.breakStrengthCache.computeIfAbsent(state, this.backendCalculation);
}
/**
* Calculate how effectively a block can be destroyed
*
* @param state the block state to be mined
* @return A double containing the destruction speed with the best tool
*/
private double getBestDestructionSpeed(IBlockState state) {
final ItemStack stack = isAutoTool()
? ctx.inventory().itemAt(this.getBestSlot(state, false, null))
: ctx.player().getHeldItemMainhand();
return calculateSpeedVsBlock(stack, state) * avoidanceMultiplier(state.getBlock());
} }
/** /**
@@ -81,92 +97,55 @@ public class ToolSet {
* harvest level order; there is a chance for multiple at the same with modded tools * harvest level order; there is a chance for multiple at the same with modded tools
* but in that case we don't really care. * but in that case we don't really care.
* *
* @param itemStack a possibly empty ItemStack * @param stack a possibly empty ItemStack
* @return values from 0 up * @return The tool's harvest level, or {@code -1} if the stack isn't a tool
*/ */
private int getMaterialCost(ItemStack itemStack) { private static int getMaterialCost(ItemStack stack) {
if (itemStack.getItem() instanceof ItemTool) { if (stack.getItem() instanceof IItemTool) {
ItemTool tool = (ItemTool) itemStack.getItem(); return ((IItemTool) stack.getItem()).getHarvestLevel();
return ((IItemTool) tool).getHarvestLevel();
} else { } else {
return -1; return -1;
} }
} }
public boolean hasSilkTouch(ItemStack stack) {
return EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, stack) > 0;
}
/** /**
* Calculate which tool on the hotbar is best for mining, depending on an override setting, * Calculate which tool on the hotbar is best for mining, depending on an override setting,
* related to auto tool movement cost, it will either return current selected slot, or the best slot. * related to auto tool movement cost, it will either return current selected slot, or the best slot.
* *
* @param b the blockstate to be mined * @param state the blockstate to be mined
* @param preferSilkTouch whether to prefer silk touch tools
* @param extra An additional filter to apply on top of the default, setting-based ones, may be {@code null}
* @return An int containing the index in the tools array that worked best * @return An int containing the index in the tools array that worked best
*/ */
public InventorySlot getBestSlot(final IBlockState state, final boolean preferSilkTouch,
final Predicate<? super ItemStack> extra) {
final Comparator<ItemStack> compare = Comparator
// Prioritize mining speed over everything
.<ItemStack>comparingDouble(stack -> calculateSpeedVsBlock(stack, state))
// Prioritize silk touch tools, if preferSilkTouch is true, over reduced material cost
.thenComparing(ToolSet::hasSilkTouch, (a, b) -> preferSilkTouch ? Boolean.compare(a, b) : 0)
// Minimize material cost
.thenComparing(Comparator.comparingInt(ToolSet::getMaterialCost).reversed());
public int getBestSlot(Block b, boolean preferSilkTouch) { return ((Baritone) ctx.baritone()).getInventoryBehavior().findBestAccessibleMatching(
return getBestSlot(b, preferSilkTouch, false); compare,
} stack -> {
if (!Baritone.settings().useSwordToMine.value && stack.getItem() instanceof ItemSword) {
public int getBestSlot(Block b, boolean preferSilkTouch, boolean pathingCalculation) { return false;
}
/* if (Baritone.settings().itemSaver.value
If we actually want know what efficiency our held item has instead of the best one && stack.getItemDamage() + Baritone.settings().itemSaverThreshold.value >= stack.getMaxDamage()
possible, this lets us make pathing depend on the actual tool to be used (if auto tool is disabled) && stack.getMaxDamage() > 1
*/ ) {
if (!Baritone.settings().autoTool.value && pathingCalculation) { return false;
return player.inventory.currentItem; }
} return extra == null || extra.test(stack);
int best = 0;
double highestSpeed = Double.NEGATIVE_INFINITY;
int lowestCost = Integer.MIN_VALUE;
boolean bestSilkTouch = false;
IBlockState blockState = b.getDefaultState();
for (int i = 0; i < 9; i++) {
ItemStack itemStack = player.inventory.getStackInSlot(i);
if (!Baritone.settings().useSwordToMine.value && itemStack.getItem() instanceof ItemSword) {
continue;
}
if (Baritone.settings().itemSaver.value && (itemStack.getItemDamage() + Baritone.settings().itemSaverThreshold.value) >= itemStack.getMaxDamage() && itemStack.getMaxDamage() > 1) {
continue;
}
double speed = calculateSpeedVsBlock(itemStack, blockState);
boolean silkTouch = hasSilkTouch(itemStack);
if (speed > highestSpeed) {
highestSpeed = speed;
best = i;
lowestCost = getMaterialCost(itemStack);
bestSilkTouch = silkTouch;
} else if (speed == highestSpeed) {
int cost = getMaterialCost(itemStack);
if ((cost < lowestCost && (silkTouch || !bestSilkTouch)) ||
(preferSilkTouch && !bestSilkTouch && silkTouch)) {
highestSpeed = speed;
best = i;
lowestCost = cost;
bestSilkTouch = silkTouch;
} }
} );
}
return best;
} }
/** public static boolean isAutoTool() {
* Calculate how effectively a block can be destroyed return Baritone.settings().autoTool.value && !Baritone.settings().assumeExternalAutoTool.value;
*
* @param b the blockstate to be mined
* @return A double containing the destruction ticks with the best tool
*/
private double getBestDestructionTime(Block b) {
ItemStack stack = player.inventory.getStackInSlot(getBestSlot(b, false, true));
return calculateSpeedVsBlock(stack, b.getDefaultState()) * avoidanceMultiplier(b);
}
private double avoidanceMultiplier(Block b) {
return Baritone.settings().blocksToAvoidBreaking.value.contains(b) ? Baritone.settings().avoidBreakingMultiplier.value : 1;
} }
/** /**
@@ -175,10 +154,19 @@ public class ToolSet {
* *
* @param item the item to mine it with * @param item the item to mine it with
* @param state the blockstate to be mined * @param state the blockstate to be mined
* @return how long it would take in ticks * @return the speed of how fast we'll mine it. 1/(time in ticks)
*/ */
public static double calculateSpeedVsBlock(ItemStack item, IBlockState state) { public static double calculateSpeedVsBlock(ItemStack item, IBlockState state) {
float hardness = state.getBlockHardness(null, null); float hardness;
try {
// noinspection DataFlowIssue
hardness = state.getBlockHardness(null, null);
} catch (NullPointerException ignored) {
// Just catch the exception and act as if the block is unbreakable. Even in situations where we could
// reasonably determine the hardness by passing the correct world/position (not via 'getStrVsBlock' during
// performance critical cost calculation), it's not worth it for the sake of consistency.
return -1;
}
if (hardness < 0) { if (hardness < 0) {
return -1; return -1;
} }
@@ -199,6 +187,15 @@ public class ToolSet {
} }
} }
private static double avoidanceMultiplier(Block block) {
return Baritone.settings().blocksToAvoidBreaking.value.contains(block)
? Baritone.settings().avoidBreakingMultiplier.value : 1;
}
private static boolean hasSilkTouch(ItemStack stack) {
return EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, stack) > 0;
}
/** /**
* Calculates any modifier to breaking time based on status effects. * Calculates any modifier to breaking time based on status effects.
* *
@@ -206,11 +203,11 @@ public class ToolSet {
*/ */
private double potionAmplifier() { private double potionAmplifier() {
double speed = 1; double speed = 1;
if (player.isPotionActive(MobEffects.HASTE)) { if (ctx.player().isPotionActive(MobEffects.HASTE)) {
speed *= 1 + (player.getActivePotionEffect(MobEffects.HASTE).getAmplifier() + 1) * 0.2; speed *= 1 + (ctx.player().getActivePotionEffect(MobEffects.HASTE).getAmplifier() + 1) * 0.2;
} }
if (player.isPotionActive(MobEffects.MINING_FATIGUE)) { if (ctx.player().isPotionActive(MobEffects.MINING_FATIGUE)) {
switch (player.getActivePotionEffect(MobEffects.MINING_FATIGUE).getAmplifier()) { switch (ctx.player().getActivePotionEffect(MobEffects.MINING_FATIGUE).getAmplifier()) {
case 0: case 0:
speed *= 0.3; speed *= 0.3;
break; break;
@@ -227,4 +224,60 @@ public class ToolSet {
} }
return speed; return speed;
} }
/*
* Copyright (C) 2002-2022 Sebastiano Vigna
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
private static final class Cache extends Reference2DoubleOpenHashMap<IBlockState> {
public double computeIfAbsent(final IBlockState key, final ToDoubleFunction<IBlockState> mappingFunction) {
int pos = this.find(key);
if (pos >= 0) {
return this.value[pos];
} else {
double newValue = mappingFunction.applyAsDouble(key);
this.insert(-pos - 1, key, newValue);
return newValue;
}
}
private int find(final IBlockState k) {
if (((k) == (null))) return containsNullKey ? n : -(n + 1);
Object curr;
final Object[] key = this.key;
int pos;
// The starting point.
if (((curr = key[pos = (HashCommon.mix(System.identityHashCode(k))) & mask]) == (null))) return -(pos + 1);
if (((k) == (curr))) return pos;
// There's always an unused entry.
while (true) {
if (((curr = key[pos = (pos + 1) & mask]) == (null))) return -(pos + 1);
if (((k) == (curr))) return pos;
}
}
private void insert(int pos, IBlockState k, double v) {
if (pos == this.n) {
this.containsNullKey = true;
}
final Object[] key = this.key;
key[pos] = k;
this.value[pos] = v;
if (this.size++ >= this.maxFill) {
this.rehash(HashCommon.arraySize(this.size + 1, this.f));
}
}
}
} }

View File

@@ -0,0 +1,73 @@
/*
* This file is part of Baritone.
*
* Baritone is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Baritone is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
*/
package baritone.utils.player;
import baritone.api.utils.IBaritoneInventory;
import baritone.api.utils.IPlayerContext;
import baritone.api.utils.InventorySlot;
import baritone.api.utils.Pair;
import net.minecraft.item.ItemStack;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @author Brady
*/
public final class BaritoneInventory implements IBaritoneInventory {
private final IPlayerContext ctx;
public BaritoneInventory(IPlayerContext ctx) {
this.ctx = ctx;
}
@Override
public Stream<Pair<InventorySlot, ItemStack>> allSlots() {
return Stream.concat(this.hotbarSlots(), Stream.concat(Stream.of(this.offhand()), this.inventorySlots()));
}
@Override
public Stream<Pair<InventorySlot, ItemStack>> hotbarSlots() {
return IntStream.range(0, 9).mapToObj(InventorySlot::inventory).map(this::itemSlotPairAt);
}
@Override
public Stream<Pair<InventorySlot, ItemStack>> inventorySlots() {
return IntStream.range(9, 36).mapToObj(InventorySlot::inventory).map(this::itemSlotPairAt);
}
@Override
public Pair<InventorySlot, ItemStack> offhand() {
return new Pair<>(InventorySlot.offhand(), ctx.player().inventory.offHandInventory.get(0));
}
@Override
public Stream<Pair<InventorySlot, ItemStack>> armorSlots() {
return IntStream.range(0, 4).mapToObj(InventorySlot::armor).map(this::itemSlotPairAt);
}
@Override
public ItemStack itemAt(InventorySlot slot) {
return ctx.player().inventoryContainer.getSlot(slot.getSlotId()).getStack();
}
private Pair<InventorySlot, ItemStack> itemSlotPairAt(InventorySlot slot) {
return new Pair<>(slot, this.itemAt(slot));
}
}

View File

@@ -18,6 +18,7 @@
package baritone.utils.player; package baritone.utils.player;
import baritone.Baritone; import baritone.Baritone;
import baritone.api.IBaritone;
import baritone.api.cache.IWorldData; import baritone.api.cache.IWorldData;
import baritone.api.utils.*; import baritone.api.utils.*;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
@@ -38,11 +39,18 @@ public final class BaritonePlayerContext implements IPlayerContext {
private final Baritone baritone; private final Baritone baritone;
private final Minecraft mc; private final Minecraft mc;
private final IPlayerController playerController; private final IPlayerController playerController;
private final IBaritoneInventory inventory;
public BaritonePlayerContext(Baritone baritone, Minecraft mc) { public BaritonePlayerContext(Baritone baritone, Minecraft mc) {
this.baritone = baritone; this.baritone = baritone;
this.mc = mc; this.mc = mc;
this.playerController = new BaritonePlayerController(mc); this.playerController = new BaritonePlayerController(mc);
this.inventory = new BaritoneInventory(this);
}
@Override
public IBaritone baritone() {
return this.baritone;
} }
@Override @Override
@@ -60,6 +68,11 @@ public final class BaritonePlayerContext implements IPlayerContext {
return this.playerController; return this.playerController;
} }
@Override
public IBaritoneInventory inventory() {
return this.player() == null ? null : this.inventory;
}
@Override @Override
public World world() { public World world() {
return this.mc.world; return this.mc.world;