instances = baritoneLoader.iterator();
provider = instances.next();
-
- settings = new Settings();
- SettingsUtil.readAndApply(settings);
}
public static IBaritoneProvider getProvider() {
diff --git a/src/api/java/baritone/api/IBaritone.java b/src/api/java/baritone/api/IBaritone.java
index 2c892d982..64c4a2918 100644
--- a/src/api/java/baritone/api/IBaritone.java
+++ b/src/api/java/baritone/api/IBaritone.java
@@ -23,8 +23,10 @@ import baritone.api.cache.IWorldProvider;
import baritone.api.event.listener.IEventBus;
import baritone.api.pathing.calc.IPathingControlManager;
import baritone.api.process.*;
+import baritone.api.selection.ISelectionManager;
import baritone.api.utils.IInputOverrideHandler;
import baritone.api.utils.IPlayerContext;
+import baritone.api.command.manager.ICommandManager;
/**
* @author Brady
@@ -32,15 +34,6 @@ import baritone.api.utils.IPlayerContext;
*/
public interface IBaritone {
- /**
- * Call as soon as Minecraft is ready, initializes all of the processes, behaviors, etc. This will
- * only effectively be ran once, any additional calls are redundant because the initialization state
- * is saved.
- *
- * Or whenever your overeager utility client wants.
- */
- void init();
-
/**
* @return The {@link IPathingBehavior} instance
* @see IPathingBehavior
@@ -128,6 +121,18 @@ public interface IBaritone {
*/
IEventBus getGameEventHandler();
+ /**
+ * @return The {@link ISelectionManager} instance
+ * @see ISelectionManager
+ */
+ ISelectionManager getSelectionManager();
+
+ /**
+ * @return The {@link ICommandManager} instance
+ * @see ICommandManager
+ */
+ ICommandManager getCommandManager();
+
/**
* Open click
*/
diff --git a/src/api/java/baritone/api/IBaritoneProvider.java b/src/api/java/baritone/api/IBaritoneProvider.java
index 1719fcc79..2fd648d32 100644
--- a/src/api/java/baritone/api/IBaritoneProvider.java
+++ b/src/api/java/baritone/api/IBaritoneProvider.java
@@ -18,12 +18,15 @@
package baritone.api;
import baritone.api.cache.IWorldScanner;
+import baritone.api.command.Command;
+import baritone.api.command.ICommandSystem;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import java.util.List;
+import java.util.Objects;
/**
- * Provides the present {@link IBaritone} instances
+ * Provides the present {@link IBaritone} instances, as well as non-baritone instance related APIs.
*
* @author leijurv
*/
@@ -57,7 +60,7 @@ public interface IBaritoneProvider {
*/
default IBaritone getBaritoneForPlayer(ClientPlayerEntity player) {
for (IBaritone baritone : getAllBaritones()) {
- if (player.equals(baritone.getPlayerContext().player())) {
+ if (Objects.equals(player, baritone.getPlayerContext().player())) {
return baritone;
}
}
@@ -71,4 +74,12 @@ public interface IBaritoneProvider {
* @return The {@link IWorldScanner} instance.
*/
IWorldScanner getWorldScanner();
+
+ /**
+ * Returns the {@link ICommandSystem} instance. This is not bound to a specific {@link IBaritone}
+ * instance because {@link ICommandSystem} itself controls global behavior for {@link Command}s.
+ *
+ * @return The {@link ICommandSystem} instance.
+ */
+ ICommandSystem getCommandSystem();
}
diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java
index f76dda7dd..b6a8770f0 100644
--- a/src/api/java/baritone/api/Settings.java
+++ b/src/api/java/baritone/api/Settings.java
@@ -140,6 +140,13 @@ public final class Settings {
*/
public final Setting allowDiagonalDescend = new Setting<>(false);
+ /**
+ * Allow diagonal ascending
+ *
+ * Actually pretty safe, much safer than diagonal descend tbh
+ */
+ public final Setting allowDiagonalAscend = new Setting<>(false);
+
/**
* Allow mining the block directly beneath its feet
*
@@ -241,7 +248,12 @@ public final class Settings {
/**
* How many degrees to randomize the yaw every tick. Set to 0 to disable
*/
- public final Setting randomLooking = new Setting<>(2d);
+ public final Setting randomLooking113 = new Setting<>(2d);
+
+ /**
+ * How many degrees to randomize the pitch and yaw every tick. Set to 0 to disable
+ */
+ public final Setting randomLooking = new Setting<>(0.01d);
/**
* This is the big A* setting.
@@ -485,15 +497,20 @@ public final class Settings {
public final Setting chatControl = new Setting<>(true);
/**
- * A second override over chatControl to force it on
+ * Some clients like Impact try to force chatControl to off, so here's a second setting to do it anyway
*/
- public final Setting removePrefix = new Setting<>(false);
+ public final Setting chatControlAnyway = new Setting<>(false);
/**
* Render the path
*/
public final Setting renderPath = new Setting<>(true);
+ /**
+ * Render the path as a line instead of a frickin thingy
+ */
+ public final Setting renderPathAsLine = new Setting<>(false);
+
/**
* Render the goal
*/
@@ -594,10 +611,41 @@ public final class Settings {
public final Setting cachedChunksOpacity = new Setting<>(0.5f);
/**
- * Whether or not to use the "#" command prefix
+ * Whether or not to allow you to run Baritone commands with the prefix
*/
public final Setting prefixControl = new Setting<>(true);
+ /**
+ * The command prefix for chat control
+ */
+ public final Setting prefix = new Setting<>("#");
+
+ /**
+ * Use a short Baritone prefix [B] instead of [Baritone] when logging to chat
+ */
+ public final Setting shortBaritonePrefix = new Setting<>(false);
+
+ /**
+ * Echo commands to chat when they are run
+ */
+ public final Setting echoCommands = new Setting<>(true);
+
+ /**
+ * Censor coordinates in goals and block positions
+ */
+ public final Setting censorCoordinates = new Setting<>(false);
+
+ /**
+ * Censor arguments to ran commands, to hide, for example, coordinates to #goal
+ */
+ public final Setting censorRanCommands = new Setting<>(false);
+
+ /**
+ * Always prefer silk touch tools over regular tools. This will not sacrifice speed, but it will always prefer silk
+ * touch tools over other tools of the same speed. This includes always choosing ANY silk touch tool over your hand.
+ */
+ public final Setting preferSilkTouch = new Setting<>(false);
+
/**
* Don't stop walking forward when you need to break blocks in your way
*/
@@ -659,7 +707,12 @@ public final class Settings {
public final Setting exploreMaintainY = new Setting<>(64);
/**
- * Replant nether wart while farming
+ * Replant normal Crops while farming and leave cactus and sugarcane to regrow
+ */
+ public final Setting replantCrops = new Setting<>(true);
+
+ /**
+ * Replant nether wart while farming. This setting only has an effect when replantCrops is also enabled
*/
public final Setting replantNetherWart = new Setting<>(false);
@@ -721,11 +774,45 @@ public final class Settings {
*/
public final Setting breakCorrectBlockPenaltyMultiplier = new Setting<>(10d);
+ /**
+ * When this setting is true, build a schematic with the highest X coordinate being the origin, instead of the lowest
+ */
+ public final Setting schematicOrientationX = new Setting<>(false);
+
+ /**
+ * When this setting is true, build a schematic with the highest Y coordinate being the origin, instead of the lowest
+ */
+ public final Setting schematicOrientationY = new Setting<>(false);
+
+ /**
+ * When this setting is true, build a schematic with the highest Z coordinate being the origin, instead of the lowest
+ */
+ public final Setting schematicOrientationZ = new Setting<>(false);
+
+ /**
+ * Distance to scan every tick for updates. Expanding this beyond player reach distance (i.e. setting it to 6 or above)
+ * is only necessary in very large schematics where rescanning the whole thing is costly.
+ */
+ public final Setting builderTickScanRadius = new Setting<>(5);
+
/**
* While mining, should it also consider dropped items of the correct type as a pathing destination (as well as ore blocks)?
*/
public final Setting mineScanDroppedItems = new Setting<>(true);
+ /**
+ * While mining, wait this number of milliseconds after mining an ore to see if it will drop an item
+ * instead of immediately going onto the next one
+ *
+ * Thanks Louca
+ */
+ public final Setting mineDropLoiterDurationMSThanksLouca = new Setting<>(250L);
+
+ /**
+ * Trim incorrect positions too far away, helps performance but hurts reliability in very large schematics
+ */
+ public final Setting distanceTrim = new Setting<>(true);
+
/**
* Cancel the current path if the goal has changed, and the path originally ended in the goal but doesn't anymore.
*
@@ -886,6 +973,51 @@ public final class Settings {
*/
public final Setting colorGoalBox = new Setting<>(Color.GREEN);
+ /**
+ * The color of the goal box when it's inverted
+ */
+ public final Setting colorInvertedGoalBox = new Setting<>(Color.RED);
+
+ /**
+ * The color of all selections
+ */
+ public final Setting colorSelection = new Setting<>(Color.CYAN);
+
+ /**
+ * The color of the selection pos 1
+ */
+ public final Setting colorSelectionPos1 = new Setting<>(Color.BLACK);
+
+ /**
+ * The color of the selection pos 2
+ */
+ public final Setting colorSelectionPos2 = new Setting<>(Color.ORANGE);
+
+ /**
+ * The opacity of the selection. 0 is completely transparent, 1 is completely opaque
+ */
+ public final Setting selectionOpacity = new Setting<>(.5f);
+
+ /**
+ * Line width of the goal when rendered, in pixels
+ */
+ public final Setting selectionLineWidth = new Setting<>(2F);
+
+ /**
+ * Render selections
+ */
+ public final Setting renderSelection = new Setting<>(true);
+
+ /**
+ * Ignore depth when rendering selections
+ */
+ public final Setting renderSelectionIgnoreDepth = new Setting<>(true);
+
+ /**
+ * Render selection corners
+ */
+ public final Setting renderSelectionCorners = new Setting<>(true);
+
/**
* A map of lowercase setting field names to their respective setting
@@ -900,6 +1032,7 @@ public final class Settings {
public final Map, Type> settingTypes;
public final class Setting {
+
public T value;
public final T defaultValue;
private String name;
@@ -982,10 +1115,10 @@ public final class Settings {
}
@SuppressWarnings("unchecked")
- public List> getAllValuesByType(Class klass) {
+ public List> getAllValuesByType(Class cla$$) {
List> result = new ArrayList<>();
for (Setting> setting : allSettings) {
- if (setting.getValueClass().equals(klass)) {
+ if (setting.getValueClass().equals(cla$$)) {
result.add((Setting) setting);
}
}
diff --git a/src/api/java/baritone/api/accessor/IGuiScreen.java b/src/api/java/baritone/api/accessor/IGuiScreen.java
new file mode 100644
index 000000000..ba01e2755
--- /dev/null
+++ b/src/api/java/baritone/api/accessor/IGuiScreen.java
@@ -0,0 +1,25 @@
+/*
+ * 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.accessor;
+
+import java.net.URI;
+
+public interface IGuiScreen {
+
+ void openLink(URI url);
+}
diff --git a/src/api/java/baritone/api/accessor/IItemStack.java b/src/api/java/baritone/api/accessor/IItemStack.java
new file mode 100644
index 000000000..480c713fa
--- /dev/null
+++ b/src/api/java/baritone/api/accessor/IItemStack.java
@@ -0,0 +1,23 @@
+/*
+ * 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.accessor;
+
+public interface IItemStack {
+
+ int getBaritoneHash();
+}
diff --git a/src/api/java/baritone/api/behavior/IPathingBehavior.java b/src/api/java/baritone/api/behavior/IPathingBehavior.java
index 157774489..5444bb838 100644
--- a/src/api/java/baritone/api/behavior/IPathingBehavior.java
+++ b/src/api/java/baritone/api/behavior/IPathingBehavior.java
@@ -64,9 +64,17 @@ public interface IPathingBehavior extends IBehavior {
Goal getGoal();
/**
- * @return Whether or not a path is currently being executed.
+ * @return Whether or not a path is currently being executed. This will be false if there's currently a pause.
+ * @see #hasPath()
*/
- default boolean isPathing() {
+ boolean isPathing();
+
+ /**
+ * @return If there is a current path. Note that the path is not necessarily being executed, for example when there
+ * is a pause in effect.
+ * @see #isPathing()
+ */
+ default boolean hasPath() {
return getCurrent() != null;
}
diff --git a/src/api/java/baritone/api/cache/IWaypoint.java b/src/api/java/baritone/api/cache/IWaypoint.java
index 01df2a48b..ff2350993 100644
--- a/src/api/java/baritone/api/cache/IWaypoint.java
+++ b/src/api/java/baritone/api/cache/IWaypoint.java
@@ -17,12 +17,9 @@
package baritone.api.cache;
-import net.minecraft.util.math.BlockPos;
-import org.apache.commons.lang3.ArrayUtils;
+import baritone.api.utils.BetterBlockPos;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
/**
* A marker for a position in the world.
@@ -60,7 +57,7 @@ public interface IWaypoint {
*
* @return The block position of this waypoint
*/
- BlockPos getLocation();
+ BetterBlockPos getLocation();
enum Tag {
@@ -92,20 +89,48 @@ public interface IWaypoint {
/**
* The names for the tag, anything that the tag can be referred to as.
*/
- private final String[] names;
+ public final String[] names;
Tag(String... names) {
this.names = names;
}
/**
- * Finds a tag from one of the names that could be used to identify said tag.
- *
- * @param name The name of the tag
- * @return The tag, if one is found, otherwise, {@code null}
+ * @return A name that can be passed to {@link #getByName(String)} to retrieve this tag
*/
- public static Tag fromString(String name) {
- return TAG_LIST.stream().filter(tag -> ArrayUtils.contains(tag.names, name.toLowerCase())).findFirst().orElse(null);
+ public String getName() {
+ return names[0];
+ }
+
+ /**
+ * Gets a tag by one of its names.
+ *
+ * @param name The name to search for.
+ * @return The tag, if found, or null.
+ */
+ public static Tag getByName(String name) {
+ for (Tag action : Tag.values()) {
+ for (String alias : action.names) {
+ if (alias.equalsIgnoreCase(name)) {
+ return action;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @return All tag names.
+ */
+ public static String[] getAllNames() {
+ Set names = new HashSet<>();
+
+ for (Tag tag : Tag.values()) {
+ names.addAll(Arrays.asList(tag.names));
+ }
+
+ return names.toArray(new String[0]);
}
}
}
diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java
index caa44cbc6..325d4bd0e 100644
--- a/src/api/java/baritone/api/cache/IWorldScanner.java
+++ b/src/api/java/baritone/api/cache/IWorldScanner.java
@@ -17,6 +17,7 @@
package baritone.api.cache;
+import baritone.api.utils.BlockOptionalMetaLookup;
import baritone.api.utils.IPlayerContext;
import net.minecraft.block.Block;
import net.minecraft.util.math.BlockPos;
@@ -33,28 +34,53 @@ public interface IWorldScanner {
/**
* Scans the world, up to the specified max chunk radius, for the specified blocks.
*
- * @param ctx The {@link IPlayerContext} containing player and world info that the
- * scan is based upon
- * @param blocks The blocks to scan for
+ * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon
+ * @param filter The blocks to scan for
* @param max The maximum number of blocks to scan before cutoff
- * @param yLevelThreshold If a block is found within this Y level, the current result will be
- * returned, if the value is negative, then this condition doesn't apply.
+ * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value
+ * is negative, then this condition doesn't apply.
* @param maxSearchRadius The maximum chunk search radius
* @return The matching block positions
*/
- List scanChunkRadius(IPlayerContext ctx, List blocks, int max, int yLevelThreshold, int maxSearchRadius);
+ List scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius);
+
+ default List scanChunkRadius(IPlayerContext ctx, List filter, int max, int yLevelThreshold, int maxSearchRadius) {
+ return scanChunkRadius(ctx, new BlockOptionalMetaLookup(filter.toArray(new Block[0])), max, yLevelThreshold, maxSearchRadius);
+ }
/**
* Scans a single chunk for the specified blocks.
*
- * @param ctx The {@link IPlayerContext} containing player and world info that the
- * scan is based upon
+ * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon
+ * @param filter The blocks to scan for
+ * @param pos The position of the target chunk
+ * @param max The maximum number of blocks to scan before cutoff
+ * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value
+ * is negative, then this condition doesn't apply.
+ * @return The matching block positions
+ */
+ List scanChunk(IPlayerContext ctx, BlockOptionalMetaLookup filter, ChunkPos pos, int max, int yLevelThreshold);
+
+ /**
+ * Scans a single chunk for the specified blocks.
+ *
+ * @param ctx The {@link IPlayerContext} containing player and world info that the scan is based upon
* @param blocks The blocks to scan for
* @param pos The position of the target chunk
* @param max The maximum number of blocks to scan before cutoff
- * @param yLevelThreshold If a block is found within this Y level, the current result will be
- * returned, if the value is negative, then this condition doesn't apply.
+ * @param yLevelThreshold If a block is found within this Y level, the current result will be returned, if the value
+ * is negative, then this condition doesn't apply.
* @return The matching block positions
*/
- List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold);
+ default List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold) {
+ return scanChunk(ctx, new BlockOptionalMetaLookup(blocks), pos, max, yLevelThreshold);
+ }
+
+ /**
+ * Repacks 40 chunks around the player.
+ *
+ * @param ctx The player context for that player.
+ * @return The number of chunks queued for repacking.
+ */
+ int repack(IPlayerContext ctx);
}
diff --git a/src/api/java/baritone/api/cache/Waypoint.java b/src/api/java/baritone/api/cache/Waypoint.java
index 2b9a7232b..9c4fbfbfc 100644
--- a/src/api/java/baritone/api/cache/Waypoint.java
+++ b/src/api/java/baritone/api/cache/Waypoint.java
@@ -17,7 +17,7 @@
package baritone.api.cache;
-import net.minecraft.util.math.BlockPos;
+import baritone.api.utils.BetterBlockPos;
import java.util.Date;
@@ -31,9 +31,9 @@ public class Waypoint implements IWaypoint {
private final String name;
private final Tag tag;
private final long creationTimestamp;
- private final BlockPos location;
+ private final BetterBlockPos location;
- public Waypoint(String name, Tag tag, BlockPos location) {
+ public Waypoint(String name, Tag tag, BetterBlockPos location) {
this(name, tag, location, System.currentTimeMillis());
}
@@ -46,7 +46,7 @@ public class Waypoint implements IWaypoint {
* @param location The waypoint location
* @param creationTimestamp When the waypoint was created
*/
- public Waypoint(String name, Tag tag, BlockPos location, long creationTimestamp) {
+ public Waypoint(String name, Tag tag, BetterBlockPos location, long creationTimestamp) {
this.name = name;
this.tag = tag;
this.location = location;
@@ -55,7 +55,7 @@ public class Waypoint implements IWaypoint {
@Override
public int hashCode() {
- return name.hashCode() + tag.hashCode() + location.hashCode(); //lol
+ return name.hashCode() ^ tag.hashCode() ^ location.hashCode() ^ Long.hashCode(creationTimestamp);
}
@Override
@@ -74,13 +74,18 @@ public class Waypoint implements IWaypoint {
}
@Override
- public BlockPos getLocation() {
+ public BetterBlockPos getLocation() {
return this.location;
}
@Override
public String toString() {
- return name + " " + location.toString() + " " + new Date(creationTimestamp).toString();
+ return String.format(
+ "%s %s %s",
+ name,
+ BetterBlockPos.from(location).toString(),
+ new Date(creationTimestamp).toString()
+ );
}
@Override
diff --git a/src/api/java/baritone/api/command/Command.java b/src/api/java/baritone/api/command/Command.java
new file mode 100644
index 000000000..cd8fa5de3
--- /dev/null
+++ b/src/api/java/baritone/api/command/Command.java
@@ -0,0 +1,86 @@
+/*
+ * 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.command;
+
+import baritone.api.IBaritone;
+import baritone.api.utils.Helper;
+import baritone.api.utils.IPlayerContext;
+import baritone.api.command.exception.CommandException;
+import baritone.api.command.helpers.arguments.IArgConsumer;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public abstract class Command implements Helper {
+
+ protected IBaritone baritone;
+ protected IPlayerContext ctx;
+
+ /**
+ * The names of this command. This is what you put after the command prefix.
+ */
+ protected final List names;
+
+ /**
+ * Creates a new Baritone control command.
+ *
+ * @param names The names of this command. This is what you put after the command prefix.
+ */
+ protected Command(IBaritone baritone, String... names) {
+ this.names = Collections.unmodifiableList(Stream.of(names)
+ .map(s -> s.toLowerCase(Locale.US))
+ .collect(Collectors.toList()));
+ this.baritone = baritone;
+ this.ctx = baritone.getPlayerContext();
+ }
+
+ /**
+ * Called when this command is executed.
+ */
+ public abstract void execute(String label, IArgConsumer args) throws CommandException;
+
+ /**
+ * Called when the command needs to tab complete. Return a Stream representing the entries to put in the completions
+ * list.
+ */
+ public abstract Stream tabComplete(String label, IArgConsumer args) throws CommandException;
+
+ /**
+ * @return A single-line string containing a short description of this command's purpose.
+ */
+ public abstract String getShortDesc();
+
+ /**
+ * @return A list of lines that will be printed by the help command when the user wishes to view them.
+ */
+ public abstract List getLongDesc();
+
+ /**
+ * @return {@code true} if this command should be hidden from the help menu
+ */
+ public boolean hiddenFromHelp() {
+ return false;
+ }
+
+ public final List getNames() {
+ return this.names;
+ }
+}
diff --git a/src/api/java/baritone/api/command/IBaritoneChatControl.java b/src/api/java/baritone/api/command/IBaritoneChatControl.java
new file mode 100644
index 000000000..5009f3f02
--- /dev/null
+++ b/src/api/java/baritone/api/command/IBaritoneChatControl.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.command;
+
+import baritone.api.Settings;
+
+import java.util.UUID;
+
+/**
+ * @author Brady
+ * @since 9/26/2019
+ */
+public interface IBaritoneChatControl {
+
+ /**
+ * In certain cases chat components need to execute commands for you. For example, the paginator automatically runs
+ * commands when you click the forward and back arrows to show you the previous/next page.
+ *
+ * If the prefix is changed in the meantime, then the command will go to chat. That's no good. So here's a permanent
+ * prefix that forces a command to run, regardless of the current prefix, chat/prefix control being enabled, etc.
+ *
+ * If used right (by both developers and users), it should be impossible to expose a command accidentally to the
+ * server. As a rule of thumb, if you have a clickable chat component, always use this prefix. If you're suggesting
+ * a command (a component that puts text into your text box, or something else), use {@link Settings#prefix}.
+ */
+ String FORCE_COMMAND_PREFIX = String.format("<<%s>>", UUID.randomUUID().toString());
+}
diff --git a/src/api/java/baritone/api/command/ICommandSystem.java b/src/api/java/baritone/api/command/ICommandSystem.java
new file mode 100644
index 000000000..98e8ed9c6
--- /dev/null
+++ b/src/api/java/baritone/api/command/ICommandSystem.java
@@ -0,0 +1,29 @@
+/*
+ * 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.command;
+
+import baritone.api.command.argparser.IArgParserManager;
+
+/**
+ * @author Brady
+ * @since 10/4/2019
+ */
+public interface ICommandSystem {
+
+ IArgParserManager getParserManager();
+}
diff --git a/src/api/java/baritone/api/command/argparser/IArgParser.java b/src/api/java/baritone/api/command/argparser/IArgParser.java
new file mode 100644
index 000000000..868ad6963
--- /dev/null
+++ b/src/api/java/baritone/api/command/argparser/IArgParser.java
@@ -0,0 +1,60 @@
+/*
+ * 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.command.argparser;
+
+import baritone.api.command.argument.ICommandArgument;
+
+public interface IArgParser {
+
+ /**
+ * @return the class of this parser.
+ */
+ Class getTarget();
+
+ /**
+ * A stateless argument parser is just that. It takes a {@link ICommandArgument} and outputs its type.
+ */
+ interface Stateless extends IArgParser {
+
+ /**
+ * @param arg The argument to parse.
+ * @return What it was parsed into.
+ * @throws RuntimeException if you want the parsing to fail. The exception will be caught and turned into an
+ * appropriate error.
+ */
+ T parseArg(ICommandArgument arg) throws Exception;
+ }
+
+ /**
+ * A stated argument parser is similar to a stateless one. It also takes a {@link ICommandArgument}, but it also
+ * takes a second argument that can be any type, referred to as the state.
+ */
+ interface Stated extends IArgParser {
+
+ Class getStateType();
+
+ /**
+ * @param arg The argument to parse.
+ * @param state Can be anything.
+ * @return What it was parsed into.
+ * @throws RuntimeException if you want the parsing to fail. The exception will be caught and turned into an
+ * appropriate error.
+ */
+ T parseArg(ICommandArgument arg, S state) throws Exception;
+ }
+}
diff --git a/src/api/java/baritone/api/command/argparser/IArgParserManager.java b/src/api/java/baritone/api/command/argparser/IArgParserManager.java
new file mode 100644
index 000000000..fe819150a
--- /dev/null
+++ b/src/api/java/baritone/api/command/argparser/IArgParserManager.java
@@ -0,0 +1,65 @@
+/*
+ * 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.command.argparser;
+
+import baritone.api.command.argument.ICommandArgument;
+import baritone.api.command.exception.CommandInvalidTypeException;
+import baritone.api.command.registry.Registry;
+
+/**
+ * @author Brady
+ * @since 10/4/2019
+ */
+public interface IArgParserManager {
+
+ /**
+ * @param type The type trying to be parsed
+ * @return A parser that can parse arguments into this class, if found.
+ */
+ IArgParser.Stateless getParserStateless(Class type);
+
+ /**
+ * @param type The type trying to be parsed
+ * @return A parser that can parse arguments into this class, if found.
+ */
+ IArgParser.Stated getParserStated(Class type, Class stateKlass);
+
+ /**
+ * Attempt to parse the specified argument with a stateless {@link IArgParser} that outputs the specified class.
+ *
+ * @param type The type to try and parse the argument into.
+ * @param arg The argument to parse.
+ * @return An instance of the specified class.
+ * @throws CommandInvalidTypeException If the parsing failed
+ */
+ T parseStateless(Class type, ICommandArgument arg) throws CommandInvalidTypeException;
+
+ /**
+ * Attempt to parse the specified argument with a stated {@link IArgParser} that outputs the specified class.
+ *
+ * @param type The type to try and parse the argument into.
+ * @param arg The argument to parse.
+ * @param state The state to pass to the {@link IArgParser.Stated}.
+ * @return An instance of the specified class.
+ * @throws CommandInvalidTypeException If the parsing failed
+ * @see IArgParser.Stated
+ */
+ T parseStated(Class type, Class stateKlass, ICommandArgument arg, S state) throws CommandInvalidTypeException;
+
+ Registry getRegistry();
+}
diff --git a/src/api/java/baritone/api/command/argument/ICommandArgument.java b/src/api/java/baritone/api/command/argument/ICommandArgument.java
new file mode 100644
index 000000000..fb5a45770
--- /dev/null
+++ b/src/api/java/baritone/api/command/argument/ICommandArgument.java
@@ -0,0 +1,102 @@
+/*
+ * 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.command.argument;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.command.argparser.IArgParser;
+import baritone.api.command.exception.CommandInvalidTypeException;
+import net.minecraft.util.Direction;
+
+/**
+ * A {@link ICommandArgument} is an immutable object representing one command argument. It contains data on the index of
+ * that argument, its value, and the rest of the string that argument was found in
+ *
+ * You're recommended to use {@link IArgConsumer}}s to handle these.
+ *
+ * @author Brady
+ * @since 10/2/2019
+ */
+public interface ICommandArgument {
+
+ /**
+ * @return The index of this command argument in the list of command arguments generated
+ */
+ int getIndex();
+
+ /**
+ * @return The raw value of just this argument
+ */
+ String getValue();
+
+ /**
+ * @return The raw value of the remaining arguments after this one was captured
+ */
+ String getRawRest();
+
+ /**
+ * Gets an enum value from the enum class with the same name as this argument's value
+ *
+ * For example if you getEnum as an {@link Direction}, and this argument's value is "up", it will return {@link
+ * Direction#UP}
+ *
+ * @param enumClass The enum class to search
+ * @return An enum constant of that class with the same name as this argument's value
+ * @throws CommandInvalidTypeException If the constant couldn't be found
+ * @see IArgConsumer#peekEnum(Class)
+ * @see IArgConsumer#peekEnum(Class, int)
+ * @see IArgConsumer#peekEnumOrNull(Class)
+ * @see IArgConsumer#peekEnumOrNull(Class, int)
+ * @see IArgConsumer#getEnum(Class)
+ * @see IArgConsumer#getEnumOrNull(Class)
+ */
+ > E getEnum(Class enumClass) throws CommandInvalidTypeException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse this argument into the specified class
+ *
+ * @param type The class to parse this argument into
+ * @return An instance of the specified type
+ * @throws CommandInvalidTypeException If the parsing failed
+ */
+ T getAs(Class type) throws CommandInvalidTypeException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse this argument into the specified class
+ *
+ * @param type The class to parse this argument into
+ * @return If the parser succeeded
+ */
+ boolean is(Class type);
+
+ /**
+ * Tries to use a stated {@link IArgParser} to parse this argument into the specified class
+ *
+ * @param type The class to parse this argument into
+ * @return An instance of the specified type
+ * @throws CommandInvalidTypeException If the parsing failed
+ */
+ T getAs(Class type, Class stateType, S state) throws CommandInvalidTypeException;
+
+ /**
+ * Tries to use a stated {@link IArgParser} to parse this argument into the specified class
+ *
+ * @param type The class to parse this argument into
+ * @return If the parser succeeded
+ */
+ boolean is(Class type, Class stateType, S state);
+}
diff --git a/src/api/java/baritone/api/command/datatypes/BlockById.java b/src/api/java/baritone/api/command/datatypes/BlockById.java
new file mode 100644
index 000000000..150eb004f
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/BlockById.java
@@ -0,0 +1,53 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.exception.CommandException;
+import baritone.api.command.helpers.tabcomplete.TabCompleteHelper;
+import net.minecraft.block.Block;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.registry.Registry;
+
+import java.util.stream.Stream;
+
+public enum BlockById implements IDatatypeFor {
+ INSTANCE;
+
+ @Override
+ public Block get(IDatatypeContext ctx) throws CommandException {
+ ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString());
+ Block block;
+ if ((block = Registry.BLOCK.getValue(id).orElse(null)) == null) {
+ throw new IllegalArgumentException("no block found by that id");
+ }
+ return block;
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ return new TabCompleteHelper()
+ .append(
+ Registry.BLOCK.keySet()
+ .stream()
+ .map(Object::toString)
+ )
+ .filterPrefixNamespaced(ctx.getConsumer().getString())
+ .sortAlphabetically()
+ .stream();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/EntityClassById.java b/src/api/java/baritone/api/command/datatypes/EntityClassById.java
new file mode 100644
index 000000000..b0d9bc08f
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/EntityClassById.java
@@ -0,0 +1,49 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.exception.CommandException;
+import baritone.api.command.helpers.tabcomplete.TabCompleteHelper;
+import net.minecraft.entity.EntityType;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.registry.Registry;
+
+import java.util.stream.Stream;
+
+public enum EntityClassById implements IDatatypeFor {
+ INSTANCE;
+
+ @Override
+ public EntityType get(IDatatypeContext ctx) throws CommandException {
+ ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString());
+ EntityType entity;
+ if ((entity = Registry.ENTITY_TYPE.getValue(id).orElse(null)) == null) {
+ throw new IllegalArgumentException("no entity found by that id");
+ }
+ return entity;
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ return new TabCompleteHelper()
+ .append(Registry.ENTITY_TYPE.stream().map(Object::toString))
+ .filterPrefixNamespaced(ctx.getConsumer().getString())
+ .sortAlphabetically()
+ .stream();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java b/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java
new file mode 100644
index 000000000..29dc5f0b7
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/ForBlockOptionalMeta.java
@@ -0,0 +1,37 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.utils.BlockOptionalMeta;
+import baritone.api.command.exception.CommandException;
+
+import java.util.stream.Stream;
+
+public enum ForBlockOptionalMeta implements IDatatypeFor {
+ INSTANCE;
+
+ @Override
+ public BlockOptionalMeta get(IDatatypeContext ctx) throws CommandException {
+ return new BlockOptionalMeta(ctx.getConsumer().getString());
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) {
+ return ctx.getConsumer().tabCompleteDatatype(BlockById.INSTANCE);
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/ForDirection.java b/src/api/java/baritone/api/command/datatypes/ForDirection.java
new file mode 100644
index 000000000..9bb6f3683
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/ForDirection.java
@@ -0,0 +1,43 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.tabcomplete.TabCompleteHelper;
+import baritone.api.command.exception.CommandException;
+import net.minecraft.util.Direction;
+
+import java.util.Locale;
+import java.util.stream.Stream;
+
+public enum ForDirection implements IDatatypeFor {
+ INSTANCE;
+
+ @Override
+ public Direction get(IDatatypeContext ctx) throws CommandException {
+ return Direction.valueOf(ctx.getConsumer().getString().toUpperCase(Locale.US));
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ return new TabCompleteHelper()
+ .append(Stream.of(Direction.values())
+ .map(Direction::getName).map(String::toLowerCase))
+ .filterPrefix(ctx.getConsumer().getString())
+ .stream();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/ForWaypoints.java b/src/api/java/baritone/api/command/datatypes/ForWaypoints.java
new file mode 100644
index 000000000..db5068392
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/ForWaypoints.java
@@ -0,0 +1,81 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.IBaritone;
+import baritone.api.cache.IWaypoint;
+import baritone.api.cache.IWaypointCollection;
+import baritone.api.command.helpers.tabcomplete.TabCompleteHelper;
+import baritone.api.command.exception.CommandException;
+
+import java.util.Comparator;
+import java.util.stream.Stream;
+
+public enum ForWaypoints implements IDatatypeFor {
+ INSTANCE;
+
+ @Override
+ public IWaypoint[] get(IDatatypeContext ctx) throws CommandException {
+ final String input = ctx.getConsumer().getString();
+ final IWaypoint.Tag tag = IWaypoint.Tag.getByName(input);
+
+ // If the input doesn't resolve to a valid tag, resolve by name
+ return tag == null
+ ? getWaypointsByName(ctx.getBaritone(), input)
+ : getWaypointsByTag(ctx.getBaritone(), tag);
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ return new TabCompleteHelper()
+ .append(getWaypointNames(ctx.getBaritone()))
+ .sortAlphabetically()
+ .prepend(IWaypoint.Tag.getAllNames())
+ .filterPrefix(ctx.getConsumer().getString())
+ .stream();
+ }
+
+ public static IWaypointCollection waypoints(IBaritone baritone) {
+ return baritone.getWorldProvider().getCurrentWorld().getWaypoints();
+ }
+
+ public static IWaypoint[] getWaypoints(IBaritone baritone) {
+ return waypoints(baritone).getAllWaypoints().stream()
+ .sorted(Comparator.comparingLong(IWaypoint::getCreationTimestamp).reversed())
+ .toArray(IWaypoint[]::new);
+ }
+
+ public static String[] getWaypointNames(IBaritone baritone) {
+ return Stream.of(getWaypoints(baritone))
+ .map(IWaypoint::getName)
+ .filter(name -> !name.isEmpty())
+ .toArray(String[]::new);
+ }
+
+ public static IWaypoint[] getWaypointsByTag(IBaritone baritone, IWaypoint.Tag tag) {
+ return waypoints(baritone).getByTag(tag).stream()
+ .sorted(Comparator.comparingLong(IWaypoint::getCreationTimestamp).reversed())
+ .toArray(IWaypoint[]::new);
+ }
+
+ public static IWaypoint[] getWaypointsByName(IBaritone baritone, String name) {
+ return Stream.of(getWaypoints(baritone))
+ .filter(waypoint -> waypoint.getName().equalsIgnoreCase(name))
+ .toArray(IWaypoint[]::new);
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/IDatatype.java b/src/api/java/baritone/api/command/datatypes/IDatatype.java
new file mode 100644
index 000000000..385154bc4
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/IDatatype.java
@@ -0,0 +1,56 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.argparser.IArgParser;
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.command.exception.CommandException;
+
+import java.util.stream.Stream;
+
+/**
+ * An {@link IDatatype} is similar to an {@link IArgParser} in the sense that it is capable of consuming an argument
+ * to transform it into a usable form as the code desires.
+ *
+ * A fundamental difference is that an {@link IDatatype} is capable of consuming multiple arguments. For example,
+ * {@link RelativeBlockPos} is an {@link IDatatypePost} which requires at least 3 {@link RelativeCoordinate} arguments
+ * to be specified.
+ *
+ * Another difference is that an {@link IDatatype} can be tab-completed, providing comprehensive auto completion
+ * that can substitute based on existing input or provide possibilities for the next piece of input.
+ *
+ * @see IDatatypeContext
+ * @see IDatatypeFor
+ * @see IDatatypePost
+ */
+public interface IDatatype {
+
+ /**
+ * Attempts to complete missing or partial input provided through the {@link IArgConsumer}} provided by
+ * {@link IDatatypeContext#getConsumer()} in order to aide the user in executing commands.
+ *
+ * One benefit over datatypes over {@link IArgParser}s is that instead of each command trying to guess what values
+ * the datatype will accept, or simply not tab completing at all, datatypes that support tab completion can provide
+ * accurate information using the same methods used to parse arguments in the first place.
+ *
+ * @param ctx The argument consumer to tab complete
+ * @return A stream representing the strings that can be tab completed. DO NOT INCLUDE SPACES IN ANY STRINGS.
+ * @see IArgConsumer#tabCompleteDatatype(IDatatype)
+ */
+ Stream tabComplete(IDatatypeContext ctx) throws CommandException;
+}
diff --git a/src/api/java/baritone/api/command/datatypes/IDatatypeContext.java b/src/api/java/baritone/api/command/datatypes/IDatatypeContext.java
new file mode 100644
index 000000000..4b8269c3c
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/IDatatypeContext.java
@@ -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 .
+ */
+
+package baritone.api.command.datatypes;
+
+import baritone.api.IBaritone;
+import baritone.api.command.helpers.arguments.IArgConsumer;
+
+/**
+ * Provides an {@link IDatatype} with contextual information so
+ * that it can perform the desired operation on the target level.
+ *
+ * @see IDatatype
+ *
+ * @author Brady
+ * @since 9/26/2019
+ */
+public interface IDatatypeContext {
+
+ /**
+ * Provides the {@link IBaritone} instance that is associated with the action relating to datatype handling.
+ *
+ * @return The context {@link IBaritone} instance.
+ */
+ IBaritone getBaritone();
+
+ /**
+ * Provides the {@link IArgConsumer}} to fetch input information from.
+ *
+ * @return The context {@link IArgConsumer}}.
+ */
+ IArgConsumer getConsumer();
+}
diff --git a/src/api/java/baritone/api/command/datatypes/IDatatypeFor.java b/src/api/java/baritone/api/command/datatypes/IDatatypeFor.java
new file mode 100644
index 000000000..2f0a9c140
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/IDatatypeFor.java
@@ -0,0 +1,44 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.exception.CommandException;
+
+import java.util.function.Supplier;
+
+/**
+ * An {@link IDatatype} which acts as a {@link Supplier}, in essence. The only difference
+ * is that it requires an {@link IDatatypeContext} to be provided due to the expectation that
+ * implementations of {@link IDatatype} are singletons.
+ */
+public interface IDatatypeFor extends IDatatype {
+
+ /**
+ * Consumes the desired amount of arguments from the specified {@link IDatatypeContext}, and
+ * then returns the parsed value. This method will more than likely return a {@link IllegalArgumentException}
+ * if the expected input does not conform to a parseable value. As far as a {@link CommandException} being
+ * thrown is concerned, see the note below for specifics.
+ *
+ * @see IDatatypeContext
+ *
+ * @param ctx The context
+ * @return The parsed data-type
+ * @throws CommandException If there was an issue parsing using another type or arguments could not be polled.
+ */
+ T get(IDatatypeContext ctx) throws CommandException;
+}
diff --git a/src/api/java/baritone/api/command/datatypes/IDatatypePost.java b/src/api/java/baritone/api/command/datatypes/IDatatypePost.java
new file mode 100644
index 000000000..aa5b261da
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/IDatatypePost.java
@@ -0,0 +1,41 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.exception.CommandException;
+
+import java.util.function.Function;
+
+/**
+ * An {@link IDatatype} which acts as a {@link Function}, in essence. The only difference
+ * is that it requires an {@link IDatatypeContext} to be provided due to the expectation that
+ * implementations of {@link IDatatype} are singletons.
+ */
+public interface IDatatypePost extends IDatatype {
+
+ /**
+ * Takes the expected input and transforms it based on the value held by {@code original}. If {@code original}
+ * is null, it is expected that the implementation of this method has a case to handle it, such that a
+ * {@link NullPointerException} will never be thrown as a result.
+ *
+ * @param ctx The datatype context
+ * @param original The transformable value
+ * @return The transformed value
+ */
+ T apply(IDatatypeContext ctx, O original) throws CommandException;
+}
diff --git a/src/api/java/baritone/api/command/datatypes/IDatatypePostFunction.java b/src/api/java/baritone/api/command/datatypes/IDatatypePostFunction.java
new file mode 100644
index 000000000..fe79d6a6d
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/IDatatypePostFunction.java
@@ -0,0 +1,29 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.exception.CommandException;
+
+/**
+ * @author Brady
+ * @since 9/26/2019
+ */
+public interface IDatatypePostFunction {
+
+ T apply(O original) throws CommandException;
+}
diff --git a/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java b/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java
new file mode 100644
index 000000000..4aff75c41
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/NearbyPlayer.java
@@ -0,0 +1,56 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.IBaritone;
+import baritone.api.command.exception.CommandException;
+import baritone.api.command.helpers.tabcomplete.TabCompleteHelper;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.util.text.ITextComponent;
+
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * An {@link IDatatype} used to resolve nearby players, those within
+ * render distance of the target {@link IBaritone} instance.
+ */
+public enum NearbyPlayer implements IDatatypeFor {
+ INSTANCE;
+
+ @Override
+ public PlayerEntity get(IDatatypeContext ctx) throws CommandException {
+ final String username = ctx.getConsumer().getString();
+ return getPlayers(ctx).stream()
+ .filter(s -> s.getName().getString().equalsIgnoreCase(username))
+ .findFirst().orElse(null);
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ return new TabCompleteHelper()
+ .append(getPlayers(ctx).stream().map(PlayerEntity::getName).map(ITextComponent::getString))
+ .filterPrefix(ctx.getConsumer().getString())
+ .sortAlphabetically()
+ .stream();
+ }
+
+ private static List extends PlayerEntity> getPlayers(IDatatypeContext ctx) {
+ return ctx.getBaritone().getPlayerContext().world().getPlayers();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeBlockPos.java b/src/api/java/baritone/api/command/datatypes/RelativeBlockPos.java
new file mode 100644
index 000000000..513ef41f3
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeBlockPos.java
@@ -0,0 +1,57 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.utils.BetterBlockPos;
+import baritone.api.command.exception.CommandException;
+
+import java.util.stream.Stream;
+
+public enum RelativeBlockPos implements IDatatypePost {
+ INSTANCE;
+
+ @Override
+ public BetterBlockPos apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException {
+ if (origin == null) {
+ origin = BetterBlockPos.ORIGIN;
+ }
+
+ final IArgConsumer consumer = ctx.getConsumer();
+ return new BetterBlockPos(
+ consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x),
+ consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y),
+ consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z)
+ );
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ final IArgConsumer consumer = ctx.getConsumer();
+ if (consumer.hasAny() && !consumer.has(4)) {
+ while (consumer.has(2)) {
+ if (consumer.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) == null) {
+ break;
+ }
+ consumer.get();
+ }
+ return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE);
+ }
+ return Stream.empty();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java b/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java
new file mode 100644
index 000000000..dc4d56304
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeCoordinate.java
@@ -0,0 +1,64 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.command.exception.CommandException;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+public enum RelativeCoordinate implements IDatatypePost {
+ INSTANCE;
+ private static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)([k-k]?)|)$");
+
+ @Override
+ public Double apply(IDatatypeContext ctx, Double origin) throws CommandException {
+ if (origin == null) {
+ origin = 0.0D;
+ }
+
+ Matcher matcher = PATTERN.matcher(ctx.getConsumer().getString());
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("pattern doesn't match");
+ }
+
+ boolean isRelative = !matcher.group(1).isEmpty();
+
+ double offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2).replaceAll("k", ""));
+
+ if (matcher.group(2).contains("k")) {
+ offset *= 1000;
+ }
+
+ if (isRelative) {
+ return origin + offset;
+ }
+ return offset;
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) throws CommandException {
+ final IArgConsumer consumer = ctx.getConsumer();
+ if (!consumer.has(2) && consumer.getString().matches("^(~|$)")) {
+ return Stream.of("~");
+ }
+ return Stream.empty();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeFile.java b/src/api/java/baritone/api/command/datatypes/RelativeFile.java
new file mode 100644
index 000000000..f886a1c65
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeFile.java
@@ -0,0 +1,103 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.command.exception.CommandException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.FileSystems;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Path;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import static baritone.api.utils.Helper.HELPER;
+
+public enum RelativeFile implements IDatatypePost {
+ INSTANCE;
+
+ @Override
+ public File apply(IDatatypeContext ctx, File original) throws CommandException {
+ if (original == null) {
+ original = new File("./");
+ }
+
+ Path path;
+ try {
+ path = FileSystems.getDefault().getPath(ctx.getConsumer().getString());
+ } catch (InvalidPathException e) {
+ throw new IllegalArgumentException("invalid path");
+ }
+ return getCanonicalFileUnchecked(original.toPath().resolve(path).toFile());
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) {
+ return Stream.empty();
+ }
+
+ /**
+ * Seriously
+ *
+ * @param file File
+ * @return Canonical file of file
+ * @author LoganDark
+ */
+ private static File getCanonicalFileUnchecked(File file) {
+ try {
+ return file.getCanonicalFile();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public static Stream tabComplete(IArgConsumer consumer, File base0) throws CommandException {
+ // I will not make the caller deal with this, seriously
+ // Tab complete code is beautiful and I'm not going to bloat it with dumb ass checked exception bullshit -LoganDark
+
+ // lol owned -Brady
+
+ File base = getCanonicalFileUnchecked(base0);
+ String currentPathStringThing = consumer.getString();
+ Path currentPath = FileSystems.getDefault().getPath(currentPathStringThing);
+ Path basePath = currentPath.isAbsolute() ? currentPath.getRoot() : base.toPath();
+ boolean useParent = !currentPathStringThing.isEmpty() && !currentPathStringThing.endsWith(File.separator);
+ File currentFile = currentPath.isAbsolute() ? currentPath.toFile() : new File(base, currentPathStringThing);
+ return Stream.of(Objects.requireNonNull(getCanonicalFileUnchecked(
+ useParent
+ ? currentFile.getParentFile()
+ : currentFile
+ ).listFiles()))
+ .map(f -> (currentPath.isAbsolute() ? f : basePath.relativize(f.toPath()).toString()) +
+ (f.isDirectory() ? File.separator : ""))
+ .filter(s -> s.toLowerCase(Locale.US).startsWith(currentPathStringThing.toLowerCase(Locale.US)))
+ .filter(s -> !s.contains(" "));
+ }
+
+ public static File gameDir() {
+ File gameDir = HELPER.mc.gameDir.getAbsoluteFile();
+ if (gameDir.getName().equals(".")) {
+ return gameDir.getParentFile();
+ }
+ return gameDir;
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoal.java b/src/api/java/baritone/api/command/datatypes/RelativeGoal.java
new file mode 100644
index 000000000..73bc6e0d3
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeGoal.java
@@ -0,0 +1,79 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.pathing.goals.Goal;
+import baritone.api.pathing.goals.GoalBlock;
+import baritone.api.pathing.goals.GoalXZ;
+import baritone.api.pathing.goals.GoalYLevel;
+import baritone.api.utils.BetterBlockPos;
+import baritone.api.command.exception.CommandException;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
+
+public enum RelativeGoal implements IDatatypePost {
+ INSTANCE;
+
+ @Override
+ public Goal apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException {
+ if (origin == null) {
+ origin = BetterBlockPos.ORIGIN;
+ }
+ final IArgConsumer consumer = ctx.getConsumer();
+
+ List> coords = new ArrayList<>();
+ final IArgConsumer copy = consumer.copy(); // This is a hack and should be fixed in the future probably
+ for (int i = 0; i < 3; i++) {
+ if (copy.peekDatatypeOrNull(RelativeCoordinate.INSTANCE) != null) {
+ coords.add(o -> consumer.getDatatypePost(RelativeCoordinate.INSTANCE, o));
+ copy.get(); // Consume so we actually decrement the remaining arguments
+ }
+ }
+
+ switch (coords.size()) {
+ case 0:
+ return new GoalBlock(origin);
+ case 1:
+ return new GoalYLevel(
+ MathHelper.floor(coords.get(0).apply((double) origin.y))
+ );
+ case 2:
+ return new GoalXZ(
+ MathHelper.floor(coords.get(0).apply((double) origin.x)),
+ MathHelper.floor(coords.get(1).apply((double) origin.z))
+ );
+ case 3:
+ return new GoalBlock(
+ MathHelper.floor(coords.get(0).apply((double) origin.x)),
+ MathHelper.floor(coords.get(1).apply((double) origin.y)),
+ MathHelper.floor(coords.get(2).apply((double) origin.z))
+ );
+ default:
+ throw new IllegalStateException("Unexpected coords size: " + coords.size());
+ }
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) {
+ return ctx.getConsumer().tabCompleteDatatype(RelativeCoordinate.INSTANCE);
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java b/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java
new file mode 100644
index 000000000..19621b952
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeGoalBlock.java
@@ -0,0 +1,53 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.pathing.goals.GoalBlock;
+import baritone.api.utils.BetterBlockPos;
+import baritone.api.command.exception.CommandException;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.stream.Stream;
+
+public enum RelativeGoalBlock implements IDatatypePost {
+ INSTANCE;
+
+ @Override
+ public GoalBlock apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException {
+ if (origin == null) {
+ origin = BetterBlockPos.ORIGIN;
+ }
+
+ final IArgConsumer consumer = ctx.getConsumer();
+ return new GoalBlock(
+ MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x)),
+ MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y)),
+ MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.z))
+ );
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) {
+ final IArgConsumer consumer = ctx.getConsumer();
+ if (consumer.hasAtMost(3)) {
+ return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE);
+ }
+ return Stream.empty();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java b/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java
new file mode 100644
index 000000000..83d52de86
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeGoalXZ.java
@@ -0,0 +1,52 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.pathing.goals.GoalXZ;
+import baritone.api.utils.BetterBlockPos;
+import baritone.api.command.exception.CommandException;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.stream.Stream;
+
+public enum RelativeGoalXZ implements IDatatypePost {
+ INSTANCE;
+
+ @Override
+ public GoalXZ apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException {
+ if (origin == null) {
+ origin = BetterBlockPos.ORIGIN;
+ }
+
+ final IArgConsumer consumer = ctx.getConsumer();
+ return new GoalXZ(
+ MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.x)),
+ MathHelper.floor(consumer.getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y))
+ );
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) {
+ final IArgConsumer consumer = ctx.getConsumer();
+ if (consumer.hasAtMost(2)) {
+ return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE);
+ }
+ return Stream.empty();
+ }
+}
diff --git a/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java b/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java
new file mode 100644
index 000000000..4dd195a4d
--- /dev/null
+++ b/src/api/java/baritone/api/command/datatypes/RelativeGoalYLevel.java
@@ -0,0 +1,50 @@
+/*
+ * 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.command.datatypes;
+
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import baritone.api.pathing.goals.GoalYLevel;
+import baritone.api.utils.BetterBlockPos;
+import baritone.api.command.exception.CommandException;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.stream.Stream;
+
+public enum RelativeGoalYLevel implements IDatatypePost {
+ INSTANCE;
+
+ @Override
+ public GoalYLevel apply(IDatatypeContext ctx, BetterBlockPos origin) throws CommandException {
+ if (origin == null) {
+ origin = BetterBlockPos.ORIGIN;
+ }
+
+ return new GoalYLevel(
+ MathHelper.floor(ctx.getConsumer().getDatatypePost(RelativeCoordinate.INSTANCE, (double) origin.y))
+ );
+ }
+
+ @Override
+ public Stream tabComplete(IDatatypeContext ctx) {
+ final IArgConsumer consumer = ctx.getConsumer();
+ if (consumer.hasAtMost(1)) {
+ return consumer.tabCompleteDatatype(RelativeCoordinate.INSTANCE);
+ }
+ return Stream.empty();
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandErrorMessageException.java b/src/api/java/baritone/api/command/exception/CommandErrorMessageException.java
new file mode 100644
index 000000000..4a21bede7
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandErrorMessageException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.command.exception;
+
+public abstract class CommandErrorMessageException extends CommandException {
+
+ protected CommandErrorMessageException(String reason) {
+ super(reason);
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandException.java b/src/api/java/baritone/api/command/exception/CommandException.java
new file mode 100644
index 000000000..b8962c159
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.command.exception;
+
+public abstract class CommandException extends Exception implements ICommandException {
+
+ protected CommandException(String reason) {
+ super(reason);
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandInvalidArgumentException.java b/src/api/java/baritone/api/command/exception/CommandInvalidArgumentException.java
new file mode 100644
index 000000000..1902d7355
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandInvalidArgumentException.java
@@ -0,0 +1,34 @@
+/*
+ * 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.command.exception;
+
+import baritone.api.command.argument.ICommandArgument;
+
+public abstract class CommandInvalidArgumentException extends CommandErrorMessageException {
+
+ public final ICommandArgument arg;
+
+ protected CommandInvalidArgumentException(ICommandArgument arg, String reason) {
+ super(String.format(
+ "Error at argument #%s: %s",
+ arg.getIndex() == -1 ? "" : Integer.toString(arg.getIndex() + 1),
+ reason
+ ));
+ this.arg = arg;
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandInvalidStateException.java b/src/api/java/baritone/api/command/exception/CommandInvalidStateException.java
new file mode 100644
index 000000000..0fa22fcbb
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandInvalidStateException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.command.exception;
+
+public class CommandInvalidStateException extends CommandErrorMessageException {
+
+ public CommandInvalidStateException(String reason) {
+ super(reason);
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandInvalidTypeException.java b/src/api/java/baritone/api/command/exception/CommandInvalidTypeException.java
new file mode 100644
index 000000000..516fd308f
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandInvalidTypeException.java
@@ -0,0 +1,39 @@
+/*
+ * 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.command.exception;
+
+import baritone.api.command.argument.ICommandArgument;
+
+public class CommandInvalidTypeException extends CommandInvalidArgumentException {
+
+ public CommandInvalidTypeException(ICommandArgument arg, String expected) {
+ super(arg, String.format("Expected %s", expected));
+ }
+
+ public CommandInvalidTypeException(ICommandArgument arg, String expected, Throwable cause) {
+ super(arg, String.format("Expected %s.\nMore details: %s", expected, cause.getMessage()));
+ }
+
+ public CommandInvalidTypeException(ICommandArgument arg, String expected, String got) {
+ super(arg, String.format("Expected %s, but got %s instead", expected, got));
+ }
+
+ public CommandInvalidTypeException(ICommandArgument arg, String expected, String got, Throwable cause) {
+ super(arg, String.format("Expected %s, but got %s instead.\nMore details: %s", expected, got, cause.getMessage()));
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandNoParserForTypeException.java b/src/api/java/baritone/api/command/exception/CommandNoParserForTypeException.java
new file mode 100644
index 000000000..4bf7a1ac7
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandNoParserForTypeException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.command.exception;
+
+public class CommandNoParserForTypeException extends CommandUnhandledException {
+
+ public CommandNoParserForTypeException(Class> klass) {
+ super(String.format("Could not find a handler for type %s", klass.getSimpleName()));
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandNotEnoughArgumentsException.java b/src/api/java/baritone/api/command/exception/CommandNotEnoughArgumentsException.java
new file mode 100644
index 000000000..e2e05cfbf
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandNotEnoughArgumentsException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.command.exception;
+
+public class CommandNotEnoughArgumentsException extends CommandErrorMessageException {
+
+ public CommandNotEnoughArgumentsException(int minArgs) {
+ super(String.format("Not enough arguments (expected at least %d)", minArgs));
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandNotFoundException.java b/src/api/java/baritone/api/command/exception/CommandNotFoundException.java
new file mode 100644
index 000000000..123661c83
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandNotFoundException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.command.exception;
+
+import baritone.api.command.Command;
+import baritone.api.command.argument.ICommandArgument;
+
+import java.util.List;
+
+import static baritone.api.utils.Helper.HELPER;
+
+public class CommandNotFoundException extends CommandException {
+
+ public final String command;
+
+ public CommandNotFoundException(String command) {
+ super(String.format("Command not found: %s", command));
+ this.command = command;
+ }
+
+ @Override
+ public void handle(Command command, List args) {
+ HELPER.logDirect(getMessage());
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandTooManyArgumentsException.java b/src/api/java/baritone/api/command/exception/CommandTooManyArgumentsException.java
new file mode 100644
index 000000000..9aec48ea9
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandTooManyArgumentsException.java
@@ -0,0 +1,25 @@
+/*
+ * 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.command.exception;
+
+public class CommandTooManyArgumentsException extends CommandErrorMessageException {
+
+ public CommandTooManyArgumentsException(int maxArgs) {
+ super(String.format("Too many arguments (expected at most %d)", maxArgs));
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/CommandUnhandledException.java b/src/api/java/baritone/api/command/exception/CommandUnhandledException.java
new file mode 100644
index 000000000..394dd65e9
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/CommandUnhandledException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.command.exception;
+
+import baritone.api.command.Command;
+import baritone.api.command.argument.ICommandArgument;
+import net.minecraft.util.text.TextFormatting;
+
+import java.util.List;
+
+import static baritone.api.utils.Helper.HELPER;
+
+public class CommandUnhandledException extends RuntimeException implements ICommandException {
+
+ public CommandUnhandledException(String message) {
+ super(message);
+ }
+
+ public CommandUnhandledException(Throwable cause) {
+ super(cause);
+ }
+
+ @Override
+ public void handle(Command command, List args) {
+ HELPER.logDirect("An unhandled exception occurred." +
+ "The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues",
+ TextFormatting.RED);
+
+ this.printStackTrace();
+ }
+}
diff --git a/src/api/java/baritone/api/command/exception/ICommandException.java b/src/api/java/baritone/api/command/exception/ICommandException.java
new file mode 100644
index 000000000..3c96cb520
--- /dev/null
+++ b/src/api/java/baritone/api/command/exception/ICommandException.java
@@ -0,0 +1,55 @@
+/*
+ * 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.command.exception;
+
+import baritone.api.command.Command;
+import baritone.api.command.argument.ICommandArgument;
+import net.minecraft.util.text.TextFormatting;
+
+import java.util.List;
+
+import static baritone.api.utils.Helper.HELPER;
+
+/**
+ * The base for a Baritone Command Exception, checked or unchecked. Provides a
+ * {@link #handle(Command, List)} method that is used to provide useful output
+ * to the user for diagnosing issues that may have occurred during execution.
+ *
+ * Anything implementing this interface should be assignable to {@link Exception}.
+ *
+ * @author Brady
+ * @since 9/20/2019
+ */
+public interface ICommandException {
+
+ /**
+ * @see Exception#getMessage()
+ * @return The exception details
+ */
+ String getMessage();
+
+ /**
+ * Called when this exception is thrown, to handle the exception.
+ *
+ * @param command The command that threw it.
+ * @param args The arguments the command was called with.
+ */
+ default void handle(Command command, List args) {
+ HELPER.logDirect(this.getMessage(), TextFormatting.RED);
+ }
+}
diff --git a/src/api/java/baritone/api/command/helpers/arguments/IArgConsumer.java b/src/api/java/baritone/api/command/helpers/arguments/IArgConsumer.java
new file mode 100644
index 000000000..637fcd674
--- /dev/null
+++ b/src/api/java/baritone/api/command/helpers/arguments/IArgConsumer.java
@@ -0,0 +1,595 @@
+/*
+ * 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.command.helpers.arguments;
+
+import baritone.api.command.Command;
+import baritone.api.command.exception.CommandTooManyArgumentsException;
+import baritone.api.utils.Helper;
+import baritone.api.command.argparser.IArgParser;
+import baritone.api.command.argument.ICommandArgument;
+import baritone.api.command.datatypes.IDatatype;
+import baritone.api.command.datatypes.IDatatypeFor;
+import baritone.api.command.datatypes.IDatatypePost;
+import baritone.api.command.exception.CommandException;
+import baritone.api.command.exception.CommandInvalidTypeException;
+import baritone.api.command.exception.CommandNotEnoughArgumentsException;
+import net.minecraft.util.Direction;
+
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.stream.Stream;
+
+/**
+ * The {@link IArgConsumer} is how {@link Command}s read the arguments passed to them. This class has many benefits:
+ *
+ *
+ * - Mutability. The whole concept of the {@link IArgConsumer}} is to let you gradually consume arguments in any way
+ * you'd like. You can change your consumption based on earlier arguments, for subcommands for example.
+ * - You don't need to keep track of your consumption. The {@link IArgConsumer}} keeps track of the arguments you
+ * consume so that it can throw detailed exceptions whenever something is out of the ordinary. Additionally, if you
+ * need to retrieve an argument after you've already consumed it - look no further than {@link #consumed()}!
+ * - Easy retrieval of many different types. If you need to retrieve an instance of an int or float for example,
+ * look no further than {@link #getAs(Class)}. If you need a more powerful way of retrieving data, try out the many
+ * {@code getDatatype...} methods.
+ * - It's very easy to throw detailed exceptions. The {@link IArgConsumer}} has many different methods that can
+ * enforce the number of arguments, the type of arguments, and more, throwing different types of
+ * {@link CommandException}s if something seems off. You're recommended to do all validation and store all needed
+ * data in variables BEFORE logging any data to chat via {@link Helper#logDirect(String)}, so that the error
+ * handlers can do their job and log the error to chat.
+ *
+ */
+public interface IArgConsumer {
+
+ LinkedList getArgs();
+
+ Deque getConsumed();
+
+ /**
+ * @param num The number of arguments to check for
+ * @return {@code true} if there are at least {@code num} arguments left in this {@link IArgConsumer}}
+ * @see #hasAny()
+ * @see #hasAtMost(int)
+ * @see #hasExactly(int)
+ */
+ boolean has(int num);
+
+ /**
+ * @return {@code true} if there is at least 1 argument left in this {@link IArgConsumer}}
+ * @see #has(int)
+ * @see #hasAtMostOne()
+ * @see #hasExactlyOne()
+ */
+ boolean hasAny();
+
+ /**
+ * @param num The number of arguments to check for
+ * @return {@code true} if there are at most {@code num} arguments left in this {@link IArgConsumer}}
+ * @see #has(int)
+ * @see #hasAtMost(int)
+ * @see #hasExactly(int)
+ */
+ boolean hasAtMost(int num);
+
+ /**
+ * @return {@code true} if there is at most 1 argument left in this {@link IArgConsumer}}
+ * @see #hasAny()
+ * @see #hasAtMostOne()
+ * @see #hasExactlyOne()
+ */
+ boolean hasAtMostOne();
+
+ /**
+ * @param num The number of arguments to check for
+ * @return {@code true} if there are exactly {@code num} arguments left in this {@link IArgConsumer}}
+ * @see #has(int)
+ * @see #hasAtMost(int)
+ */
+ boolean hasExactly(int num);
+
+ /**
+ * @return {@code true} if there is exactly 1 argument left in this {@link IArgConsumer}}
+ * @see #hasAny()
+ * @see #hasAtMostOne()
+ */
+ boolean hasExactlyOne();
+
+ /**
+ * @param index The index to peek
+ * @return The argument at index {@code index} in this {@link IArgConsumer}}, with 0 being the next one. This does not
+ * mutate the {@link IArgConsumer}}
+ * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left
+ * @see #peek()
+ * @see #peekString(int)
+ * @see #peekAs(Class, int)
+ * @see #get()
+ */
+ ICommandArgument peek(int index) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @return The next argument in this {@link IArgConsumer}}. This does not mutate the {@link IArgConsumer}}
+ * @throws CommandNotEnoughArgumentsException If there is less than one argument left
+ * @see #peek(int)
+ * @see #peekString()
+ * @see #peekAs(Class)
+ * @see #get()
+ */
+ ICommandArgument peek() throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @param index The index to peek
+ * @param type The type to check for
+ * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next
+ * argument
+ * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left
+ * @see #peek()
+ * @see #getAs(Class)
+ */
+ boolean is(Class> type, int index) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @param type The type to check for
+ * @return If an ArgParser.Stateless for the specified {@code type} would succeed in parsing the next
+ * argument
+ * @throws CommandNotEnoughArgumentsException If there is less than one argument left
+ * @see #peek()
+ * @see #getAs(Class)
+ */
+ boolean is(Class> type) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @param index The index to peek
+ * @return The value of the argument at index {@code index} in this {@link IArgConsumer}}, with 0 being the next one
+ * This does not mutate the {@link IArgConsumer}}
+ * @throws CommandNotEnoughArgumentsException If there is less than {@code index + 1} arguments left
+ * @see #peek()
+ * @see #peekString()
+ */
+ String peekString(int index) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @return The value of the next argument in this {@link IArgConsumer}}. This does not mutate the {@link IArgConsumer}}
+ * @throws CommandNotEnoughArgumentsException If there is less than one argument left
+ * @see #peekString(int)
+ * @see #getString()
+ */
+ String peekString() throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @param index The index to peek
+ * @param enumClass The class to search
+ * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the
+ * next argument's value
+ * @throws java.util.NoSuchElementException If the constant couldn't be found
+ * @see #peekEnumOrNull(Class)
+ * @see #getEnum(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E peekEnum(Class enumClass, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ /**
+ * @param enumClass The class to search
+ * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the
+ * next argument's value
+ * @throws CommandInvalidTypeException If the constant couldn't be found
+ * @see #peekEnumOrNull(Class)
+ * @see #getEnum(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E peekEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ /**
+ * @param index The index to peek
+ * @param enumClass The class to search
+ * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the
+ * next argument's value. If no constant could be found, null
+ * @see #peekEnum(Class)
+ * @see #getEnumOrNull(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E peekEnumOrNull(Class enumClass, int index) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @param enumClass The class to search
+ * @return From the specified enum class, an enum constant of that class. The enum constant's name will match the
+ * next argument's value. If no constant could be found, null
+ * @see #peekEnum(Class)
+ * @see #getEnumOrNull(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E peekEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified
+ * class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @param index The index to peek
+ * @return An instance of the specified type
+ * @throws CommandInvalidTypeException If the parsing failed
+ * @see IArgParser
+ * @see #peekAs(Class)
+ * @see #peekAsOrDefault(Class, Object, int)
+ * @see #peekAsOrNull(Class, int)
+ */
+ T peekAs(Class type, int index) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @return An instance of the specified type
+ * @throws CommandInvalidTypeException If the parsing failed
+ * @see IArgParser
+ * @see #peekAs(Class, int)
+ * @see #peekAsOrDefault(Class, Object)
+ * @see #peekAsOrNull(Class)
+ */
+ T peekAs(Class type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified
+ * class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @param def The value to return if the argument can't be parsed
+ * @param index The index to peek
+ * @return An instance of the specified type, or {@code def} if it couldn't be parsed
+ * @see IArgParser
+ * @see #peekAsOrDefault(Class, Object)
+ * @see #peekAs(Class, int)
+ * @see #peekAsOrNull(Class, int)
+ */
+ T peekAsOrDefault(Class type, T def, int index) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @param def The value to return if the argument can't be parsed
+ * @return An instance of the specified type, or {@code def} if it couldn't be parsed
+ * @see IArgParser
+ * @see #peekAsOrDefault(Class, Object, int)
+ * @see #peekAs(Class)
+ * @see #peekAsOrNull(Class)
+ */
+ T peekAsOrDefault(Class type, T def) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the argument at the specified index into the specified
+ * class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @param index The index to peek
+ * @return An instance of the specified type, or {@code null} if it couldn't be parsed
+ * @see IArgParser
+ * @see #peekAsOrNull(Class)
+ * @see #peekAs(Class, int)
+ * @see #peekAsOrDefault(Class, Object, int)
+ */
+ T peekAsOrNull(Class type, int index) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @return An instance of the specified type, or {@code null} if it couldn't be parsed
+ * @see IArgParser
+ * @see #peekAsOrNull(Class, int)
+ * @see #peekAs(Class)
+ * @see #peekAsOrDefault(Class, Object)
+ */
+ T peekAsOrNull(Class type) throws CommandNotEnoughArgumentsException;
+
+ T peekDatatype(IDatatypeFor datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ T peekDatatype(IDatatypePost datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ T peekDatatype(IDatatypePost datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ T peekDatatypeOrNull(IDatatypeFor datatype);
+
+ T peekDatatypeOrNull(IDatatypePost datatype);
+
+ > T peekDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ > T peekDatatypePostOrDefault(D datatype, O original, T def);
+
+ > T peekDatatypePostOrNull(D datatype, O original);
+
+ /**
+ * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method.
+ *
+ * @param datatype The datatype to get
+ * @return The datatype instance
+ * @see IDatatype
+ * @see IDatatypeFor
+ */
+ > T peekDatatypeFor(Class datatype);
+
+ /**
+ * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method.
+ *
+ * @param datatype The datatype to get
+ * @param def The default value
+ * @return The datatype instance, or {@code def} if it throws an exception
+ * @see IDatatype
+ * @see IDatatypeFor
+ */
+ > T peekDatatypeForOrDefault(Class datatype, T def);
+
+ /**
+ * Attempts to get the specified {@link IDatatypeFor} from this ArgConsumer
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * Since this is a peek operation, this ArgConsumer will not be mutated by any call to this method.
+ *
+ * @param datatype The datatype to get
+ * @return The datatype instance, or {@code null} if it throws an exception
+ * @see IDatatype
+ * @see IDatatypeFor
+ */
+ > T peekDatatypeForOrNull(Class datatype);
+
+ /**
+ * Gets the next argument and returns it. This consumes the first argument so that subsequent calls will return
+ * later arguments
+ *
+ * @return The next argument
+ * @throws CommandNotEnoughArgumentsException If there's less than one argument left
+ */
+ ICommandArgument get() throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Gets the value of the next argument and returns it. This consumes the first argument so that subsequent calls
+ * will return later arguments
+ *
+ * @return The value of the next argument
+ * @throws CommandNotEnoughArgumentsException If there's less than one argument left
+ */
+ String getString() throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Gets an enum value from the enum class with the same name as the next argument's value
+ *
+ * For example if you getEnum as an {@link Direction}, and the next argument's value is "up", this will return
+ * {@link Direction#UP}
+ *
+ * @param enumClass The enum class to search
+ * @return An enum constant of that class with the same name as the next argument's value
+ * @throws CommandInvalidTypeException If the constant couldn't be found
+ * @see #peekEnum(Class)
+ * @see #getEnumOrNull(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E getEnum(Class enumClass) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ /**
+ * Gets an enum value from the enum class with the same name as the next argument's value
+ *
+ * For example if you getEnum as an {@link Direction}, and the next argument's value is "up", this will return
+ * {@link Direction#UP}
+ *
+ * @param enumClass The enum class to search
+ * @param def The default value
+ * @return An enum constant of that class with the same name as the next argument's value, or {@code def} if it
+ * couldn't be found
+ * @see #getEnum(Class)
+ * @see #getEnumOrNull(Class)
+ * @see #peekEnumOrNull(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E getEnumOrDefault(Class enumClass, E def) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Gets an enum value from the enum class with the same name as the next argument's value
+ *
+ * For example if you getEnum as an {@link Direction}, and the next argument's value is "up", this will return
+ * {@link Direction#UP}
+ *
+ * @param enumClass The enum class to search
+ * @return An enum constant of that class with the same name as the next argument's value, or {@code null} if it
+ * couldn't be found
+ * @see #getEnum(Class)
+ * @see #getEnumOrDefault(Class, Enum)
+ * @see #peekEnumOrNull(Class)
+ * @see ICommandArgument#getEnum(Class)
+ */
+ > E getEnumOrNull(Class enumClass) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @return An instance of the specified type
+ * @throws CommandInvalidTypeException If the parsing failed
+ * @see IArgParser
+ * @see #get()
+ * @see #getAsOrDefault(Class, Object)
+ * @see #getAsOrNull(Class)
+ * @see #peekAs(Class)
+ * @see #peekAsOrDefault(Class, Object, int)
+ * @see #peekAsOrNull(Class, int)
+ */
+ T getAs(Class type) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @param def The default value
+ * @return An instance of the specified type, or {@code def} if it couldn't be parsed
+ * @see IArgParser
+ * @see #get()
+ * @see #getAs(Class)
+ * @see #getAsOrNull(Class)
+ * @see #peekAs(Class)
+ * @see #peekAsOrDefault(Class, Object, int)
+ * @see #peekAsOrNull(Class, int)
+ */
+ T getAsOrDefault(Class type, T def) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * Tries to use a stateless {@link IArgParser} to parse the next argument into the specified class
+ *
+ * A critical difference between {@link IDatatype}s and {@link IArgParser}s is how many arguments they can take.
+ * While {@link IArgParser}s always operate on a single argument's value, {@link IDatatype}s get access to the entire
+ * {@link IArgConsumer}}.
+ *
+ * @param type The type to peek as
+ * @return An instance of the specified type, or {@code null} if it couldn't be parsed
+ * @see IArgParser
+ * @see #get()
+ * @see #getAs(Class)
+ * @see #getAsOrDefault(Class, Object)
+ * @see #peekAs(Class)
+ * @see #peekAsOrDefault(Class, Object, int)
+ * @see #peekAsOrNull(Class, int)
+ */
+ T getAsOrNull(Class type) throws CommandNotEnoughArgumentsException;
+
+ > T getDatatypePost(D datatype, O original) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ > T getDatatypePostOrDefault(D datatype, O original, T _default);
+
+ > T getDatatypePostOrNull(D datatype, O original);
+
+ > T getDatatypeFor(D datatype) throws CommandInvalidTypeException, CommandNotEnoughArgumentsException;
+
+ > T getDatatypeForOrDefault(D datatype, T def);
+
+ > T getDatatypeForOrNull(D datatype);
+
+ Stream tabCompleteDatatype(T datatype);
+
+ /**
+ * Returns the "raw rest" of the string. For example, from a string arg1 arg2 arg3, split
+ * into three {@link ICommandArgument}s {@code "arg1"}, {@code "arg2"}, and {@code "arg3"}:
+ *
+ *
+ * - {@code rawRest()} would return
arg1 arg2 arg3
+ * - After calling {@link #get()}, {@code rawRest()} would return
arg2 arg3 (note the
+ * double space - it is preserved!)
+ * - After calling {@link #get()} again, {@code rawRest()} would return {@code "arg3"}
+ * - After calling {@link #get()} one last time, {@code rawRest()} would return {@code ""}
+ *
+ *
+ * @return The "raw rest" of the string.
+ */
+ String rawRest();
+
+ /**
+ * @param min The minimum amount of arguments to require.
+ * @throws CommandNotEnoughArgumentsException If there are less than {@code min} arguments left.
+ * @see #requireMax(int)
+ * @see #requireExactly(int)
+ */
+ void requireMin(int min) throws CommandNotEnoughArgumentsException;
+
+ /**
+ * @param max The maximum amount of arguments allowed.
+ * @throws CommandTooManyArgumentsException If there are more than {@code max} arguments left.
+ * @see #requireMin(int)
+ * @see #requireExactly(int)
+ */
+ void requireMax(int max) throws CommandTooManyArgumentsException;
+
+ /**
+ * @param args The exact amount of arguments to require.
+ * @throws CommandNotEnoughArgumentsException If there are less than {@code args} arguments left.
+ * @throws CommandTooManyArgumentsException If there are more than {@code args} arguments left.
+ * @see #requireMin(int)
+ * @see #requireMax(int)
+ */
+ void requireExactly(int args) throws CommandException;
+
+ /**
+ * @return If this {@link IArgConsumer}} has consumed at least one argument.
+ * @see #consumed()
+ * @see #consumedString()
+ */
+ boolean hasConsumed();
+
+ /**
+ * @return The last argument this {@link IArgConsumer}} has consumed, or an "unknown" argument, indicated by a
+ * comamnd argument index that has a value of {@code -1}, if no arguments have been consumed yet.
+ * @see #consumedString()
+ * @see #hasConsumed()
+ */
+ ICommandArgument consumed();
+
+ /**
+ * @return The value of thelast argument this {@link IArgConsumer}} has consumed, or an empty string if no arguments
+ * have been consumed yet
+ * @see #consumed()
+ * @see #hasConsumed()
+ */
+ String consumedString();
+
+ /**
+ * @return A copy of this {@link IArgConsumer}}. It has the same arguments (both consumed and not), but does not
+ * affect or mutate this instance. Useful for the various {@code peek} functions
+ */
+ IArgConsumer copy();
+}
diff --git a/src/api/java/baritone/api/command/helpers/pagination/Paginator.java b/src/api/java/baritone/api/command/helpers/pagination/Paginator.java
new file mode 100644
index 000000000..e8931f492
--- /dev/null
+++ b/src/api/java/baritone/api/command/helpers/pagination/Paginator.java
@@ -0,0 +1,184 @@
+/*
+ * 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.command.helpers.pagination;
+
+import baritone.api.utils.Helper;
+import baritone.api.command.exception.CommandException;
+import baritone.api.command.exception.CommandInvalidTypeException;
+import baritone.api.command.helpers.arguments.IArgConsumer;
+import net.minecraft.util.text.ITextComponent;
+import net.minecraft.util.text.StringTextComponent;
+import net.minecraft.util.text.TextFormatting;
+import net.minecraft.util.text.event.ClickEvent;
+import net.minecraft.util.text.event.HoverEvent;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+public class Paginator implements Helper {
+
+ public final List entries;
+ public int pageSize = 8;
+ public int page = 1;
+
+ public Paginator(List entries) {
+ this.entries = entries;
+ }
+
+ public Paginator(E... entries) {
+ this.entries = Arrays.asList(entries);
+ }
+
+ public Paginator setPageSize(int pageSize) {
+ this.pageSize = pageSize;
+ return this;
+ }
+
+ public int getMaxPage() {
+ return (entries.size() - 1) / pageSize + 1;
+ }
+
+ public boolean validPage(int page) {
+ return page > 0 && page <= getMaxPage();
+ }
+
+ public Paginator skipPages(int pages) {
+ page += pages;
+ return this;
+ }
+
+ public void display(Function transform, String commandPrefix) {
+ int offset = (page - 1) * pageSize;
+ for (int i = offset; i < offset + pageSize; i++) {
+ if (i < entries.size()) {
+ logDirect(transform.apply(entries.get(i)));
+ } else {
+ logDirect("--", TextFormatting.DARK_GRAY);
+ }
+ }
+ boolean hasPrevPage = commandPrefix != null && validPage(page - 1);
+ boolean hasNextPage = commandPrefix != null && validPage(page + 1);
+ ITextComponent prevPageComponent = new StringTextComponent("<<");
+ if (hasPrevPage) {
+ prevPageComponent.getStyle()
+ .setClickEvent(new ClickEvent(
+ ClickEvent.Action.RUN_COMMAND,
+ String.format("%s %d", commandPrefix, page - 1)
+ ))
+ .setHoverEvent(new HoverEvent(
+ HoverEvent.Action.SHOW_TEXT,
+ new StringTextComponent("Click to view previous page")
+ ));
+ } else {
+ prevPageComponent.getStyle().setColor(TextFormatting.DARK_GRAY);
+ }
+ ITextComponent nextPageComponent = new StringTextComponent(">>");
+ if (hasNextPage) {
+ nextPageComponent.getStyle()
+ .setClickEvent(new ClickEvent(
+ ClickEvent.Action.RUN_COMMAND,
+ String.format("%s %d", commandPrefix, page + 1)
+ ))
+ .setHoverEvent(new HoverEvent(
+ HoverEvent.Action.SHOW_TEXT,
+ new StringTextComponent("Click to view next page")
+ ));
+ } else {
+ nextPageComponent.getStyle().setColor(TextFormatting.DARK_GRAY);
+ }
+ ITextComponent pagerComponent = new StringTextComponent("");
+ pagerComponent.getStyle().setColor(TextFormatting.GRAY);
+ pagerComponent.appendSibling(prevPageComponent);
+ pagerComponent.appendText(" | ");
+ pagerComponent.appendSibling(nextPageComponent);
+ pagerComponent.appendText(String.format(" %d/%d", page, getMaxPage()));
+ logDirect(pagerComponent);
+ }
+
+ public void display(Function transform) {
+ display(transform, null);
+ }
+
+ public static void paginate(IArgConsumer consumer, Paginator pagi, Runnable pre, Function transform, String commandPrefix) throws CommandException {
+ int page = 1;
+ consumer.requireMax(1);
+ if (consumer.hasAny()) {
+ page = consumer.getAs(Integer.class);
+ if (!pagi.validPage(page)) {
+ throw new CommandInvalidTypeException(
+ consumer.consumed(),
+ String.format(
+ "a valid page (1-%d)",
+ pagi.getMaxPage()
+ ),
+ consumer.consumed().getValue()
+ );
+ }
+ }
+ pagi.skipPages(page - pagi.page);
+ if (pre != null) {
+ pre.run();
+ }
+ pagi.display(transform, commandPrefix);
+ }
+
+ public static void paginate(IArgConsumer consumer, List elems, Runnable pre, Function transform, String commandPrefix) throws CommandException {
+ paginate(consumer, new Paginator<>(elems), pre, transform, commandPrefix);
+ }
+
+ public static void paginate(IArgConsumer consumer, T[] elems, Runnable pre, Function transform, String commandPrefix) throws CommandException {
+ paginate(consumer, Arrays.asList(elems), pre, transform, commandPrefix);
+ }
+
+ public static void paginate(IArgConsumer consumer, Paginator pagi, Function transform, String commandPrefix) throws CommandException {
+ paginate(consumer, pagi, null, transform, commandPrefix);
+ }
+
+ public static void paginate(IArgConsumer consumer, List elems, Function transform, String commandPrefix) throws CommandException {
+ paginate(consumer, new Paginator<>(elems), null, transform, commandPrefix);
+ }
+
+ public static void paginate(IArgConsumer consumer, T[] elems, Function transform, String commandPrefix) throws CommandException {
+ paginate(consumer, Arrays.asList(elems), null, transform, commandPrefix);
+ }
+
+ public static void paginate(IArgConsumer consumer, Paginator pagi, Runnable pre, Function transform) throws CommandException {
+ paginate(consumer, pagi, pre, transform, null);
+ }
+
+ public static void paginate(IArgConsumer consumer, List elems, Runnable pre, Function transform) throws CommandException {
+ paginate(consumer, new Paginator<>(elems), pre, transform, null);
+ }
+
+ public static void paginate(IArgConsumer consumer, T[] elems, Runnable pre, Function transform) throws CommandException {
+ paginate(consumer, Arrays.asList(elems), pre, transform, null);
+ }
+
+ public static void paginate(IArgConsumer consumer, Paginator pagi, Function transform) throws CommandException {
+ paginate(consumer, pagi, null, transform, null);
+ }
+
+ public static void paginate(IArgConsumer consumer, List