Remove container memory

Signed-off-by: scorbett123 <50634068+scorbett123@users.noreply.github.com>
This commit is contained in:
scorbett123 2021-12-12 19:50:27 +00:00
parent 0ca84a790b
commit 90fcffdf2a
10 changed files with 0 additions and 717 deletions

View File

@ -555,13 +555,6 @@ public final class Settings {
*/
public final Setting<Boolean> pruneRegionsFromRAM = new Setting<>(true);
/**
* Remember the contents of containers (chests, echests, furnaces)
* <p>
* Really buggy since the packet stuff is multithreaded badly thanks to brady
*/
public final Setting<Boolean> containerMemory = new Setting<>(false);
/**
* Fill in blocks behind you
*/

View File

@ -1,44 +0,0 @@
/*
* 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.cache;
import net.minecraft.util.math.BlockPos;
import java.util.Map;
/**
* @author Brady
* @since 9/23/2018
*/
public interface IContainerMemory {
/**
* Gets a remembered inventory by its block position.
*
* @param pos The position of the container block
* @return The remembered inventory
*/
IRememberedInventory getInventoryByPos(BlockPos pos);
/**
* Gets the map of all block positions to their remembered inventories.
*
* @return Map of block positions to their respective remembered inventories
*/
Map<BlockPos, IRememberedInventory> getRememberedInventories();
}

View File

@ -1,39 +0,0 @@
/*
* 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.cache;
import net.minecraft.item.ItemStack;
import java.util.List;
/**
* @author Brady
* @since 9/23/2018
*/
public interface IRememberedInventory {
/**
* @return The contents of this inventory
*/
List<ItemStack> getContents();
/**
* @return The number of slots in this inventory
*/
int getSize();
}

View File

@ -37,9 +37,4 @@ public interface IWorldData {
*/
IWaypointCollection getWaypoints();
/**
* @return The {@link IContainerMemory} instance
* @see IContainerMemory
*/
IContainerMemory getContainerMemory();
}

View File

@ -68,7 +68,6 @@ public class Baritone implements IBaritone {
private PathingBehavior pathingBehavior;
private LookBehavior lookBehavior;
private MemoryBehavior memoryBehavior;
private InventoryBehavior inventoryBehavior;
private InputOverrideHandler inputOverrideHandler;
@ -100,7 +99,6 @@ public class Baritone implements IBaritone {
// the Behavior constructor calls baritone.registerBehavior(this) so this populates the behaviors arraylist
pathingBehavior = new PathingBehavior(this);
lookBehavior = new LookBehavior(this);
memoryBehavior = new MemoryBehavior(this);
inventoryBehavior = new InventoryBehavior(this);
inputOverrideHandler = new InputOverrideHandler(this);
}
@ -151,10 +149,6 @@ public class Baritone implements IBaritone {
return this.playerContext;
}
public MemoryBehavior getMemoryBehavior() {
return this.memoryBehavior;
}
@Override
public FollowProcess getFollowProcess() {
return this.followProcess;

View File

@ -1,332 +0,0 @@
/*
* 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.behavior;
import baritone.Baritone;
import baritone.api.cache.IWaypoint;
import baritone.api.cache.Waypoint;
import baritone.api.event.events.BlockInteractEvent;
import baritone.api.event.events.PacketEvent;
import baritone.api.event.events.PlayerUpdateEvent;
import baritone.api.event.events.TickEvent;
import baritone.api.event.events.type.EventState;
import baritone.api.utils.BetterBlockPos;
import baritone.api.utils.Helper;
import baritone.cache.ContainerMemory;
import baritone.utils.BlockStateInterface;
import net.minecraft.block.Block;
import net.minecraft.block.BlockBed;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.network.Packet;
import net.minecraft.network.play.client.CPacketCloseWindow;
import net.minecraft.network.play.client.CPacketPlayerTryUseItemOnBlock;
import net.minecraft.network.play.server.SPacketCloseWindow;
import net.minecraft.network.play.server.SPacketOpenWindow;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityLockable;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentString;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.util.text.event.ClickEvent;
import net.minecraft.util.text.event.HoverEvent;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX;
/**
* doesn't work for horse inventories :^)
*
* @author Brady
* @since 8/6/2018
*/
public final class MemoryBehavior extends Behavior {
private final List<FutureInventory> futureInventories = new ArrayList<>(); // this is per-bot
private Integer enderChestWindowId; // nae nae
public MemoryBehavior(Baritone baritone) {
super(baritone);
}
@Override
public synchronized void onTick(TickEvent event) {
if (!Baritone.settings().containerMemory.value) {
return;
}
if (event.getType() == TickEvent.Type.OUT) {
enderChestWindowId = null;
futureInventories.clear();
}
}
@Override
public synchronized void onPlayerUpdate(PlayerUpdateEvent event) {
if (event.getState() == EventState.PRE) {
updateInventory();
}
}
@Override
public synchronized void onSendPacket(PacketEvent event) {
if (!Baritone.settings().containerMemory.value) {
return;
}
Packet p = event.getPacket();
if (event.getState() == EventState.PRE) {
if (p instanceof CPacketPlayerTryUseItemOnBlock) {
CPacketPlayerTryUseItemOnBlock packet = event.cast();
TileEntity tileEntity = ctx.world().getTileEntity(packet.getPos());
// if tileEntity is an ender chest, we don't need to do anything. ender chests are treated the same regardless of what coordinate right clicked
// Ensure the TileEntity is a container of some sort
if (tileEntity instanceof TileEntityLockable) {
TileEntityLockable lockable = (TileEntityLockable) tileEntity;
int size = lockable.getSizeInventory();
BetterBlockPos position = BetterBlockPos.from(tileEntity.getPos());
BetterBlockPos adj = BetterBlockPos.from(neighboringConnectedBlock(position));
System.out.println(position + " " + adj);
if (adj != null) {
size *= 2; // double chest or double trapped chest
if (adj.getX() < position.getX() || adj.getZ() < position.getZ()) {
position = adj; // standardize on the lower coordinate, regardless of which side of the large chest we right clicked
}
}
this.futureInventories.add(new FutureInventory(System.nanoTime() / 1000000L, size, lockable.getGuiID(), position));
}
}
if (p instanceof CPacketCloseWindow) {
getCurrent().save();
}
}
}
@Override
public synchronized void onReceivePacket(PacketEvent event) {
if (!Baritone.settings().containerMemory.value) {
return;
}
Packet p = event.getPacket();
if (event.getState() == EventState.PRE) {
if (p instanceof SPacketOpenWindow) {
SPacketOpenWindow packet = event.cast();
// Remove any entries that were created over a second ago, this should make up for INSANE latency
futureInventories.removeIf(i -> System.nanoTime() / 1000000L - i.time > 1000);
System.out.println("Received packet " + packet.getGuiId() + " " + packet.getEntityId() + " " + packet.getSlotCount() + " " + packet.getWindowId());
System.out.println(packet.getWindowTitle());
if (packet.getWindowTitle() instanceof TextComponentTranslation && ((TextComponentTranslation) packet.getWindowTitle()).getKey().equals("container.enderchest")) {
// title is not customized (i.e. this isn't just a renamed shulker)
enderChestWindowId = packet.getWindowId();
return;
}
futureInventories.stream()
.filter(i -> i.type.equals(packet.getGuiId()) && i.slots == packet.getSlotCount())
.findFirst().ifPresent(matched -> {
// Remove the future inventory
futureInventories.remove(matched);
// Setup the remembered inventory
getCurrentContainer().setup(matched.pos, packet.getWindowId(), packet.getSlotCount());
});
}
if (p instanceof SPacketCloseWindow) {
getCurrent().save();
}
}
}
@Override
public void onBlockInteract(BlockInteractEvent event) {
if (event.getType() == BlockInteractEvent.Type.USE) {
BetterBlockPos pos = BetterBlockPos.from(event.getPos());
IBlockState state = BlockStateInterface.get(ctx, pos);
if (state.getBlock() instanceof BlockBed) {
if (state.getValue(BlockBed.PART) == BlockBed.EnumPartType.FOOT) {
pos = pos.offset(state.getValue(BlockBed.FACING));
}
Set<IWaypoint> waypoints = baritone.getWorldProvider().getCurrentWorld().getWaypoints().getByTag(IWaypoint.Tag.BED);
boolean exists = waypoints.stream().map(IWaypoint::getLocation).filter(pos::equals).findFirst().isPresent();
if (!exists) {
baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("bed", Waypoint.Tag.BED, pos));
}
}
}
}
@Override
public void onPlayerDeath() {
Waypoint deathWaypoint = new Waypoint("death", Waypoint.Tag.DEATH, ctx.playerFeet());
baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(deathWaypoint);
ITextComponent component = new TextComponentString("Death position saved.");
component.getStyle()
.setColor(TextFormatting.WHITE)
.setHoverEvent(new HoverEvent(
HoverEvent.Action.SHOW_TEXT,
new TextComponentString("Click to goto death")
))
.setClickEvent(new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
String.format(
"%s%s goto %s @ %d",
FORCE_COMMAND_PREFIX,
"wp",
deathWaypoint.getTag().getName(),
deathWaypoint.getCreationTimestamp()
)
));
Helper.HELPER.logDirect(component);
}
private void updateInventory() {
if (!Baritone.settings().containerMemory.value) {
return;
}
int windowId = ctx.player().openContainer.windowId;
if (enderChestWindowId != null) {
if (windowId == enderChestWindowId) {
getCurrent().contents = ctx.player().openContainer.getInventory().subList(0, 27);
} else {
getCurrent().save();
enderChestWindowId = null;
}
}
if (getCurrentContainer() != null) {
getCurrentContainer().getInventoryFromWindow(windowId).ifPresent(inventory -> inventory.updateFromOpenWindow(ctx));
}
}
private ContainerMemory getCurrentContainer() {
if (baritone.getWorldProvider().getCurrentWorld() == null) {
return null;
}
return (ContainerMemory) baritone.getWorldProvider().getCurrentWorld().getContainerMemory();
}
private BlockPos neighboringConnectedBlock(BlockPos in) {
BlockStateInterface bsi = baritone.bsi;
Block block = bsi.get0(in).getBlock();
if (block != Blocks.TRAPPED_CHEST && block != Blocks.CHEST) {
return null; // other things that have contents, but can be placed adjacent without combining
}
for (int i = 0; i < 4; i++) {
BlockPos adj = in.offset(EnumFacing.byHorizontalIndex(i));
if (bsi.get0(adj).getBlock() == block) {
return adj;
}
}
return null;
}
/**
* An inventory that we are not yet fully aware of, but are expecting to exist at some point in the future.
*/
private static final class FutureInventory {
/**
* The time that we initially expected the inventory to be provided, in milliseconds
*/
private final long time;
/**
* The amount of slots in the inventory
*/
private final int slots;
/**
* The type of inventory
*/
private final String type;
/**
* The position of the inventory container
*/
private final BlockPos pos;
private FutureInventory(long time, int slots, String type, BlockPos pos) {
this.time = time;
this.slots = slots;
this.type = type;
this.pos = pos;
// betterblockpos has censoring
System.out.println("Future inventory created " + time + " " + slots + " " + type + " " + BetterBlockPos.from(pos));
}
}
public Optional<List<ItemStack>> echest() {
return Optional.ofNullable(getCurrent().contents).map(Collections::unmodifiableList);
}
public EnderChestMemory getCurrent() {
Path path = baritone.getWorldProvider().getCurrentWorld().directory;
return EnderChestMemory.getByServerAndPlayer(path.getParent(), ctx.player().getUniqueID());
}
public static class EnderChestMemory {
private static final Map<Path, EnderChestMemory> memory = new HashMap<>();
private final Path enderChest;
private List<ItemStack> contents;
private EnderChestMemory(Path enderChest) {
this.enderChest = enderChest;
System.out.println("Echest storing in " + enderChest);
try {
this.contents = ContainerMemory.readItemStacks(Files.readAllBytes(enderChest));
} catch (IOException e) {
e.printStackTrace();
System.out.println("CANNOT read echest =( =(");
this.contents = null;
}
}
public synchronized void save() {
System.out.println("Saving");
if (contents != null) {
try {
enderChest.getParent().toFile().mkdir();
Files.write(enderChest, ContainerMemory.writeItemStacks(contents));
} catch (IOException e) {
e.printStackTrace();
System.out.println("CANNOT save echest =( =(");
}
}
}
private static synchronized EnderChestMemory getByServerAndPlayer(Path serverStorage, UUID player) {
return memory.computeIfAbsent(serverStorage.resolve("echests").resolve(player.toString()), EnderChestMemory::new);
}
}
}

View File

@ -1,183 +0,0 @@
/*
* 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.cache;
import baritone.Baritone;
import baritone.api.cache.IContainerMemory;
import baritone.api.cache.IRememberedInventory;
import baritone.api.utils.IPlayerContext;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minecraft.item.ItemStack;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.*;
public class ContainerMemory implements IContainerMemory {
private final Path saveTo;
/**
* The current remembered inventories
*/
private final Map<BlockPos, RememberedInventory> inventories = new HashMap<>();
public ContainerMemory(Path saveTo) {
this.saveTo = saveTo;
try {
read(Files.readAllBytes(saveTo));
} catch (NoSuchFileException ignored) {
inventories.clear();
} catch (Exception ex) {
ex.printStackTrace();
inventories.clear();
}
}
private void read(byte[] bytes) throws IOException {
PacketBuffer in = new PacketBuffer(Unpooled.wrappedBuffer(bytes));
int chests = in.readInt();
for (int i = 0; i < chests; i++) {
int x = in.readInt();
int y = in.readInt();
int z = in.readInt();
RememberedInventory rem = new RememberedInventory();
rem.items.addAll(readItemStacks(in));
rem.size = rem.items.size();
rem.windowId = -1;
if (rem.items.isEmpty()) {
continue; // this only happens if the list has no elements, not if the list has elements that are all empty item stacks
}
inventories.put(new BlockPos(x, y, z), rem);
}
}
public synchronized void save() throws IOException {
if (!Baritone.settings().containerMemory.value) {
return;
}
ByteBuf buf = Unpooled.buffer(0, Integer.MAX_VALUE);
PacketBuffer out = new PacketBuffer(buf);
out.writeInt(inventories.size());
for (Map.Entry<BlockPos, RememberedInventory> entry : inventories.entrySet()) {
out = new PacketBuffer(out.writeInt(entry.getKey().getX()));
out = new PacketBuffer(out.writeInt(entry.getKey().getY()));
out = new PacketBuffer(out.writeInt(entry.getKey().getZ()));
out = writeItemStacks(entry.getValue().getContents(), out);
}
Files.write(saveTo, out.array());
}
public synchronized void setup(BlockPos pos, int windowId, int slotCount) {
RememberedInventory inventory = inventories.computeIfAbsent(pos, x -> new RememberedInventory());
inventory.windowId = windowId;
inventory.size = slotCount;
}
public synchronized Optional<RememberedInventory> getInventoryFromWindow(int windowId) {
return inventories.values().stream().filter(i -> i.windowId == windowId).findFirst();
}
@Override
public final synchronized RememberedInventory getInventoryByPos(BlockPos pos) {
return inventories.get(pos);
}
@Override
public final synchronized Map<BlockPos, IRememberedInventory> getRememberedInventories() {
// make a copy since this map is modified from the packet thread
return new HashMap<>(inventories);
}
public static List<ItemStack> readItemStacks(byte[] bytes) throws IOException {
PacketBuffer in = new PacketBuffer(Unpooled.wrappedBuffer(bytes));
return readItemStacks(in);
}
public static List<ItemStack> readItemStacks(PacketBuffer in) throws IOException {
int count = in.readInt();
List<ItemStack> result = new ArrayList<>();
for (int i = 0; i < count; i++) {
result.add(in.readItemStack());
}
return result;
}
public static byte[] writeItemStacks(List<ItemStack> write) {
ByteBuf buf = Unpooled.buffer(0, Integer.MAX_VALUE);
PacketBuffer out = new PacketBuffer(buf);
out = writeItemStacks(write, out);
return out.array();
}
public static PacketBuffer writeItemStacks(List<ItemStack> write, PacketBuffer out2) {
PacketBuffer out = out2; // avoid reassigning an argument LOL
out = new PacketBuffer(out.writeInt(write.size()));
for (ItemStack stack : write) {
out = out.writeItemStack(stack);
}
return out;
}
/**
* An inventory that we are aware of.
* <p>
* Associated with a {@link BlockPos} in {@link ContainerMemory#inventories}.
*/
public static class RememberedInventory implements IRememberedInventory {
/**
* The list of items in the inventory
*/
private final List<ItemStack> items;
/**
* The last known window ID of the inventory
*/
private int windowId;
/**
* The size of the inventory
*/
private int size;
private RememberedInventory() {
this.items = new ArrayList<>();
}
@Override
public final List<ItemStack> getContents() {
return Collections.unmodifiableList(this.items);
}
@Override
public final int getSize() {
return this.size;
}
public void updateFromOpenWindow(IPlayerContext ctx) {
items.clear();
items.addAll(ctx.player().openContainer.getInventory().subList(0, size));
}
}
}

