diff --git a/src/api/java/baritone/api/utils/IBaritoneInventory.java b/src/api/java/baritone/api/utils/IBaritoneInventory.java
new file mode 100644
index 000000000..a30fd97d0
--- /dev/null
+++ b/src/api/java/baritone/api/utils/IBaritoneInventory.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Baritone. If not, see .
+ */
+
+package baritone.api.utils;
+
+import net.minecraft.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. 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.
+ *
+ * @return All the player's inventory slots and items
+ */
+ Stream> allSlots();
+
+ Stream> hotbarSlots();
+
+ Stream> inventorySlots();
+
+ Pair offhand();
+}
diff --git a/src/api/java/baritone/api/utils/IPlayerContext.java b/src/api/java/baritone/api/utils/IPlayerContext.java
index 14ca69fb9..02c41255f 100644
--- a/src/api/java/baritone/api/utils/IPlayerContext.java
+++ b/src/api/java/baritone/api/utils/IPlayerContext.java
@@ -40,6 +40,8 @@ public interface IPlayerContext {
IPlayerController playerController();
+ IBaritoneInventory inventory();
+
World world();
IWorldData worldData();
diff --git a/src/main/java/baritone/utils/InventorySlot.java b/src/api/java/baritone/api/utils/InventorySlot.java
similarity index 99%
rename from src/main/java/baritone/utils/InventorySlot.java
rename to src/api/java/baritone/api/utils/InventorySlot.java
index d2637ddc6..87c730f43 100644
--- a/src/main/java/baritone/utils/InventorySlot.java
+++ b/src/api/java/baritone/api/utils/InventorySlot.java
@@ -15,7 +15,7 @@
* along with Baritone. If not, see .
*/
-package baritone.utils;
+package baritone.api.utils;
import java.util.function.ObjIntConsumer;
diff --git a/src/api/java/baritone/api/utils/Pair.java b/src/api/java/baritone/api/utils/Pair.java
new file mode 100644
index 000000000..ca7259520
--- /dev/null
+++ b/src/api/java/baritone/api/utils/Pair.java
@@ -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 .
+ */
+
+package baritone.api.utils;
+
+import java.util.Objects;
+
+/**
+ * @author Brady
+ */
+public final class Pair {
+
+ 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);
+ }
+}
diff --git a/src/main/java/baritone/behavior/InventoryBehavior.java b/src/main/java/baritone/behavior/InventoryBehavior.java
index 721322671..2641ca271 100644
--- a/src/main/java/baritone/behavior/InventoryBehavior.java
+++ b/src/main/java/baritone/behavior/InventoryBehavior.java
@@ -20,12 +20,12 @@ package baritone.behavior;
import baritone.Baritone;
import baritone.api.event.events.TickEvent;
import baritone.api.utils.Helper;
-import baritone.utils.InventorySlot;
+import baritone.api.utils.InventorySlot;
+import baritone.api.utils.Pair;
import baritone.utils.ToolSet;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
-import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.ClickType;
@@ -36,12 +36,10 @@ import net.minecraft.world.World;
import javax.annotation.Nonnull;
import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.OptionalInt;
-import java.util.Random;
+import java.util.*;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
+import java.util.stream.Stream;
public final class InventoryBehavior extends Behavior implements Helper {
@@ -69,14 +67,7 @@ public final class InventoryBehavior extends Behavior implements Helper {
}
if (Baritone.settings().allowHotbarManagement.value && baritone.getPathingBehavior().isPathing()) {
- // TODO: Some way of indicating which slots are currently reserved by this setting
- 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);
- }
+ this.setupHotbar();
}
if (lastTickRequestedMove != null) {
@@ -85,6 +76,22 @@ public final class InventoryBehavior extends Behavior implements Helper {
}
}
+ 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 = bestToolAgainst(Blocks.STONE, ItemPickaxe.class);
+ if (pick != null && pick.getType() == InventorySlot.Type.INVENTORY) {
+ requestSwapWithHotBar(pick.getInventoryIndex(), 0);
+ }
+ }
+
public boolean attemptToPutOnHotbar(int inMainInvy, IntPredicate disallowedHotbar) {
OptionalInt destination = getTempHotbarSlot(disallowedHotbar);
if (destination.isPresent()) {
@@ -132,37 +139,22 @@ public final class InventoryBehavior extends Behavior implements Helper {
return true;
}
- private int firstValidThrowaway() { // TODO offhand idk
- NonNullList invy = ctx.player().inventory.mainInventory;
- for (int i = 0; i < invy.size(); i++) {
- if (this.isThrowawayItem(invy.get(i))) {
- return i;
- }
- }
- return -1;
- }
-
- private int bestToolAgainst(Block against, Class extends ItemTool> cla$$) {
- NonNullList 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;
+ private InventorySlot bestToolAgainst(final Block against, final Class extends ItemTool> cla$$) {
+ return this.findBestSlotMatching(
+ Comparator.comparingDouble(stack -> ToolSet.calculateSpeedVsBlock(stack, against.getDefaultState())),
+ stack -> {
+ if (stack.isEmpty()) {
+ return false;
+ }
+ if (Baritone.settings().itemSaver.value
+ && stack.getItemDamage() + Baritone.settings().itemSaverThreshold.value >= stack.getMaxDamage()
+ && stack.getMaxDamage() > 1
+ ) {
+ return false;
+ }
+ return cla$$.isInstance(stack.getItem());
}
- }
- }
- return bestInd;
+ );
}
public boolean hasGenericThrowaway() {
@@ -250,47 +242,40 @@ public final class InventoryBehavior extends Behavior implements Helper {
return null;
}
- /**
- * Returns an {@link InventorySlot} that contains a stack matching the given predicate. The priority of the
- * returned slot is the hotbar, offhand, and finally the main inventory, if {@link #canAccessInventory()} is
- * {@code true}. Additionally, for the hotbar and main inventory, slots with a lower index will be returned.
- *
- * @param desired The predicate to match
- * @return A matching slot, or {@code null} if none.
- */
- public InventorySlot findSlotMatching(final Predicate super ItemStack> desired) {
- final InventorySlot hotbar = this.findHotbarMatching(desired);
- if (hotbar != null) {
- return hotbar;
- }
-
- final EntityPlayerSP p = ctx.player();
- final NonNullList inv = p.inventory.mainInventory;
-
- if (desired.test(p.inventory.offHandInventory.get(0))) {
- return InventorySlot.offhand();
- }
-
- if (this.canAccessInventory()) {
- for (int i = 9; i < 36; i++) {
- if (desired.test(inv.get(i))) {
- return InventorySlot.inventory(i);
- }
- }
- }
- return null;
+ public InventorySlot findSlotMatching(final Predicate super ItemStack> filter) {
+ return this.findBestSlotMatching(null, filter);
}
- public InventorySlot findHotbarMatching(final Predicate super ItemStack> desired) {
- final EntityPlayerSP p = ctx.player();
- final NonNullList inv = p.inventory.mainInventory;
- for (int i = 0; i < 9; i++) {
- ItemStack item = inv.get(i);
- if (desired.test(item)) {
- return InventorySlot.hotbar(i);
- }
- }
- return null;
+ /**
+ * 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> slots,
+ final Comparator super ItemStack> comparator,
+ final Predicate super ItemStack> filter) {
+ final Stream> 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() {
diff --git a/src/main/java/baritone/utils/player/BaritoneInventory.java b/src/main/java/baritone/utils/player/BaritoneInventory.java
new file mode 100644
index 000000000..58f1e992d
--- /dev/null
+++ b/src/main/java/baritone/utils/player/BaritoneInventory.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of Baritone.
+ *
+ * Baritone is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Baritone is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Baritone. If not, see .
+ */
+
+package baritone.utils.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
+ * @since 7/4/2023
+ */
+public final class BaritoneInventory implements IBaritoneInventory {
+
+ private final IPlayerContext ctx;
+
+ public BaritoneInventory(IPlayerContext ctx) {
+ this.ctx = ctx;
+ }
+
+ @Override
+ public Stream> allSlots() {
+ return Stream.concat(this.hotbarSlots(), Stream.concat(Stream.of(this.offhand()), this.inventorySlots()));
+ }
+
+ @Override
+ public Stream> hotbarSlots() {
+ return IntStream.range(0, 9).mapToObj(i ->
+ new Pair<>(InventorySlot.hotbar(i), ctx.player().inventory.mainInventory.get(i)));
+ }
+
+ @Override
+ public Stream> inventorySlots() {
+ return IntStream.range(9, 36).mapToObj(i ->
+ new Pair<>(InventorySlot.inventory(i), ctx.player().inventory.mainInventory.get(i)));
+ }
+
+ @Override
+ public Pair offhand() {
+ return new Pair<>(InventorySlot.offhand(), ctx.player().inventory.offHandInventory.get(0));
+ }
+}
diff --git a/src/main/java/baritone/utils/player/BaritonePlayerContext.java b/src/main/java/baritone/utils/player/BaritonePlayerContext.java
index 282d3d8b6..e7eaecb52 100644
--- a/src/main/java/baritone/utils/player/BaritonePlayerContext.java
+++ b/src/main/java/baritone/utils/player/BaritonePlayerContext.java
@@ -38,11 +38,13 @@ 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
@@ -60,6 +62,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;