This commit is contained in:
Brady 2023-07-02 19:46:48 -05:00
parent 7f27c9d85f
commit c44701e689
No known key found for this signature in database
GPG Key ID: 73A788379A197567
3 changed files with 228 additions and 41 deletions

View File

@ -75,7 +75,7 @@ public final class Settings {
/**
* Allow Baritone to automatically put useful items (such as tools and throwaway blocks) on the hotbar while
* pathing. Having this setting enabled implies {@link #allowInventory}.
* pathing. Requires {@link #allowInventory}.
*/
public final Setting<Boolean> allowHotbarManagement = new Setting<>(false);

View File

@ -20,6 +20,7 @@ package baritone.behavior;
import baritone.Baritone;
import baritone.api.event.events.TickEvent;
import baritone.api.utils.Helper;
import baritone.utils.InventorySlot;
import baritone.utils.ToolSet;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
@ -27,8 +28,10 @@ import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.ClickType;
import net.minecraft.item.*;
import net.minecraft.network.play.client.CPacketPlayerDigging;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
import java.util.OptionalInt;
@ -163,9 +166,11 @@ public final class InventoryBehavior extends Behavior implements Helper {
}
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;
final IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z));
if (maybe != null) {
return this.throwaway(select, stack -> {
return op.test(stack -> {
if (!(stack.getItem() instanceof ItemBlock)) {
return false;
}
@ -178,75 +183,116 @@ public final class InventoryBehavior extends Behavior implements Helper {
stack.getItem().getMetadata(stack.getMetadata()),
ctx.player()
));
}) || this.throwaway(select, stack -> {
}) || 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 this.throwaway(select, this::isThrowawayItem);
return op.test(this::isThrowawayItem);
}
public boolean canSelectItem(Predicate<? super ItemStack> desired) {
return this.throwaway(false, desired);
return this.resolveSelectionStrategy(desired) != null;
}
public boolean trySelectItem(Predicate<? super ItemStack> desired) {
return this.throwaway(true, desired);
final SelectionStrategy strategy = this.resolveSelectionStrategy(desired);
if (strategy != null) {
strategy.run();
// TODO: Consider cases where returning the SelectionType is needed/useful to the caller
return true;
}
return false;
}
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired) {
EntityPlayerSP p = ctx.player();
NonNullList<ItemStack> inv = p.inventory.mainInventory;
public SelectionStrategy resolveSelectionStrategy(Predicate<? super ItemStack> desired) {
final InventorySlot slot = this.findSlotMatching(desired);
if (slot != null) {
switch (slot.getType()) {
case HOTBAR:
return SelectionStrategy.of(SelectionType.IMMEDIATE, () ->
ctx.player().inventory.currentItem = slot.getInventoryIndex());
case OFFHAND:
// 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 = ctx.player().inventory.mainInventory.get(i);
// if (item.isEmpty() || item.getItem() instanceof ItemPickaxe) {
// ctx.player().inventory.currentItem = i;
// break;
// }
// }
// TODO: This method of acquiring an offhand item is temporary and just guarantees that there won't
// be any item usage issues. 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.
// If there is an empty slot on the hotbar, switch to that slot and swap the offhand into it
final InventorySlot empty = this.findSlotMatching(ItemStack::isEmpty);
if (empty != null && empty.getType() == InventorySlot.Type.HOTBAR) {
return SelectionStrategy.of(SelectionType.ENQUEUED, () -> {
ctx.player().inventory.currentItem = empty.getInventoryIndex();
ctx.playerController().syncHeldItem();
ctx.player().connection.sendPacket(new CPacketPlayerDigging(
CPacketPlayerDigging.Action.SWAP_HELD_ITEMS,
BlockPos.ORIGIN,
EnumFacing.DOWN
));
});
}
break;
case INVENTORY:
if (this.canAccessInventory()) {
return SelectionStrategy.of(SelectionType.ENQUEUED, () -> {
// TODO: Determine if hotbar swap can be immediate, and return type accordingly
requestSwapWithHotBar(slot.getInventoryIndex(), 7);
ctx.player().inventory.currentItem = 7;
});
}
break;
}
}
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 EntityPlayerSP p = ctx.player();
final 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;
}
return true;
return InventorySlot.hotbar(i);
}
}
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;
}
}
return InventorySlot.offhand();
}
if (this.canAccessInventory()) {
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 InventorySlot.inventory(i);
}
}
}
return false;
return null;
}
public boolean canAccessInventory() {
return Baritone.settings().allowInventory.value || Baritone.settings().allowHotbarManagement.value;
return Baritone.settings().allowInventory.value;
}
public boolean isThrowawayItem(ItemStack stack) {
@ -256,4 +302,30 @@ public final class InventoryBehavior extends Behavior implements Helper {
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

@ -0,0 +1,115 @@
/*
* 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;
/**
* @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 {
SLOTS[0] = new InventorySlot(0, Type.CRAFTING_OUTPUT);
for (int i = 0; i < 4; i++) {
SLOTS[i + 1] = new InventorySlot(i + 1, Type.CRAFTING_GRID);
}
for (int i = 0; i < 4; i++) {
SLOTS[i + 5] = new InventorySlot(i + 5, Type.ARMOR);
}
for (int i = 0; i < 27; i++) {
SLOTS[i + 9] = new InventorySlot(i + 9, Type.INVENTORY);
}
for (int i = 0; i < 9; i++) {
SLOTS[i + 36] = new InventorySlot(i + 36, Type.HOTBAR);
}
SLOTS[45] = new InventorySlot(45, Type.OFFHAND);
}
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;
}
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 hotbar(final int index) {
if (index < 0 || index >= 9) {
throw new IllegalArgumentException();
}
return SLOTS[index + 36];
}
public static InventorySlot inventory(final int index) {
if (index < 9 || index >= 36) {
throw new IllegalArgumentException();
}
return SLOTS[index];
}
public static InventorySlot offhand() {
return SLOTS[45];
}
public enum Type {
CRAFTING_OUTPUT,
CRAFTING_GRID,
ARMOR,
INVENTORY,
HOTBAR,
OFFHAND
}
}