View File

@ -19,7 +19,6 @@ package baritone.cache;
import baritone.Baritone;
import baritone.api.cache.ICachedWorld;
import baritone.api.cache.IContainerMemory;
import baritone.api.cache.IWaypointCollection;
import baritone.api.cache.IWorldData;
@ -35,7 +34,6 @@ public class WorldData implements IWorldData {
public final CachedWorld cache;
private final WaypointCollection waypoints;
private final ContainerMemory containerMemory;
//public final MapData map;
public final Path directory;
public final int dimension;
@ -44,7 +42,6 @@ public class WorldData implements IWorldData {
this.directory = directory;
this.cache = new CachedWorld(directory.resolve("cache"), dimension);
this.waypoints = new WaypointCollection(directory.resolve("waypoints"));
this.containerMemory = new ContainerMemory(directory.resolve("containers"));
this.dimension = dimension;
}
@ -53,15 +50,6 @@ public class WorldData implements IWorldData {
System.out.println("Started saving the world in a new thread");
cache.save();
});
Baritone.getExecutor().execute(() -> {
System.out.println("Started saving saved containers in a new thread");
try {
containerMemory.save();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Failed to save saved containers");
}
});
}
@Override
@ -73,9 +61,4 @@ public class WorldData implements IWorldData {
public IWaypointCollection getWaypoints() {
return this.waypoints;
}
@Override
public IContainerMemory getContainerMemory() {
return this.containerMemory;
}
}

