From 827125d82710c862bd792dad1a103ddbc4643e20 Mon Sep 17 00:00:00 2001 From: Brady Date: Tue, 4 Jul 2023 21:34:57 -0700 Subject: [PATCH] Inventory API thing and substitution of legacy code Also stole `Pair` from elytra branch :sunglasses: --- .../api/utils/IBaritoneInventory.java | 42 +++++ .../baritone/api/utils/IPlayerContext.java | 2 + .../baritone/api}/utils/InventorySlot.java | 2 +- src/api/java/baritone/api/utils/Pair.java | 59 +++++++ .../baritone/behavior/InventoryBehavior.java | 151 ++++++++---------- .../utils/player/BaritoneInventory.java | 62 +++++++ .../utils/player/BaritonePlayerContext.java | 7 + 7 files changed, 241 insertions(+), 84 deletions(-) create mode 100644 src/api/java/baritone/api/utils/IBaritoneInventory.java rename src/{main/java/baritone => api/java/baritone/api}/utils/InventorySlot.java (99%) create mode 100644 src/api/java/baritone/api/utils/Pair.java create mode 100644 src/main/java/baritone/utils/player/BaritoneInventory.java 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 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 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 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 filter) { + return this.findBestSlotMatching(null, filter); } - public InventorySlot findHotbarMatching(final Predicate 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 comparator, final Predicate filter) { + return this.findBestMatching0(ctx.inventory().allSlots(), comparator, filter); + } + + public InventorySlot findHotbarMatching(final Predicate filter) { + return this.findBestHotbarMatching(null, filter); + } + + public InventorySlot findBestHotbarMatching(final Comparator comparator, final Predicate filter) { + return this.findBestMatching0(ctx.inventory().hotbarSlots(), comparator, filter); + } + + private InventorySlot findBestMatching0(final Stream> slots, + final Comparator comparator, + final Predicate 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;