Compare commits
21 Commits
1.19.4
...
pr/invento
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a6d0642be | ||
|
|
dbf5058066 | ||
|
|
bc18f0eabd | ||
|
|
827ce7e2a8 | ||
|
|
2e32c63bc2 | ||
|
|
53651980ad | ||
|
|
ec089cdeaa | ||
|
|
2408c9b6af | ||
|
|
600d5e8a67 | ||
|
|
57c3e369b0 | ||
|
|
827125d827 | ||
|
|
4d7eb003e6 | ||
|
|
1ef622c936 | ||
|
|
c44701e689 | ||
|
|
7f27c9d85f | ||
|
|
15621ea763 | ||
|
|
4591fd7eb3 | ||
|
|
028405dd30 | ||
|
|
2953b1cfaf | ||
|
|
b2cdd27ca2 | ||
|
|
4bf2dd6851 |
@@ -73,6 +73,13 @@ public final class Settings {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
||||
47
src/api/java/baritone/api/utils/IBaritoneInventory.java
Normal file
47
src/api/java/baritone/api/utils/IBaritoneInventory.java
Normal 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);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package baritone.api.utils;
|
||||
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.cache.IWorldData;
|
||||
import net.minecraft.block.BlockSlab;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -34,12 +35,16 @@ import java.util.Optional;
|
||||
*/
|
||||
public interface IPlayerContext {
|
||||
|
||||
IBaritone baritone();
|
||||
|
||||
Minecraft minecraft();
|
||||
|
||||
EntityPlayerSP player();
|
||||
|
||||
IPlayerController playerController();
|
||||
|
||||
IBaritoneInventory inventory();
|
||||
|
||||
World world();
|
||||
|
||||
IWorldData worldData();
|
||||
|
||||
129
src/api/java/baritone/api/utils/InventorySlot.java
Normal file
129
src/api/java/baritone/api/utils/InventorySlot.java
Normal 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
|
||||
}
|
||||
}
|
||||
59
src/api/java/baritone/api/utils/Pair.java
Normal file
59
src/api/java/baritone/api/utils/Pair.java
Normal 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);
|
||||
}
|
||||
}
|
||||
36
src/main/java/baritone/PerformanceCritical.java
Normal file
36
src/main/java/baritone/PerformanceCritical.java
Normal 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 {}
|
||||
@@ -20,25 +20,29 @@ package baritone.behavior;
|
||||
import baritone.Baritone;
|
||||
import baritone.api.event.events.TickEvent;
|
||||
import baritone.api.utils.Helper;
|
||||
import baritone.api.utils.InventorySlot;
|
||||
import baritone.api.utils.Pair;
|
||||
import baritone.utils.ItemInteractionHelper;
|
||||
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.*;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.NonNullList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.Random;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class InventoryBehavior extends Behavior implements Helper {
|
||||
|
||||
int ticksSinceLastInventoryMove;
|
||||
int[] lastTickRequestedMove; // not everything asks every tick, so remember the request while coming to a halt
|
||||
private int ticksSinceLastInventoryMove;
|
||||
private int[] lastTickRequestedMove; // not everything asks every tick, so remember the request while coming to a halt
|
||||
|
||||
public InventoryBehavior(Baritone baritone) {
|
||||
super(baritone);
|
||||
@@ -46,31 +50,51 @@ public final class InventoryBehavior extends Behavior implements Helper {
|
||||
|
||||
@Override
|
||||
public void onTick(TickEvent event) {
|
||||
if (!Baritone.settings().allowInventory.value) {
|
||||
if (event.getType() == TickEvent.Type.OUT) {
|
||||
return;
|
||||
}
|
||||
if (event.getType() == TickEvent.Type.OUT) {
|
||||
ticksSinceLastInventoryMove++;
|
||||
|
||||
// TODO: Move these checks into "requestSwapWithHotBar" or whatever supersedes it
|
||||
if (!this.canAccessInventory()) {
|
||||
return;
|
||||
}
|
||||
if (ctx.player().openContainer != ctx.player().inventoryContainer) {
|
||||
// we have a crafting table or a chest or something open
|
||||
return;
|
||||
}
|
||||
ticksSinceLastInventoryMove++;
|
||||
if (firstValidThrowaway() >= 9) { // aka there are none on the hotbar, but there are some in main inventory
|
||||
requestSwapWithHotBar(firstValidThrowaway(), 8);
|
||||
}
|
||||
int pick = bestToolAgainst(Blocks.STONE, ItemPickaxe.class);
|
||||
if (pick >= 9) {
|
||||
requestSwapWithHotBar(pick, 0);
|
||||
|
||||
if (Baritone.settings().allowHotbarManagement.value && baritone.getPathingBehavior().isPathing()) {
|
||||
this.setupHotbar();
|
||||
}
|
||||
|
||||
if (lastTickRequestedMove != null) {
|
||||
logDebug("Remembering to move " + lastTickRequestedMove[0] + " " + lastTickRequestedMove[1] + " from a previous tick");
|
||||
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);
|
||||
if (destination.isPresent()) {
|
||||
if (!requestSwapWithHotBar(inMainInvy, destination.getAsInt())) {
|
||||
@@ -80,7 +104,7 @@ public final class InventoryBehavior extends Behavior implements Helper {
|
||||
return true;
|
||||
}
|
||||
|
||||
public OptionalInt getTempHotbarSlot(Predicate<Integer> disallowedHotbar) {
|
||||
public OptionalInt getTempHotbarSlot(IntPredicate disallowedHotbar) {
|
||||
// we're using 0 and 8 for pickaxe and throwaway
|
||||
ArrayList<Integer> candidates = new ArrayList<>();
|
||||
for (int i = 1; i < 8; i++) {
|
||||
@@ -117,114 +141,172 @@ public final class InventoryBehavior extends Behavior implements Helper {
|
||||
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() {
|
||||
for (Item item : Baritone.settings().acceptableThrowawayItems.value) {
|
||||
if (throwaway(false, stack -> item.equals(stack.getItem()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return this.canSelectItem(this::isThrowawayItem);
|
||||
}
|
||||
|
||||
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));
|
||||
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 Predicate<Predicate<? super ItemStack>> op = select ? this::trySelectItem : this::canSelectItem;
|
||||
|
||||
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;
|
||||
}
|
||||
if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && ((ItemBlock) stack.getItem()).getBlock().equals(maybe.getBlock()))) {
|
||||
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());
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
for (Item item : Baritone.settings().acceptableThrowawayItems.value) {
|
||||
if (throwaway(select, stack -> item.equals(stack.getItem()))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired) {
|
||||
return throwaway(select, desired, Baritone.settings().allowInventory.value);
|
||||
public SelectionStrategy resolveSelectionStrategy(final InventorySlot slot) {
|
||||
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, () -> {});
|
||||
}
|
||||
|
||||
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired, boolean allowInventory) {
|
||||
EntityPlayerSP p = ctx.player();
|
||||
NonNullList<ItemStack> inv = p.inventory.mainInventory;
|
||||
for (int 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)) {
|
||||
if (select) {
|
||||
p.inventory.currentItem = i;
|
||||
final InventorySlot hotbar = this.findHotbarMatching(item -> !ItemInteractionHelper.couldInteract(item));
|
||||
if (hotbar != null) {
|
||||
return SelectionStrategy.of(SelectionType.IMMEDIATE, () ->
|
||||
ctx.player().inventory.currentItem = hotbar.getInventoryIndex());
|
||||
}
|
||||
return true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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 null;
|
||||
}
|
||||
return true;
|
||||
|
||||
public InventorySlot findBestAccessibleMatching(final Comparator<? super ItemStack> comparator,
|
||||
final Predicate<? super ItemStack> filter) {
|
||||
final Stream<Pair<InventorySlot, ItemStack>> accessible = this.canAccessInventory()
|
||||
? ctx.inventory().allSlots()
|
||||
: Stream.concat(ctx.inventory().hotbarSlots(), Stream.of(ctx.inventory().offhand()));
|
||||
return this.findBestMatching0(accessible, comparator, filter);
|
||||
}
|
||||
|
||||
public InventorySlot findSlotMatching(final Predicate<? super ItemStack> filter) {
|
||||
return this.findBestSlotMatching(null, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (allowInventory) {
|
||||
for (int i = 9; i < 36; i++) {
|
||||
if (desired.test(inv.get(i))) {
|
||||
if (select) {
|
||||
requestSwapWithHotBar(i, 7);
|
||||
p.inventory.currentItem = 7;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
public enum SelectionType {
|
||||
IMMEDIATE, ENQUEUED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class CalculationContext {
|
||||
this.world = baritone.getPlayerContext().world();
|
||||
this.worldData = (WorldData) baritone.getPlayerContext().worldData();
|
||||
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.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;
|
||||
|
||||
@@ -18,12 +18,12 @@
|
||||
package baritone.pathing.movement;
|
||||
|
||||
import baritone.Baritone;
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.pathing.movement.ActionCosts;
|
||||
import baritone.api.pathing.movement.MovementStatus;
|
||||
import baritone.api.utils.*;
|
||||
import baritone.api.utils.input.Input;
|
||||
import baritone.behavior.InventoryBehavior;
|
||||
import baritone.pathing.movement.MovementState.MovementTarget;
|
||||
import baritone.pathing.precompute.Ternary;
|
||||
import baritone.utils.BlockStateInterface;
|
||||
@@ -578,22 +578,16 @@ public interface MovementHelper extends ActionCosts, Helper {
|
||||
* AutoTool for a specific block
|
||||
*
|
||||
* @param ctx The player context
|
||||
* @param b the blockstate to mine
|
||||
* @param state The blockstate to mine
|
||||
*/
|
||||
static void switchToBestToolFor(IPlayerContext ctx, IBlockState b) {
|
||||
switchToBestToolFor(ctx, b, new ToolSet(ctx.player()), BaritoneAPI.getSettings().preferSilkTouch.value);
|
||||
static void switchToBestToolFor(IPlayerContext ctx, IBlockState state) {
|
||||
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);
|
||||
if (strategy != null) {
|
||||
strategy.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* AutoTool for a specific block with precomputed ToolSet data
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil
|
||||
return new PathingCommand(null, PathingCommandType.CANCEL_AND_SET_GOAL);
|
||||
}
|
||||
|
||||
if (Baritone.settings().allowInventory.value) {
|
||||
if (baritone.getInventoryBehavior().canAccessInventory()) {
|
||||
ArrayList<Integer> usefulSlots = new ArrayList<>();
|
||||
List<IBlockState> noValidHotbarOption = new ArrayList<>();
|
||||
outer:
|
||||
|
||||
@@ -269,7 +269,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
|
||||
for (BlockPos pos : both) {
|
||||
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);
|
||||
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());
|
||||
if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == EnumFacing.UP) {
|
||||
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));
|
||||
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());
|
||||
if (result.typeOfHit == RayTraceResult.Type.BLOCK && result.sideHit == dir) {
|
||||
baritone.getLookBehavior().updateTarget(rot.get(), true);
|
||||
@@ -301,7 +301,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
|
||||
}
|
||||
for (BlockPos pos : bonemealable) {
|
||||
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);
|
||||
if (ctx.isLookingAt(pos)) {
|
||||
baritone.getInputOverrideHandler().setInputForceState(Input.CLICK_RIGHT, true);
|
||||
@@ -323,17 +323,17 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro
|
||||
for (BlockPos pos : toBreak) {
|
||||
goalz.add(new BuilderProcess.GoalBreak(pos));
|
||||
}
|
||||
if (baritone.getInventoryBehavior().throwaway(false, this::isPlantable)) {
|
||||
if (baritone.getInventoryBehavior().canSelectItem(this::isPlantable)) {
|
||||
for (BlockPos pos : openFarmland) {
|
||||
goalz.add(new GoalBlock(pos.up()));
|
||||
}
|
||||
}
|
||||
if (baritone.getInventoryBehavior().throwaway(false, this::isNetherWart)) {
|
||||
if (baritone.getInventoryBehavior().canSelectItem(this::isNetherWart)) {
|
||||
for (BlockPos pos : openSoulsand) {
|
||||
goalz.add(new GoalBlock(pos.up()));
|
||||
}
|
||||
}
|
||||
if (baritone.getInventoryBehavior().throwaway(false, this::isCocoa)) {
|
||||
if (baritone.getInventoryBehavior().canSelectItem(this::isCocoa)) {
|
||||
for (BlockPos pos : openLog) {
|
||||
for (EnumFacing direction : EnumFacing.Plane.HORIZONTAL) {
|
||||
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) {
|
||||
goalz.add(new GoalBlock(pos));
|
||||
}
|
||||
|
||||
100
src/main/java/baritone/utils/ItemInteractionHelper.java
Normal file
100
src/main/java/baritone/utils/ItemInteractionHelper.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,62 +18,78 @@
|
||||
package baritone.utils;
|
||||
|
||||
import baritone.Baritone;
|
||||
import baritone.PerformanceCritical;
|
||||
import baritone.api.utils.IPlayerContext;
|
||||
import baritone.api.utils.InventorySlot;
|
||||
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.state.IBlockState;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.init.Enchantments;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.item.ItemTool;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
|
||||
/**
|
||||
* A cached list of the best tools on the hotbar for any block
|
||||
*
|
||||
* @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
|
||||
* 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.
|
||||
*/
|
||||
private final Function<Block, Double> backendCalculation;
|
||||
private final ToDoubleFunction<IBlockState> backendCalculation;
|
||||
|
||||
private final EntityPlayerSP player;
|
||||
private final IPlayerContext ctx;
|
||||
|
||||
public ToolSet(EntityPlayerSP player) {
|
||||
breakStrengthCache = new HashMap<>();
|
||||
this.player = player;
|
||||
public ToolSet(IPlayerContext ctx) {
|
||||
this.ctx = ctx;
|
||||
this.breakStrengthCache = new Cache();
|
||||
|
||||
if (Baritone.settings().considerPotionEffects.value) {
|
||||
double amplifier = potionAmplifier();
|
||||
Function<Double, Double> amplify = x -> amplifier * x;
|
||||
backendCalculation = amplify.compose(this::getBestDestructionTime);
|
||||
double amplifier = this.potionAmplifier();
|
||||
this.backendCalculation = block -> amplifier * this.getBestDestructionSpeed(block);
|
||||
} else {
|
||||
backendCalculation = this::getBestDestructionTime;
|
||||
this.backendCalculation = this::getBestDestructionSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
@PerformanceCritical
|
||||
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
|
||||
* but in that case we don't really care.
|
||||
*
|
||||
* @param itemStack a possibly empty ItemStack
|
||||
* @return values from 0 up
|
||||
* @param stack a possibly empty ItemStack
|
||||
* @return The tool's harvest level, or {@code -1} if the stack isn't a tool
|
||||
*/
|
||||
private int getMaterialCost(ItemStack itemStack) {
|
||||
if (itemStack.getItem() instanceof ItemTool) {
|
||||
ItemTool tool = (ItemTool) itemStack.getItem();
|
||||
return ((IItemTool) tool).getHarvestLevel();
|
||||
private static int getMaterialCost(ItemStack stack) {
|
||||
if (stack.getItem() instanceof IItemTool) {
|
||||
return ((IItemTool) stack.getItem()).getHarvestLevel();
|
||||
} else {
|
||||
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,
|
||||
* 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
|
||||
*/
|
||||
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 getBestSlot(b, preferSilkTouch, false);
|
||||
return ((Baritone) ctx.baritone()).getInventoryBehavior().findBestAccessibleMatching(
|
||||
compare,
|
||||
stack -> {
|
||||
if (!Baritone.settings().useSwordToMine.value && stack.getItem() instanceof ItemSword) {
|
||||
return false;
|
||||
}
|
||||
if (Baritone.settings().itemSaver.value
|
||||
&& stack.getItemDamage() + Baritone.settings().itemSaverThreshold.value >= stack.getMaxDamage()
|
||||
&& stack.getMaxDamage() > 1
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return extra == null || extra.test(stack);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public int getBestSlot(Block b, boolean preferSilkTouch, boolean pathingCalculation) {
|
||||
|
||||
/*
|
||||
If we actually want know what efficiency our held item has instead of the best one
|
||||
possible, this lets us make pathing depend on the actual tool to be used (if auto tool is disabled)
|
||||
*/
|
||||
if (!Baritone.settings().autoTool.value && pathingCalculation) {
|
||||
return player.inventory.currentItem;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how effectively a block can be destroyed
|
||||
*
|
||||
* @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;
|
||||
public static boolean isAutoTool() {
|
||||
return Baritone.settings().autoTool.value && !Baritone.settings().assumeExternalAutoTool.value;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,10 +154,19 @@ public class ToolSet {
|
||||
*
|
||||
* @param item the item to mine it with
|
||||
* @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) {
|
||||
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) {
|
||||
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.
|
||||
*
|
||||
@@ -206,11 +203,11 @@ public class ToolSet {
|
||||
*/
|
||||
private double potionAmplifier() {
|
||||
double speed = 1;
|
||||
if (player.isPotionActive(MobEffects.HASTE)) {
|
||||
speed *= 1 + (player.getActivePotionEffect(MobEffects.HASTE).getAmplifier() + 1) * 0.2;
|
||||
if (ctx.player().isPotionActive(MobEffects.HASTE)) {
|
||||
speed *= 1 + (ctx.player().getActivePotionEffect(MobEffects.HASTE).getAmplifier() + 1) * 0.2;
|
||||
}
|
||||
if (player.isPotionActive(MobEffects.MINING_FATIGUE)) {
|
||||
switch (player.getActivePotionEffect(MobEffects.MINING_FATIGUE).getAmplifier()) {
|
||||
if (ctx.player().isPotionActive(MobEffects.MINING_FATIGUE)) {
|
||||
switch (ctx.player().getActivePotionEffect(MobEffects.MINING_FATIGUE).getAmplifier()) {
|
||||
case 0:
|
||||
speed *= 0.3;
|
||||
break;
|
||||
@@ -227,4 +224,60 @@ public class ToolSet {
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
src/main/java/baritone/utils/player/BaritoneInventory.java
Normal file
73
src/main/java/baritone/utils/player/BaritoneInventory.java
Normal 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));
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
package baritone.utils.player;
|
||||
|
||||
import baritone.Baritone;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.cache.IWorldData;
|
||||
import baritone.api.utils.*;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -38,11 +39,18 @@ public final class BaritonePlayerContext implements IPlayerContext {
|
||||
private final Baritone baritone;
|
||||
private final Minecraft mc;
|
||||
private final IPlayerController playerController;
|
||||
private final IBaritoneInventory inventory;
|
||||
|
||||
public BaritonePlayerContext(Baritone baritone, Minecraft mc) {
|
||||
this.baritone = baritone;
|
||||
this.mc = mc;
|
||||
this.playerController = new BaritonePlayerController(mc);
|
||||
this.inventory = new BaritoneInventory(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaritone baritone() {
|
||||
return this.baritone;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,6 +68,11 @@ public final class BaritonePlayerContext implements IPlayerContext {
|
||||
return this.playerController;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaritoneInventory inventory() {
|
||||
return this.player() == null ? null : this.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public World world() {
|
||||
return this.mc.world;
|
||||
|
||||
Reference in New Issue
Block a user