View File

@ -1,83 +0,0 @@
/*
* 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.command.defaults;
import baritone.api.IBaritone;
import baritone.api.cache.IRememberedInventory;
import baritone.api.command.Command;
import baritone.api.command.argument.IArgConsumer;
import baritone.api.command.exception.CommandException;
import baritone.api.command.exception.CommandInvalidStateException;
import baritone.api.utils.BetterBlockPos;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.ITextComponent;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
public class ChestsCommand extends Command {
public ChestsCommand(IBaritone baritone) {
super(baritone, "chests");
}
@Override
public void execute(String label, IArgConsumer args) throws CommandException {
args.requireMax(0);
Set<Map.Entry<BlockPos, IRememberedInventory>> entries =
ctx.worldData().getContainerMemory().getRememberedInventories().entrySet();
if (entries.isEmpty()) {
throw new CommandInvalidStateException("No remembered inventories");
}
for (Map.Entry<BlockPos, IRememberedInventory> entry : entries) {
// betterblockpos has censoring
BetterBlockPos pos = new BetterBlockPos(entry.getKey());
IRememberedInventory inv = entry.getValue();
logDirect(pos.toString());
for (ItemStack item : inv.getContents()) {
ITextComponent component = item.getTextComponent();
component.appendText(String.format(" x %d", item.getCount()));
logDirect(component);
}
}
}
@Override
public Stream<String> tabComplete(String label, IArgConsumer args) {
return Stream.empty();
}
@Override
public String getShortDesc() {
return "Display remembered inventories";
}
@Override
public List<String> getLongDesc() {
return Arrays.asList(
"The chests command lists remembered inventories, I guess?",
"",
"Usage:",
"> chests"
);
}
}

View File

@ -51,7 +51,6 @@ public final class DefaultCommands {
new TunnelCommand(baritone),
new RenderCommand(baritone),
new FarmCommand(baritone),
new ChestsCommand(baritone),
new FollowCommand(baritone),
new ExploreFilterCommand(baritone),
new ReloadAllCommand(baritone),