diff --git a/.gitignore b/.gitignore index 0834b1e8..bd3b054a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ classes/ # IntelliJ Files .idea/ *.iml +*.ipr +*.iws /logs/ # Copyright Files diff --git a/src/api/java/baritone/api/Settings.java b/src/api/java/baritone/api/Settings.java index 80dcf5ce..6ff01352 100644 --- a/src/api/java/baritone/api/Settings.java +++ b/src/api/java/baritone/api/Settings.java @@ -483,15 +483,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<>(true); + /** * Render the goal */ @@ -592,10 +597,25 @@ 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); + /** * Don't stop walking forward when you need to break blocks in your way */ @@ -894,6 +914,11 @@ 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); + /** * A map of lowercase setting field names to their respective setting diff --git a/src/api/java/baritone/api/behavior/IPathingBehavior.java b/src/api/java/baritone/api/behavior/IPathingBehavior.java index 15777448..8890fdc3 100644 --- a/src/api/java/baritone/api/behavior/IPathingBehavior.java +++ b/src/api/java/baritone/api/behavior/IPathingBehavior.java @@ -64,11 +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() { - return getCurrent() != null; - } + 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() + */ + boolean hasPath(); /** * Cancels the pathing behavior or the current path calculation, and all processes that could be controlling path. diff --git a/src/api/java/baritone/api/cache/IWorldScanner.java b/src/api/java/baritone/api/cache/IWorldScanner.java index caa44cbc..6f4349ac 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.IBlockFilter; import baritone.api.utils.IPlayerContext; import net.minecraft.block.Block; import net.minecraft.util.math.BlockPos; @@ -35,26 +36,26 @@ public interface IWorldScanner { * * @param ctx The {@link IPlayerContext} containing player and world info that the * scan is based upon - * @param blocks The blocks to scan for + * @param filter The block filter 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 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, IBlockFilter filter, int max, int yLevelThreshold, int 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 blocks The blocks to scan for + * @param filter The block filter 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, List blocks, ChunkPos pos, int max, int yLevelThreshold); + List scanChunk(IPlayerContext ctx, IBlockFilter filter, ChunkPos pos, int max, int yLevelThreshold); } diff --git a/src/api/java/baritone/api/event/events/TabCompleteEvent.java b/src/api/java/baritone/api/event/events/TabCompleteEvent.java new file mode 100644 index 00000000..d5bed1ff --- /dev/null +++ b/src/api/java/baritone/api/event/events/TabCompleteEvent.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.event.events; + +import baritone.api.event.events.type.Cancellable; +import baritone.api.event.events.type.Overrideable; + +/** + * @author LoganDark + */ +public abstract class TabCompleteEvent extends Cancellable { + public final Overrideable prefix; + public final Overrideable completions; + + TabCompleteEvent(String prefix, String[] completions) { + this.prefix = new Overrideable<>(prefix); + this.completions = new Overrideable<>(completions); + } + + public boolean wasModified() { + return prefix.wasModified() || completions.wasModified(); + } + + public static final class Pre extends TabCompleteEvent { + public Pre(String prefix) { + super(prefix, null); + } + } + + public static final class Post extends TabCompleteEvent { + public Post(String prefix, String[] completions) { + super(prefix, completions); + } + } +} diff --git a/src/api/java/baritone/api/event/events/type/Overrideable.java b/src/api/java/baritone/api/event/events/type/Overrideable.java new file mode 100644 index 00000000..987e64f7 --- /dev/null +++ b/src/api/java/baritone/api/event/events/type/Overrideable.java @@ -0,0 +1,35 @@ +package baritone.api.event.events.type; + +/** + * @author LoganDark + */ +public class Overrideable { + private T value; + private boolean modified; + + public Overrideable(T current) { + value = current; + } + + public T get() { + return value; + } + + public void set(T newValue) { + value = newValue; + modified = true; + } + + public boolean wasModified() { + return modified; + } + + @Override + public String toString() { + return String.format( + "Overrideable{modified=%s,value=%s}", + Boolean.toString(modified), + value.toString() + ); + } +} diff --git a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java index 71045768..9e5b6dbb 100644 --- a/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/AbstractGameEventListener.java @@ -39,6 +39,12 @@ public interface AbstractGameEventListener extends IGameEventListener { @Override default void onSendChatMessage(ChatEvent event) {} + @Override + default void onPreTabComplete(TabCompleteEvent.Pre event) {}; + + @Override + default void onPostTabComplete(TabCompleteEvent.Post event) {}; + @Override default void onChunkEvent(ChunkEvent event) {} diff --git a/src/api/java/baritone/api/event/listener/IGameEventListener.java b/src/api/java/baritone/api/event/listener/IGameEventListener.java index dc471e5f..00b24e3d 100644 --- a/src/api/java/baritone/api/event/listener/IGameEventListener.java +++ b/src/api/java/baritone/api/event/listener/IGameEventListener.java @@ -57,6 +57,21 @@ public interface IGameEventListener { */ void onSendChatMessage(ChatEvent event); + /** + * Runs whenever the client player tries to tab complete in chat. + * + * @param event The event + */ + void onPreTabComplete(TabCompleteEvent.Pre event); + + /** + * Runs whenever the client player tries to tab complete in chat once completions have been recieved from the + * server. This will only be called if the {@link TabCompleteEvent.Pre#cancel()} method was not called. + * + * @param event The event + */ + void onPostTabComplete(TabCompleteEvent.Post event); + /** * Runs before and after whenever a chunk is either loaded, unloaded, or populated. * diff --git a/src/api/java/baritone/api/pathing/goals/GoalInverted.java b/src/api/java/baritone/api/pathing/goals/GoalInverted.java new file mode 100644 index 00000000..dfe5c770 --- /dev/null +++ b/src/api/java/baritone/api/pathing/goals/GoalInverted.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.pathing.goals; + +/** + * Invert any goal + * + * @author LoganDark + */ +public class GoalInverted implements Goal { + public final Goal origin; + + public GoalInverted(Goal origin) { + this.origin = origin; + } + + @Override + public boolean isInGoal(int x, int y, int z) { + return false; + } + + @Override + public double heuristic(int x, int y, int z) { + return -origin.heuristic(x, y, z); + } + + @Override + public String toString() { + return String.format("GoalInverted{%s}", origin.toString()); + } +} diff --git a/src/api/java/baritone/api/pathing/goals/GoalXZ.java b/src/api/java/baritone/api/pathing/goals/GoalXZ.java index 7f8d16ab..5ad23336 100644 --- a/src/api/java/baritone/api/pathing/goals/GoalXZ.java +++ b/src/api/java/baritone/api/pathing/goals/GoalXZ.java @@ -18,6 +18,7 @@ package baritone.api.pathing.goals; import baritone.api.BaritoneAPI; +import baritone.api.utils.BetterBlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; @@ -45,6 +46,11 @@ public class GoalXZ implements Goal { this.z = z; } + public GoalXZ(BetterBlockPos pos) { + this.x = pos.x; + this.z = pos.z; + } + @Override public boolean isInGoal(int x, int y, int z) { return x == this.x && z == this.z; diff --git a/src/api/java/baritone/api/process/IBuilderProcess.java b/src/api/java/baritone/api/process/IBuilderProcess.java index f73f0e74..5ca5f804 100644 --- a/src/api/java/baritone/api/process/IBuilderProcess.java +++ b/src/api/java/baritone/api/process/IBuilderProcess.java @@ -58,6 +58,8 @@ public interface IBuilderProcess extends IBaritoneProcess { void pause(); + boolean isPaused(); + void resume(); void clearArea(BlockPos corner1, BlockPos corner2); diff --git a/src/api/java/baritone/api/process/IMineProcess.java b/src/api/java/baritone/api/process/IMineProcess.java index 7ebabc9c..4885e32f 100644 --- a/src/api/java/baritone/api/process/IMineProcess.java +++ b/src/api/java/baritone/api/process/IMineProcess.java @@ -17,6 +17,8 @@ package baritone.api.process; +import baritone.api.utils.BlockListFilter; +import baritone.api.utils.IBlockFilter; import net.minecraft.block.Block; /** @@ -24,13 +26,12 @@ import net.minecraft.block.Block; * @since 9/23/2018 */ public interface IMineProcess extends IBaritoneProcess { - /** * Begin to search for and mine the specified blocks until * the number of specified items to get from the blocks that - * are mined. This is based on the first target block to mine. + * are mined. * - * @param quantity The number of items to get from blocks mined + * @param quantity The total number of items to get * @param blocks The blocks to mine */ void mineByName(int quantity, String... blocks); @@ -41,9 +42,18 @@ public interface IMineProcess extends IBaritoneProcess { * are mined. This is based on the first target block to mine. * * @param quantity The number of items to get from blocks mined - * @param blocks The blocks to mine + * @param filter The filter to run blocks through */ - void mine(int quantity, Block... blocks); + void mine(int quantity, IBlockFilter filter); + + /** + * Begin to search for and mine the specified blocks. + * + * @param filter The filter to run blocks through + */ + default void mine(IBlockFilter filter) { + mine(0, filter); + } /** * Begin to search for and mine the specified blocks. @@ -54,6 +64,16 @@ public interface IMineProcess extends IBaritoneProcess { mineByName(0, blocks); } + /** + * Begin to search for and mine the specified blocks. + * + * @param quantity The total number of items to get + * @param blocks The blocks to mine + */ + default void mine(int quantity, Block... blocks) { + mine(quantity, new BlockListFilter(blocks)); + } + /** * Begin to search for and mine the specified blocks. * diff --git a/src/api/java/baritone/api/utils/BlockListFilter.java b/src/api/java/baritone/api/utils/BlockListFilter.java new file mode 100644 index 00000000..7168687d --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockListFilter.java @@ -0,0 +1,46 @@ +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class BlockListFilter implements IBlockFilter { + private final List blocks = new ArrayList<>(); + + public BlockListFilter(List blocks) { + this.blocks.addAll(blocks); + } + + public BlockListFilter(Block... blocks) { + this.blocks.addAll(Arrays.asList(blocks)); + } + + @Override + public boolean selected(@Nonnull IBlockState blockstate) { + return false; + } + + @Override + public List blocks() { + return null; + } + + @Override + public String toString() { + return String.format( + "BlockListFilter{%s}", + String.join( + ",", + blocks.stream() + .map(Block.REGISTRY::getNameForObject) + .map(ResourceLocation::toString) + .toArray(String[]::new) + ) + ); + } +} diff --git a/src/api/java/baritone/api/utils/BlockSelector.java b/src/api/java/baritone/api/utils/BlockSelector.java new file mode 100644 index 00000000..c711d725 --- /dev/null +++ b/src/api/java/baritone/api/utils/BlockSelector.java @@ -0,0 +1,63 @@ +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nonnull; +import java.util.Collections; +import java.util.List; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.Objects.isNull; + +public class BlockSelector implements IBlockFilter { + private final Block block; + private final IBlockState blockstate; + private static final Pattern pattern = Pattern.compile("^(.+?)(?::(\\d+))?$"); + + public BlockSelector(@Nonnull String selector) { + Matcher matcher = pattern.matcher(selector); + + if (!matcher.find()) { + throw new RuntimeException("invalid block selector"); + } + + MatchResult matchResult = matcher.toMatchResult(); + boolean hasData = matchResult.groupCount() > 1; + + ResourceLocation id = new ResourceLocation(matchResult.group(1)); + + if (!Block.REGISTRY.containsKey(id)) { + throw new IllegalArgumentException("Invalid block ID"); + } + + block = Block.REGISTRY.getObject(id); + //noinspection deprecation + blockstate = hasData ? block.getStateFromMeta(Integer.parseInt(matchResult.group(2))) : null; + } + + @Override + public boolean selected(@Nonnull IBlockState blockstate) { + return blockstate.getBlock() == block && (isNull(this.blockstate) || + block.damageDropped(blockstate) == block.damageDropped(this.blockstate)); + } + + @Override + public List blocks() { + return Collections.singletonList(block); + } + + @Override + public String toString() { + return String.format("BlockSelector{block=%s,blockstate=%s}", block, blockstate); + } + + public static IBlockState stateFromItem(ItemStack stack) { + //noinspection deprecation + return Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata()); + } +} diff --git a/src/api/java/baritone/api/utils/BlockUtils.java b/src/api/java/baritone/api/utils/BlockUtils.java index a7e00608..7d148c9f 100644 --- a/src/api/java/baritone/api/utils/BlockUtils.java +++ b/src/api/java/baritone/api/utils/BlockUtils.java @@ -39,7 +39,7 @@ public class BlockUtils { public static Block stringToBlockRequired(String name) { Block block = stringToBlockNullable(name); - Objects.requireNonNull(block); + Objects.requireNonNull(block, String.format("Invalid block name %s", name)); return block; } diff --git a/src/api/java/baritone/api/utils/CompositeBlockFilter.java b/src/api/java/baritone/api/utils/CompositeBlockFilter.java new file mode 100644 index 00000000..39b52f21 --- /dev/null +++ b/src/api/java/baritone/api/utils/CompositeBlockFilter.java @@ -0,0 +1,51 @@ +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; + +public class CompositeBlockFilter implements IBlockFilter { + List filters = new ArrayList<>(); + + public CompositeBlockFilter() { + } + + public CompositeBlockFilter(List filters) { + this.filters.addAll(filters); + } + + public CompositeBlockFilter(IBlockFilter... filters) { + this.filters.addAll(asList(filters)); + } + + @Override + public boolean selected(@Nonnull IBlockState blockstate) { + return filters.stream() + .map(f -> f.selected(blockstate)) + .filter(Boolean::valueOf).findFirst() + .orElse(false); + } + + @Override + public List blocks() { + return filters.stream() + .map(IBlockFilter::blocks) + .flatMap(Collection::stream) + .collect(Collectors.toCollection(ArrayList::new)); + } + + @Override + public String toString() { + return String.format( + "CompositeBlockFilter{%s}", + String.join(",", filters.stream().map(Object::toString).toArray(String[]::new)) + ); + } +} diff --git a/src/api/java/baritone/api/utils/ExampleBaritoneControl.java b/src/api/java/baritone/api/utils/ExampleBaritoneControlOld.java similarity index 97% rename from src/api/java/baritone/api/utils/ExampleBaritoneControl.java rename to src/api/java/baritone/api/utils/ExampleBaritoneControlOld.java index 16a56667..de0cbf6b 100644 --- a/src/api/java/baritone/api/utils/ExampleBaritoneControl.java +++ b/src/api/java/baritone/api/utils/ExampleBaritoneControlOld.java @@ -26,7 +26,14 @@ import baritone.api.cache.IWaypoint; import baritone.api.cache.Waypoint; import baritone.api.event.events.ChatEvent; import baritone.api.event.listener.AbstractGameEventListener; -import baritone.api.pathing.goals.*; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalAxis; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.pathing.goals.GoalGetToBlock; +import baritone.api.pathing.goals.GoalRunAway; +import baritone.api.pathing.goals.GoalStrictDirection; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.pathing.goals.GoalYLevel; import baritone.api.process.IBaritoneProcess; import baritone.api.process.ICustomGoalProcess; import baritone.api.process.IGetToBlockProcess; @@ -48,17 +55,24 @@ import net.minecraft.world.DimensionType; import net.minecraft.world.chunk.Chunk; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.Set; import static org.apache.commons.lang3.math.NumberUtils.isCreatable; -public class ExampleBaritoneControl implements Helper, AbstractGameEventListener { +public class ExampleBaritoneControlOld implements Helper, AbstractGameEventListener { private static final String COMMAND_PREFIX = "#"; public final IBaritone baritone; public final IPlayerContext ctx; - public ExampleBaritoneControl(IBaritone baritone) { + public ExampleBaritoneControlOld(IBaritone baritone) { this.baritone = baritone; this.ctx = baritone.getPlayerContext(); baritone.getGameEventHandler().registerEventListener(this); @@ -74,7 +88,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener event.cancel(); // always cancel if using prefixControl return; } - if (!BaritoneAPI.getSettings().chatControl.value && !BaritoneAPI.getSettings().removePrefix.value) { + if (!BaritoneAPI.getSettings().chatControl.value && !BaritoneAPI.getSettings().chatControlAnyway.value) { return; } if (runCommand(msg)) { @@ -123,7 +137,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener return true; } if (msg.equals("") || msg.equals("help") || msg.equals("?")) { - ITextComponent component = MESSAGE_PREFIX.createCopy(); + ITextComponent component = Helper.getPrefix(); component.getStyle().setColor(TextFormatting.GRAY); TextComponentString helpLink = new TextComponentString(" Click here for instructions on how to use Baritone (https://github.com/cabaletta/baritone/blob/master/USAGE.md)"); helpLink.getStyle().setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://github.com/cabaletta/baritone/blob/master/USAGE.md")); @@ -166,7 +180,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener } else { String[] params = rest.split(" "); if (params[0].equals("")) { - params = new String[]{}; + params = new String[] {}; } goal = parseGoal(params); if (goal == null) { @@ -232,7 +246,7 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener return true; } if (msg.equals("version")) { - String version = ExampleBaritoneControl.class.getPackage().getImplementationVersion(); + String version = ExampleBaritoneControlOld.class.getPackage().getImplementationVersion(); if (version == null) { logDirect("No version detected. Either dev environment or broken install."); } else { @@ -510,7 +524,8 @@ public class ExampleBaritoneControl implements Helper, AbstractGameEventListener baritone.getMineProcess().mine(quantity, block); logDirect("Will mine " + quantity + " " + blockTypes[0]); return true; - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) {} + } catch (NumberFormatException | ArrayIndexOutOfBoundsException | NullPointerException ex) { + } for (String s : blockTypes) { if (BlockUtils.stringToBlockNullable(s) == null) { logDirect(s + " isn't a valid block name"); diff --git a/src/api/java/baritone/api/utils/Helper.java b/src/api/java/baritone/api/utils/Helper.java index 99683d8a..7b36a716 100755 --- a/src/api/java/baritone/api/utils/Helper.java +++ b/src/api/java/baritone/api/utils/Helper.java @@ -23,6 +23,10 @@ import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.TextComponentString; import net.minecraft.util.text.TextFormatting; +import java.util.Arrays; + +import static java.util.Arrays.asList; + /** * @author Brady * @since 8/1/2018 @@ -32,15 +36,19 @@ public interface Helper { /** * Instance of {@link Helper}. Used for static-context reference. */ - Helper HELPER = new Helper() {}; + Helper HELPER = new Helper() { + }; - ITextComponent MESSAGE_PREFIX = new TextComponentString(String.format( - "%s[%sBaritone%s]%s", - TextFormatting.DARK_PURPLE, - TextFormatting.LIGHT_PURPLE, - TextFormatting.DARK_PURPLE, - TextFormatting.GRAY - )); + static ITextComponent getPrefix() { + return new TextComponentString("") {{ + getStyle().setColor(TextFormatting.DARK_PURPLE); + appendSibling(new TextComponentString("[")); + appendSibling(new TextComponentString(BaritoneAPI.getSettings().shortBaritonePrefix.value ? "B" : "Baritone") {{ + getStyle().setColor(TextFormatting.LIGHT_PURPLE); + }}); + appendSibling(new TextComponentString("]")); + }}; + } Minecraft mc = Minecraft.getMinecraft(); @@ -58,15 +66,41 @@ public interface Helper { logDirect(message); } + /** + * Send components to chat with the [Baritone] prefix + * + * @param components The components to send + */ + default void logDirect(ITextComponent... components) { + ITextComponent component = new TextComponentString("") {{ + appendSibling(getPrefix()); + appendSibling(new TextComponentString(" ")); + asList(components).forEach(this::appendSibling); + }}; + + Minecraft.getMinecraft().addScheduledTask(() -> BaritoneAPI.getSettings().logger.value.accept(component)); + } + + /** + * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a direct response to a chat command) + * + * @param message The message to display in chat + * @param color The color to print that message in + */ + default void logDirect(String message, TextFormatting color) { + Arrays.stream(message.split("\\n")).forEach(line -> + logDirect(new TextComponentString(line.replace("\t", " ")) {{ + getStyle().setColor(color); + }}) + ); + } + /** * Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a direct response to a chat command) * * @param message The message to display in chat */ default void logDirect(String message) { - ITextComponent component = MESSAGE_PREFIX.createCopy(); - component.getStyle().setColor(TextFormatting.GRAY); - component.appendSibling(new TextComponentString(" " + message)); - Minecraft.getMinecraft().addScheduledTask(() -> BaritoneAPI.getSettings().logger.value.accept(component)); + logDirect(message, TextFormatting.GRAY); } } diff --git a/src/api/java/baritone/api/utils/IBlockFilter.java b/src/api/java/baritone/api/utils/IBlockFilter.java new file mode 100644 index 00000000..2ad44823 --- /dev/null +++ b/src/api/java/baritone/api/utils/IBlockFilter.java @@ -0,0 +1,22 @@ +package baritone.api.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; + +import javax.annotation.Nonnull; +import java.util.List; + +public interface IBlockFilter { + /** + * @param blockstate The blockstate of the block to test. + * @return If that blockstate passes this filter. + */ + boolean selected(@Nonnull IBlockState blockstate); + + /** + * @return A possibly incomplete list of blocks this filter selects. Not all states of each block may be selected, + * and this may not contain all selected blocks, but every block on this list is guaranteed to have a selected + * state. + */ + List blocks(); +} diff --git a/src/api/java/baritone/api/utils/SettingsUtil.java b/src/api/java/baritone/api/utils/SettingsUtil.java index 4e659dec..17387aa0 100644 --- a/src/api/java/baritone/api/utils/SettingsUtil.java +++ b/src/api/java/baritone/api/utils/SettingsUtil.java @@ -120,15 +120,37 @@ public class SettingsUtil { return modified; } + public static String settingTypeToString(Settings.Setting setting) { + return setting.getType().getTypeName() + .replaceAll("(?:\\w+\\.)+(\\w+)", "$1"); + } + + public static String settingValueToString(Settings.Setting setting, T value) throws IllegalArgumentException { + Parser io = Parser.getParser(setting.getType()); + + if (io == null) { + throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName()); + } + + return io.toString(new ParserContext(setting), value); + } + + public static String settingValueToString(Settings.Setting setting) throws IllegalArgumentException { + //noinspection unchecked + return settingValueToString(setting, setting.value); + } + + public static String settingDefaultToString(Settings.Setting setting) throws IllegalArgumentException { + //noinspection unchecked + return settingValueToString(setting, setting.defaultValue); + } + public static String settingToString(Settings.Setting setting) throws IllegalStateException { if (setting.getName().equals("logger")) { return "logger"; } - Parser io = Parser.getParser(setting.getType()); - if (io == null) { - throw new IllegalStateException("Missing " + setting.getValueClass() + " " + setting.getName()); - } - return setting.getName() + " " + io.toString(new ParserContext(setting), setting.value); + + return setting.getName() + " " + settingValueToString(setting); } public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException { @@ -174,6 +196,7 @@ public class SettingsUtil { INTEGER(Integer.class, Integer::parseInt), FLOAT(Float.class, Float::parseFloat), LONG(Long.class, Long::parseLong), + STRING(String.class, String::new), ENUMFACING(EnumFacing.class, EnumFacing::byName), COLOR( Color.class, diff --git a/src/api/java/baritone/api/utils/command/BaritoneChatControl.java b/src/api/java/baritone/api/utils/command/BaritoneChatControl.java new file mode 100644 index 00000000..3de8a7eb --- /dev/null +++ b/src/api/java/baritone/api/utils/command/BaritoneChatControl.java @@ -0,0 +1,231 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command; + +import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; +import baritone.api.Settings; +import baritone.api.event.events.ChatEvent; +import baritone.api.event.events.TabCompleteEvent; +import baritone.api.event.listener.AbstractGameEventListener; +import baritone.api.utils.Helper; +import baritone.api.utils.IPlayerContext; +import baritone.api.utils.SettingsUtil; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.exception.CommandNotFoundException; +import baritone.api.utils.command.execution.CommandExecution; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.api.utils.command.manager.CommandManager; +import com.mojang.realmsclient.util.Pair; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Locale; +import java.util.UUID; +import java.util.stream.Stream; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static java.util.stream.Stream.of; + +public class BaritoneChatControl implements Helper, AbstractGameEventListener { + public final IBaritone baritone; + public final IPlayerContext ctx; + public final Settings settings = BaritoneAPI.getSettings(); + public static String FORCE_COMMAND_PREFIX = String.format("<<%s>>", UUID.randomUUID().toString()); + + public BaritoneChatControl(IBaritone baritone) { + this.baritone = baritone; + this.ctx = baritone.getPlayerContext(); + baritone.getGameEventHandler().registerEventListener(this); + } + + @Override + public void onSendChatMessage(ChatEvent event) { + String msg = event.getMessage(); + String prefix = settings.prefix.value; + boolean forceRun = msg.startsWith(FORCE_COMMAND_PREFIX); + + if (!forceRun && !settings.chatControl.value && !settings.chatControlAnyway.value && !settings.prefixControl.value) { + return; + } + + if ((settings.prefixControl.value && msg.startsWith(prefix)) || forceRun) { + event.cancel(); + + String commandStr = msg.substring(forceRun ? FORCE_COMMAND_PREFIX.length() : prefix.length()); + + if (!runCommand(commandStr) && !commandStr.trim().isEmpty()) { + new CommandNotFoundException(CommandExecution.expand(commandStr).first()).handle(null, null); + } + + return; + } + + if ((settings.chatControl.value || settings.chatControlAnyway.value) && runCommand(msg)) { + event.cancel(); + } + } + + private void logRanCommand(String msg) { + if (settings.echoCommands.value) { + logDirect(new TextComponentString(String.format("> %s", msg)) {{ + getStyle() + .setColor(TextFormatting.WHITE) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to rerun command") + )) + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + msg + )); + }}); + } + } + + public boolean runCommand(String msg) { + if (msg.trim().equalsIgnoreCase("damn")) { + logDirect("daniel"); + return false; + } else if (msg.trim().equalsIgnoreCase("orderpizza")) { + try { + ((Lol) mc.currentScreen).openLink(new URI("https://www.dominos.com/en/pages/order/")); + } catch (NullPointerException | URISyntaxException ignored) { + } + + return false; + } + + if (msg.isEmpty()) { + msg = "help"; + } + + Pair> pair = CommandExecution.expand(msg); + ArgConsumer argc = new ArgConsumer(pair.second()); + + if (!argc.has()) { + for (Settings.Setting setting : settings.allSettings) { + if (setting.getName().equals("logger")) { + continue; + } + + if (setting.getName().equalsIgnoreCase(pair.first())) { + logRanCommand(msg); + + if (setting.getValueClass() == Boolean.class) { + CommandManager.execute(String.format("set toggle %s", setting.getName())); + } else { + CommandManager.execute(String.format("set %s", setting.getName())); + } + + return true; + } + } + } else if (argc.hasExactlyOne()) { + for (Settings.Setting setting : settings.allSettings) { + if (setting.getName().equals("logger")) { + continue; + } + + if (setting.getName().equalsIgnoreCase(pair.first())) { + logRanCommand(msg); + CommandManager.execute(String.format("set %s %s", setting.getName(), argc.getS())); + return true; + } + } + } + + CommandExecution execution = CommandExecution.from(pair); + + if (isNull(execution)) { + return false; + } + + logRanCommand(msg); + CommandManager.execute(execution); + + return true; + } + + @Override + public void onPreTabComplete(TabCompleteEvent.Pre event) { + if (!settings.prefixControl.value) { + return; + } + + String prefix = event.prefix.get(); + String commandPrefix = settings.prefix.value; + + if (!prefix.startsWith(commandPrefix)) { + return; + } + + String msg = prefix.substring(commandPrefix.length()); + + List args = CommandArgument.from(msg, true); + Stream stream = tabComplete(msg); + + if (args.size() == 1) { + stream = stream.map(x -> commandPrefix + x); + } + + event.completions.set(stream.toArray(String[]::new)); + } + + public Stream tabComplete(String msg) { + List args = CommandArgument.from(msg, true); + ArgConsumer argc = new ArgConsumer(args); + + if (argc.hasAtMost(2)) { + if (argc.hasExactly(1)) { + return new TabCompleteHelper() + .addCommands() + .addSettings() + .filterPrefix(argc.getS()) + .stream(); + } + + Settings.Setting setting = settings.byLowerName.get(argc.getS().toLowerCase(Locale.US)); + + if (nonNull(setting)) { + if (setting.getValueClass() == Boolean.class) { + TabCompleteHelper helper = new TabCompleteHelper(); + + if ((Boolean) setting.value) { + helper.append(of("true", "false")); + } else { + helper.append(of("false", "true")); + } + + return helper.filterPrefix(argc.getS()).stream(); + } else { + return of(SettingsUtil.settingValueToString(setting)); + } + } + } + + return CommandManager.tabComplete(msg); + } +} diff --git a/src/api/java/baritone/api/utils/command/Command.java b/src/api/java/baritone/api/utils/command/Command.java new file mode 100644 index 00000000..26fca9e1 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/Command.java @@ -0,0 +1,114 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command; + +import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; +import baritone.api.Settings; +import baritone.api.utils.Helper; +import baritone.api.utils.IPlayerContext; +import baritone.api.utils.command.execution.CommandExecution; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.client.Minecraft; + +import java.util.ArrayList; +import java.util.Arrays; +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 = BaritoneAPI.getProvider().getPrimaryBaritone(); + protected IPlayerContext ctx = baritone.getPlayerContext(); + protected Minecraft MC = mc; + + /** + * The names of this command. This is what you put after the command prefix. + */ + public final List names; + + /** + * A single-line string containing a short description of this command's purpose. + */ + public final String shortDesc; + + /** + * Creates a new Baritone control command. + * + * @param names The names of this command. This is what you put after the command prefix. + * @param shortDesc A single-line string containing a short description of this command's purpose. + */ + protected Command(List names, String shortDesc) { + this.names = names.stream() + .map(s -> s.toLowerCase(Locale.US)) + .collect(Collectors.toCollection(ArrayList::new)); + this.shortDesc = shortDesc; + } + + protected Command(String name, String shortDesc) { + this(Collections.singletonList(name), shortDesc); + } + + /** + * Executes this command with the specified arguments. + * + * @param execution The command execution to execute this command with + */ + public void execute(CommandExecution execution) { + executed(execution.label, execution.args, execution.settings); + } + + /** + * Tab completes this command with the specified arguments. This won't throw any exceptions ever. + * + * @param execution The command execution to tab complete + * @return The list of completions. + */ + public Stream tabComplete(CommandExecution execution) { + try { + return tabCompleted(execution.label, execution.args, execution.settings); + } catch (Throwable t) { + return Arrays.stream(new String[0]); + } + } + + /** + * Called when this command is executed. + */ + protected abstract void executed(String label, ArgConsumer args, Settings settings); + + /** + * Called when the command needs to tab complete. Return a Stream representing the entries to put in the completions + * list. + */ + protected abstract Stream tabCompleted(String label, ArgConsumer args, Settings settings); + + /** + * @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; + } +} diff --git a/src/api/java/baritone/api/utils/command/Lol.java b/src/api/java/baritone/api/utils/command/Lol.java new file mode 100644 index 00000000..5c56544e --- /dev/null +++ b/src/api/java/baritone/api/utils/command/Lol.java @@ -0,0 +1,7 @@ +package baritone.api.utils.command; + +import java.net.URI; + +public interface Lol { + void openLink(URI url); +} diff --git a/src/api/java/baritone/api/utils/command/argparser/ArgParser.java b/src/api/java/baritone/api/utils/command/argparser/ArgParser.java new file mode 100644 index 00000000..8fc08e86 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/ArgParser.java @@ -0,0 +1,45 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.argparser; + +public abstract class ArgParser implements IArgParser { + private final Class klass; + + protected ArgParser(Class klass) { + this.klass = klass; + } + + @Override + public Class getKlass() { + return klass; + } + + public static abstract class Stated extends ArgParser implements IArgParser.Stated { + private final Class stateKlass; + + protected Stated(Class klass, Class stateKlass) { + super(klass); + this.stateKlass = stateKlass; + } + + @Override + public Class getStateKlass() { + return stateKlass; + } + } +} diff --git a/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java b/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java new file mode 100644 index 00000000..6b5d4761 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/ArgParserManager.java @@ -0,0 +1,100 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.argparser; + +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.exception.CommandNoParserForTypeException; +import baritone.api.utils.command.registry.Registry; + +import java.util.Iterator; + +import static java.util.Objects.isNull; + +public class ArgParserManager { + public static final Registry REGISTRY = new Registry<>(); + + static { + DefaultArgParsers.all.forEach(REGISTRY::register); + } + + /** + * @param klass The class to search for. + * @return A parser that can parse arguments into this class, if found. + */ + public static ArgParser.Stateless getParserStateless(Class klass) { + for (Iterator it = REGISTRY.descendingIterator(); it.hasNext(); ) { + ArgParser parser = it.next(); + + if (parser instanceof ArgParser.Stateless && parser.getKlass().isAssignableFrom(klass)) { + //noinspection unchecked + return (ArgParser.Stateless) parser; + } + } + + return null; + } + + /** + * @param klass The class to search for. + * @return A parser that can parse arguments into this class, if found. + */ + public static ArgParser.Stated getParserStated(Class klass, Class stateKlass) { + for (Iterator it = REGISTRY.descendingIterator(); it.hasNext(); ) { + ArgParser parser = it.next(); + + //noinspection unchecked + if (parser instanceof ArgParser.Stated + && parser.getKlass().isAssignableFrom(klass) + && ((ArgParser.Stated) parser).getStateKlass().isAssignableFrom(stateKlass)) { + //noinspection unchecked + return (ArgParser.Stated) parser; + } + } + + return null; + } + + public static T parseStateless(Class klass, CommandArgument arg) { + ArgParser.Stateless parser = getParserStateless(klass); + + if (isNull(parser)) { + throw new CommandNoParserForTypeException(klass); + } + + try { + return parser.parseArg(arg); + } catch (RuntimeException exc) { + throw new CommandInvalidTypeException(arg, klass.getSimpleName()); + } + } + + public static T parseStated(Class klass, Class stateKlass, CommandArgument arg, S state) { + ArgParser.Stated parser = getParserStated(klass, stateKlass); + + if (isNull(parser)) { + throw new CommandNoParserForTypeException(klass); + } + + try { + return parser.parseArg(arg, state); + } catch (RuntimeException exc) { + throw new CommandInvalidTypeException(arg, klass.getSimpleName()); + } + } +} diff --git a/src/api/java/baritone/api/utils/command/argparser/DefaultArgParsers.java b/src/api/java/baritone/api/utils/command/argparser/DefaultArgParsers.java new file mode 100644 index 00000000..301ad891 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/DefaultArgParsers.java @@ -0,0 +1,110 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.argparser; + +import baritone.api.utils.command.argument.CommandArgument; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +import static java.util.Arrays.asList; + +public class DefaultArgParsers { + public static class IntArgumentParser extends ArgParser implements IArgParser.Stateless { + public static final IntArgumentParser INSTANCE = new IntArgumentParser(); + + public IntArgumentParser() { + super(Integer.class); + } + + @Override + public Integer parseArg(CommandArgument arg) throws RuntimeException { + return Integer.parseInt(arg.value); + } + } + + public static class FloatArgumentParser extends ArgParser implements IArgParser.Stateless { + public static final FloatArgumentParser INSTANCE = new FloatArgumentParser(); + + public FloatArgumentParser() { + super(Float.class); + } + + @Override + public Float parseArg(CommandArgument arg) throws RuntimeException { + String value = arg.value; + + if (!value.matches("^[+-]?\\d+(?:\\.\\d+)$")) { + throw new RuntimeException("failed float format check"); + } + + return Float.parseFloat(value); + } + } + + public static class DoubleArgumentParser extends ArgParser implements IArgParser.Stateless { + public static final DoubleArgumentParser INSTANCE = new DoubleArgumentParser(); + + public DoubleArgumentParser() { + super(Double.class); + } + + @Override + public Double parseArg(CommandArgument arg) throws RuntimeException { + String value = arg.value; + + if (!value.matches("^[+-]?\\d+(?:\\.\\d+)$")) { + throw new RuntimeException("failed double format check"); + } + + return Double.parseDouble(value); + } + } + + public static class BooleanArgumentParser extends ArgParser implements IArgParser.Stateless { + public static final BooleanArgumentParser INSTANCE = new BooleanArgumentParser(); + + public static final List TRUTHY_VALUES = asList("1", "true", "yes", "t", "y", "on", "enable"); + public static final List FALSY_VALUES = asList("0", "false", "no", "f", "n", "off", "disable"); + + public BooleanArgumentParser() { + super(Boolean.class); + } + + @Override + public Boolean parseArg(CommandArgument arg) throws RuntimeException { + String value = arg.value; + + if (TRUTHY_VALUES.contains(value.toLowerCase(Locale.US))) { + return true; + } else if (FALSY_VALUES.contains(value.toLowerCase(Locale.US))) { + return false; + } else { + throw new RuntimeException("invalid boolean"); + } + } + } + + public static final List> all = Collections.unmodifiableList(asList( + IntArgumentParser.INSTANCE, + FloatArgumentParser.INSTANCE, + DoubleArgumentParser.INSTANCE, + BooleanArgumentParser.INSTANCE + )); +} diff --git a/src/api/java/baritone/api/utils/command/argparser/IArgParser.java b/src/api/java/baritone/api/utils/command/argparser/IArgParser.java new file mode 100644 index 00000000..1d8890ed --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argparser/IArgParser.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.utils.command.argparser; + +import baritone.api.utils.command.argument.CommandArgument; + +public interface IArgParser { + /** + * @return the class of this parser. + */ + Class getKlass(); + + 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(CommandArgument arg) throws RuntimeException; + } + + interface Stated extends IArgParser { + Class getStateKlass(); + + /** + * @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(CommandArgument arg, S state) throws RuntimeException; + } +} diff --git a/src/api/java/baritone/api/utils/command/argument/CommandArgument.java b/src/api/java/baritone/api/utils/command/argument/CommandArgument.java new file mode 100644 index 00000000..5b507fcd --- /dev/null +++ b/src/api/java/baritone/api/utils/command/argument/CommandArgument.java @@ -0,0 +1,104 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.argument; + +import baritone.api.utils.command.argparser.ArgParserManager; +import baritone.api.utils.command.exception.CommandInvalidTypeException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@SuppressWarnings("UnusedReturnValue") +public class CommandArgument { + public final int index; + public final String value; + public final String rawRest; + public final static Pattern argPattern = Pattern.compile("\\S+"); + + private CommandArgument(int index, String value, String rawRest) { + this.index = index; + this.value = value; + this.rawRest = rawRest; + } + + public > E getE(Class enumClass) { + //noinspection OptionalGetWithoutIsPresent + return Arrays.stream(enumClass.getEnumConstants()) + .filter(e -> e.name().equalsIgnoreCase(value)) + .findFirst() + .get(); + } + + public T getAs(Class type) { + return ArgParserManager.parseStateless(type, this); + } + + public boolean is(Class type) { + try { + getAs(type); + return true; + } catch (Throwable t) { + return false; + } + } + + public T getAs(Class type, Class stateType, S state) { + return ArgParserManager.parseStated(type, stateType, this, state); + } + + public boolean is(Class type, Class stateType, S state) { + try { + getAs(type, stateType, state); + return true; + } catch (Throwable t) { + return false; + } + } + + public static List from(String string, boolean preserveEmptyLast) { + List args = new ArrayList<>(); + Matcher argMatcher = argPattern.matcher(string); + int lastEnd = -1; + while (argMatcher.find()) { + args.add(new CommandArgument( + args.size(), + argMatcher.group(), + string.substring(argMatcher.start()) + )); + + lastEnd = argMatcher.end(); + } + + if (preserveEmptyLast && lastEnd < string.length()) { + args.add(new CommandArgument(args.size(), "", "")); + } + + return args; + } + + public static List from(String string) { + return from(string, false); + } + + public static CommandArgument unknown() { + return new CommandArgument(-1, "", ""); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/BlockById.java b/src/api/java/baritone/api/utils/command/datatypes/BlockById.java new file mode 100644 index 00000000..c8cfe274 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/BlockById.java @@ -0,0 +1,43 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.util.ResourceLocation; + +import java.util.stream.Stream; + +public class BlockById implements IDatatypeFor { + public final Block block; + + public BlockById() { + block = null; + } + + public BlockById(ArgConsumer consumer) { + ResourceLocation id = new ResourceLocation(consumer.getS()); + + if ((block = Block.REGISTRY.getObject(id)) == Blocks.AIR) { + throw new RuntimeException("no block found by that id"); + } + } + + @Override + public Block get() { + return block; + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + return new TabCompleteHelper() + .append( + Block.REGISTRY.getKeys() + .stream() + .map(Object::toString) + ) + .filterPrefixNamespaced(consumer.getS()) + .sortAlphabetically() + .stream(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/EntityClassById.java b/src/api/java/baritone/api/utils/command/datatypes/EntityClassById.java new file mode 100644 index 00000000..eb841724 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/EntityClassById.java @@ -0,0 +1,45 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.util.ResourceLocation; + +import java.util.stream.Stream; + +import static java.util.Objects.isNull; + +public class EntityClassById implements IDatatypeFor> { + public final Class entity; + + public EntityClassById() { + entity = null; + } + + public EntityClassById(ArgConsumer consumer) { + ResourceLocation id = new ResourceLocation(consumer.getS()); + + if (isNull(entity = EntityList.REGISTRY.getObject(id))) { + throw new RuntimeException("no entity found by that id"); + } + } + + @Override + public Class get() { + return entity; + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + return new TabCompleteHelper() + .append( + EntityList.getEntityNameList() + .stream() + .map(Object::toString) + ) + .filterPrefixNamespaced(consumer.getS()) + .sortAlphabetically() + .stream(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/ForBlockSelector.java b/src/api/java/baritone/api/utils/command/datatypes/ForBlockSelector.java new file mode 100644 index 00000000..7b7dbf72 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/ForBlockSelector.java @@ -0,0 +1,28 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.BlockSelector; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +public class ForBlockSelector implements IDatatypeFor { + public final BlockSelector selector; + + public ForBlockSelector() { + selector = null; + } + + public ForBlockSelector(ArgConsumer consumer) { + selector = new BlockSelector(consumer.getS()); + } + + @Override + public BlockSelector get() { + return selector; + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + return consumer.tabCompleteDatatype(BlockById.class); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatype.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatype.java new file mode 100644 index 00000000..35caeaa2 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatype.java @@ -0,0 +1,17 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.exception.CommandInvalidArgumentException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +/** + * Since interfaces cannot enforce the presence of a constructor, it's on you to make sure there is a constructor that + * accepts a single {@link ArgConsumer} argument. The constructor will perform all needed validation, and + * {@link ArgConsumer#getDatatype(Class)} will handle RuntimeExceptions and translate them into + * {@link CommandInvalidArgumentException}s. There must always be a constructor with no arguments so that + * {@link ArgConsumer} can create an instance for tab completion. + */ +public interface IDatatype { + Stream tabComplete(ArgConsumer consumer); +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatypeFor.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatypeFor.java new file mode 100644 index 00000000..313fd6b1 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatypeFor.java @@ -0,0 +1,5 @@ +package baritone.api.utils.command.datatypes; + +public interface IDatatypeFor extends IDatatype { + T get(); +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/IDatatypePost.java b/src/api/java/baritone/api/utils/command/datatypes/IDatatypePost.java new file mode 100644 index 00000000..2168f25a --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/IDatatypePost.java @@ -0,0 +1,5 @@ +package baritone.api.utils.command.datatypes; + +public interface IDatatypePost extends IDatatype { + T apply(O original); +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/PlayerByUsername.java b/src/api/java/baritone/api/utils/command/datatypes/PlayerByUsername.java new file mode 100644 index 00000000..07e765e5 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/PlayerByUsername.java @@ -0,0 +1,53 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.BaritoneAPI; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.entity.player.EntityPlayer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Objects.isNull; + +public class PlayerByUsername implements IDatatypeFor { + private final List players = + BaritoneAPI.getProvider().getPrimaryBaritone().getPlayerContext().world().playerEntities; + public final EntityPlayer player; + + public PlayerByUsername() { + player = null; + } + + public PlayerByUsername(ArgConsumer consumer) { + String username = consumer.getS(); + + if (isNull( + player = players + .stream() + .filter(s -> s.getName().equalsIgnoreCase(username)) + .findFirst() + .orElse(null) + )) { + throw new RuntimeException("no player found by that username"); + } + } + + @Override + public EntityPlayer get() { + return player; + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + return new TabCompleteHelper() + .append( + players + .stream() + .map(EntityPlayer::getName) + ) + .filterPrefix(consumer.getS()) + .sortAlphabetically() + .stream(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeBlockPos.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeBlockPos.java new file mode 100644 index 00000000..788986b1 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeBlockPos.java @@ -0,0 +1,54 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +import static java.util.Objects.isNull; + +public class RelativeBlockPos implements IDatatypePost { + final RelativeCoordinate x; + final RelativeCoordinate y; + final RelativeCoordinate z; + + public RelativeBlockPos() { + x = null; + y = null; + z = null; + } + + public RelativeBlockPos(ArgConsumer consumer) { + x = consumer.getDatatype(RelativeCoordinate.class); + y = consumer.getDatatype(RelativeCoordinate.class); + z = consumer.getDatatype(RelativeCoordinate.class); + } + + @Override + public BetterBlockPos apply(BetterBlockPos origin) { + return new BetterBlockPos( + x.apply((double) origin.x), + y.apply((double) origin.y), + z.apply((double) origin.z) + ); + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + if (consumer.has() && !consumer.has(4)) { + while (consumer.has(2)) { + if (isNull(consumer.peekDatatypeOrNull(RelativeCoordinate.class))) { + break; + } + + consumer.get(); + + if (!consumer.has(2)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.class); + } + } + } + + return Stream.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeCoordinate.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeCoordinate.java new file mode 100644 index 00000000..9516fea7 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeCoordinate.java @@ -0,0 +1,57 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.math.MathHelper; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +public class RelativeCoordinate implements IDatatypePost { + public static Pattern PATTERN = Pattern.compile("^(~?)([+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)|)$"); + + final boolean isRelative; + final double offset; + + public RelativeCoordinate() { + isRelative = true; + offset = 0; + } + + public RelativeCoordinate(ArgConsumer consumer) { + if (!consumer.has()) { + throw new RuntimeException("relative coordinate requires an argument"); + } + + Matcher matcher = PATTERN.matcher(consumer.getS()); + + if (!matcher.matches()) { + throw new RuntimeException("pattern doesn't match"); + } + + isRelative = !matcher.group(1).isEmpty(); + offset = matcher.group(2).isEmpty() ? 0 : Double.parseDouble(matcher.group(2)); + } + + @Override + public Double apply(Double origin) { + if (isRelative) { + return origin + offset; + } + + return offset; + } + + public int applyFloor(double origin) { + return MathHelper.floor(apply(origin)); + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + if (!consumer.has(2) && consumer.getS().matches("^(~|$)")) { + return Stream.of("~"); + } + + return Stream.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeFile.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeFile.java new file mode 100644 index 00000000..58013f4f --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeFile.java @@ -0,0 +1,51 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.io.File; +import java.nio.file.FileSystems; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.Stream; + +public class RelativeFile implements IDatatypePost { + private final Path path; + + public RelativeFile() { + path = null; + } + + public RelativeFile(ArgConsumer consumer) { + try { + path = FileSystems.getDefault().getPath(consumer.getS()); + } catch (InvalidPathException e) { + throw new RuntimeException("invalid path"); + } + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + return Stream.empty(); + } + + public static Stream tabComplete(ArgConsumer consumer, File base) { + String currentPathStringThing = consumer.getS(); + 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 Arrays.stream(Objects.requireNonNull((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))); + } + + @Override + public File apply(File original) { + return original.toPath().resolve(path).toFile(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeGoal.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoal.java new file mode 100644 index 00000000..13033549 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoal.java @@ -0,0 +1,64 @@ +package baritone.api.utils.command.datatypes; + +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.utils.command.helpers.arguments.ArgConsumer; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Objects.nonNull; + +public class RelativeGoal implements IDatatypePost { + final RelativeCoordinate[] coords; + + public RelativeGoal() { + coords = new RelativeCoordinate[0]; + } + + public RelativeGoal(ArgConsumer consumer) { + List coordsList = new ArrayList<>(); + + for (int i = 0; i < 3; i++) { + if (nonNull(consumer.peekDatatypeOrNull(RelativeCoordinate.class))) { + coordsList.add(consumer.getDatatype(RelativeCoordinate.class)); + } + } + + coords = coordsList.toArray(new RelativeCoordinate[0]); + } + + @Override + public Goal apply(BetterBlockPos origin) { + switch (coords.length) { + case 0: + return new GoalBlock(origin); + case 1: + return new GoalYLevel( + coords[0].applyFloor(origin.y) + ); + case 2: + return new GoalXZ( + coords[0].applyFloor(origin.x), + coords[1].applyFloor(origin.z) + ); + case 3: + return new GoalBlock( + coords[0].applyFloor(origin.x), + coords[1].applyFloor(origin.y), + coords[2].applyFloor(origin.z) + ); + default: + throw new IllegalStateException("Unexpected coords size: " + coords.length); + } + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + return consumer.tabCompleteDatatype(RelativeCoordinate.class); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalBlock.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalBlock.java new file mode 100644 index 00000000..28ff08bb --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalBlock.java @@ -0,0 +1,42 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +public class RelativeGoalBlock implements IDatatypePost { + final RelativeCoordinate[] coords; + + public RelativeGoalBlock() { + coords = new RelativeCoordinate[0]; + } + + public RelativeGoalBlock(ArgConsumer consumer) { + coords = new RelativeCoordinate[] { + consumer.getDatatype(RelativeCoordinate.class), + consumer.getDatatype(RelativeCoordinate.class), + consumer.getDatatype(RelativeCoordinate.class) + }; + } + + @Override + public GoalBlock apply(BetterBlockPos origin) { + return new GoalBlock( + coords[0].applyFloor(origin.x), + coords[1].applyFloor(origin.y), + coords[2].applyFloor(origin.z) + ); + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + if (consumer.hasAtMost(3)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.class); + } + + return Stream.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalXZ.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalXZ.java new file mode 100644 index 00000000..8607c512 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalXZ.java @@ -0,0 +1,40 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +public class RelativeGoalXZ implements IDatatypePost { + final RelativeCoordinate[] coords; + + public RelativeGoalXZ() { + coords = new RelativeCoordinate[0]; + } + + public RelativeGoalXZ(ArgConsumer consumer) { + coords = new RelativeCoordinate[] { + consumer.getDatatype(RelativeCoordinate.class), + consumer.getDatatype(RelativeCoordinate.class) + }; + } + + @Override + public GoalXZ apply(BetterBlockPos origin) { + return new GoalXZ( + coords[0].applyFloor(origin.x), + coords[1].applyFloor(origin.z) + ); + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + if (consumer.hasAtMost(2)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.class); + } + + return Stream.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalYLevel.java b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalYLevel.java new file mode 100644 index 00000000..041eeded --- /dev/null +++ b/src/api/java/baritone/api/utils/command/datatypes/RelativeGoalYLevel.java @@ -0,0 +1,34 @@ +package baritone.api.utils.command.datatypes; + +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalYLevel; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.stream.Stream; + +public class RelativeGoalYLevel implements IDatatypePost { + final RelativeCoordinate coord; + + public RelativeGoalYLevel() { + coord = null; + } + + public RelativeGoalYLevel(ArgConsumer consumer) { + coord = consumer.getDatatype(RelativeCoordinate.class); + } + + @Override + public GoalYLevel apply(BetterBlockPos origin) { + return new GoalYLevel(coord.applyFloor(origin.y)); + } + + @Override + public Stream tabComplete(ArgConsumer consumer) { + if (consumer.hasAtMost(1)) { + return consumer.tabCompleteDatatype(RelativeCoordinate.class); + } + + return Stream.empty(); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/AxisCommand.java b/src/api/java/baritone/api/utils/command/defaults/AxisCommand.java new file mode 100644 index 00000000..367bc698 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/AxisCommand.java @@ -0,0 +1,58 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalAxis; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class AxisCommand extends Command { + public AxisCommand() { + super(asList("axis", "highway"), "Set a goal to the axes"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + Goal goal = new GoalAxis(); + baritone.getCustomGoalProcess().setGoal(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The axis command sets a goal that tells Baritone to head towards the nearest axis. That is, X=0 or Z=0.", + "", + "Usage:", + "> axis" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/BlacklistCommand.java b/src/api/java/baritone/api/utils/command/defaults/BlacklistCommand.java new file mode 100644 index 00000000..e51fa5fc --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/BlacklistCommand.java @@ -0,0 +1,66 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.process.IGetToBlockProcess; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class BlacklistCommand extends Command { + public BlacklistCommand() { + super("blacklist", "Blacklist closest block"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + IGetToBlockProcess proc = baritone.getGetToBlockProcess(); + + if (!proc.isActive()) { + throw new CommandInvalidStateException("GetToBlockProcess is not currently active"); + } + + if (proc.blacklistClosest()) { + logDirect("Blacklisted closest instances"); + } else { + throw new CommandInvalidStateException("No known locations, unable to blacklist"); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "While, for example, mining, this command blacklists the closest block so that Baritone won't attempt to get to it.", + "", + "Usage:", + "> blacklist" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/BuildCommand.java b/src/api/java/baritone/api/utils/command/defaults/BuildCommand.java new file mode 100644 index 00000000..ab6b3371 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/BuildCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeBlockPos; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class BuildCommand extends Command { + public BuildCommand() { + super("build", "Build a schematic"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + String filename = String.format("%s.schematic", args.getS()); + BetterBlockPos origin = ctx.playerFeet(); + BetterBlockPos buildOrigin; + + if (args.has()) { + args.requireMax(3); + buildOrigin = args.getDatatype(RelativeBlockPos.class).apply(origin); + } else { + args.requireMax(0); + buildOrigin = origin; + } + + boolean success = baritone.getBuilderProcess().build(filename, buildOrigin); + + if (!success) { + throw new CommandInvalidStateException("Couldn't load the schematic"); + } + + logDirect(String.format("Successfully loaded schematic '%s' for building\nOrigin: %s", filename, buildOrigin)); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.has(2)) { + args.get(); + + return args.tabCompleteDatatype(RelativeBlockPos.class); + } + + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Build a schematic from a file.", + "", + "Usage:", + "> build - Loads and builds '.schematic'", + "> build - Custom position" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/CancelCommand.java b/src/api/java/baritone/api/utils/command/defaults/CancelCommand.java new file mode 100644 index 00000000..416feae5 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/CancelCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class CancelCommand extends Command { + public CancelCommand() { + super(asList("cancel", "stop"), "Cancel what Baritone is currently doing"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + baritone.getPathingBehavior().cancelEverything(); + logDirect("ok canceled"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The cancel command tells Baritons to stop whatever it's currently doing.", + "", + "Usage:", + "> cancel" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ChestsCommand.java b/src/api/java/baritone/api/utils/command/defaults/ChestsCommand.java new file mode 100644 index 00000000..300cf974 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ChestsCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.cache.IRememberedInventory; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.ITextComponent; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class ChestsCommand extends Command { + public ChestsCommand() { + super("chests", "Display remembered inventories"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + Set> entries = + ctx.worldData().getContainerMemory().getRememberedInventories().entrySet(); + + if (entries.isEmpty()) { + throw new CommandInvalidStateException("No remembered inventories"); + } + + for (Map.Entry entry : entries) { + BlockPos pos = entry.getKey(); + IRememberedInventory inv = entry.getValue(); + + logDirect(pos.toString()); + + for (ItemStack item : inv.getContents()) { + ITextComponent component = item.getTextComponent(); + component.appendText(String.format(" x %d", item.getCount())); + logDirect(component); + } + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The chests command lists remembered inventories, I guess?", + "", + "Usage:", + "> chests" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ClearareaCommand.java b/src/api/java/baritone/api/utils/command/defaults/ClearareaCommand.java new file mode 100644 index 00000000..f3da2913 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ClearareaCommand.java @@ -0,0 +1,78 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeBlockPos; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class ClearareaCommand extends Command { + public ClearareaCommand() { + super("cleararea", "Clear an area of all blocks"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + BetterBlockPos pos1 = ctx.playerFeet(); + BetterBlockPos pos2; + + if (args.has()) { + args.requireMax(3); + pos2 = args.getDatatype(RelativeBlockPos.class).apply(pos1); + } else { + args.requireMax(0); + + Goal goal = baritone.getCustomGoalProcess().getGoal(); + + if (!(goal instanceof GoalBlock)) { + throw new CommandInvalidStateException("Goal is not a GoalBlock"); + } else { + pos2 = new BetterBlockPos(((GoalBlock) goal).getGoalPos()); + } + } + + baritone.getBuilderProcess().clearArea(pos1, pos2); + logDirect("Success"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return args.tabCompleteDatatype(RelativeBlockPos.class); + } + + @Override + public List getLongDesc() { + return asList( + "Clear an area of all blocks.", + "", + "Usage:", + "> cleararea - Clears the area marked by your current position and the current GoalBlock", + "> cleararea - Custom second corner rather than your goal" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ComeCommand.java b/src/api/java/baritone/api/utils/command/defaults/ComeCommand.java new file mode 100644 index 00000000..9817afb7 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ComeCommand.java @@ -0,0 +1,68 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.GoalBlock; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.entity.Entity; +import net.minecraft.util.math.BlockPos; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; + +public class ComeCommand extends Command { + public ComeCommand() { + super("come", "Start heading towards your camera"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + Entity entity = MC.getRenderViewEntity(); + + if (isNull(entity)) { + throw new CommandInvalidStateException("render view entity is null"); + } + + baritone.getCustomGoalProcess().setGoalAndPath(new GoalBlock(new BlockPos(entity))); + logDirect("Coming"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The come command tells Baritone to head towards your camera.", + "", + "I'm... not actually sure how useful this is, to be honest.", + "", + "Usage:", + "> come" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/CommandAlias.java b/src/api/java/baritone/api/utils/command/defaults/CommandAlias.java new file mode 100644 index 00000000..94fcd4ed --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/CommandAlias.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.manager.CommandManager; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public class CommandAlias extends Command { + public final String target; + + public CommandAlias(List names, String shortDesc, String target) { + super(names, shortDesc); + this.target = target; + } + + public CommandAlias(String name, String shortDesc, String target) { + super(name, shortDesc); + this.target = target; + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + CommandManager.execute(String.format("%s %s", target, args.rawRest())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return CommandManager.tabComplete(String.format("%s %s", target, args.rawRest())); + } + + @Override + public List getLongDesc() { + return Collections.singletonList(String.format("This command is an alias, for: %s ...", target)); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/DefaultCommands.java b/src/api/java/baritone/api/utils/command/defaults/DefaultCommands.java new file mode 100644 index 00000000..18e0bc9d --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/DefaultCommands.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.utils.command.defaults; + +import baritone.api.utils.command.Command; + +import java.util.Collections; +import java.util.List; + +import static java.util.Arrays.asList; + +public class DefaultCommands { + public static final List commands = Collections.unmodifiableList(asList( + new HelpCommand(), + new SetCommand(), + new CommandAlias(asList("modified", "mod", "baritone", "modifiedsettings"), "List modified settings", "set modified"), + new CommandAlias("reset", "Reset all settings or just one", "set reset"), + new ExcCommand(), // TODO: remove this debug command... eventually + new GoalCommand(), + new PathCommand(), + new ProcCommand(), + new VersionCommand(), + new RepackCommand(), + new BuildCommand(), + new SchematicaCommand(), + new ComeCommand(), + new AxisCommand(), + new CancelCommand(), + new ForceCancelCommand(), + new GcCommand(), + new InvertCommand(), + new ClearareaCommand(), + PauseResumeCommands.pauseCommand, + PauseResumeCommands.resumeCommand, + PauseResumeCommands.pausedCommand, + new TunnelCommand(), + new RenderCommand(), + new FarmCommand(), + new ChestsCommand(), + new FollowCommand(), + new ExploreFilterCommand(), + new ReloadAllCommand(), + new SaveAllCommand(), + new ExploreCommand(), + new BlacklistCommand(), + new FindCommand(), + new MineCommand() + )); +} diff --git a/src/api/java/baritone/api/utils/command/defaults/EmptyCommand.java b/src/api/java/baritone/api/utils/command/defaults/EmptyCommand.java new file mode 100644 index 00000000..1b5934ba --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/EmptyCommand.java @@ -0,0 +1,54 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.Helper; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class EmptyCommand extends Command { + public EmptyCommand() { + super(asList("name1", "name2"), "Short description"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + ; + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "", + "", + "Usage:", + "> " + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ExcCommand.java b/src/api/java/baritone/api/utils/command/defaults/ExcCommand.java new file mode 100644 index 00000000..4c05bd02 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ExcCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public class ExcCommand extends Command { + public ExcCommand() { + super("exc", "Throw an unhandled exception"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + throw new RuntimeException("HI THERE"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return Collections.emptyList(); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ExploreCommand.java b/src/api/java/baritone/api/utils/command/defaults/ExploreCommand.java new file mode 100644 index 00000000..a4b2d746 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ExploreCommand.java @@ -0,0 +1,71 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.GoalXZ; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeGoalXZ; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class ExploreCommand extends Command { + public ExploreCommand() { + super("explore", "Explore things"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + if (args.has()) { + args.requireExactly(2); + } else { + args.requireMax(0); + } + + GoalXZ goal = args.has() + ? args.getDatatypePost(RelativeGoalXZ.class, ctx.playerFeet()) + : new GoalXZ(ctx.playerFeet()); + + baritone.getExploreProcess().explore(goal.getX(), goal.getZ()); + logDirect(String.format("Exploring from %s", goal.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.hasAtMost(2)) { + return args.tabCompleteDatatype(RelativeGoalXZ.class); + } + + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Tell Baritone to explore randomly. If you used explorefilter before this, it will be applied.", + "", + "Usage:", + "> explore - Explore from your current position.", + "> explore - Explore from the specified X and Z position." + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ExploreFilterCommand.java b/src/api/java/baritone/api/utils/command/defaults/ExploreFilterCommand.java new file mode 100644 index 00000000..2bd41ae4 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ExploreFilterCommand.java @@ -0,0 +1,89 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeFile; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import com.google.gson.JsonSyntaxException; + +import java.io.File; +import java.nio.file.NoSuchFileException; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class ExploreFilterCommand extends Command { + public ExploreFilterCommand() { + super("explorefilter", "Explore chunks from a json"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(2); + File file = args.getDatatypePost(RelativeFile.class, MC.gameDir.getAbsoluteFile().getParentFile()); + boolean invert = false; + + if (args.has()) { + if (args.getS().equalsIgnoreCase("invert")) { + invert = true; + } else { + throw new CommandInvalidTypeException(args.consumed(), "either \"invert\" or nothing"); + } + } + + try { + baritone.getExploreProcess().applyJsonFilter(file.toPath().toAbsolutePath(), invert); + } catch (NoSuchFileException e) { + throw new CommandInvalidStateException("File not found"); + } catch (JsonSyntaxException e) { + throw new CommandInvalidStateException("Invalid JSON syntax"); + } catch (Exception e) { + throw new RuntimeException(e); + } + + logDirect(String.format("Explore filter applied. Inverted: %s", Boolean.toString(invert))); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.hasExactlyOne()) { + return RelativeFile.tabComplete(args, MC.gameDir.getAbsoluteFile().getParentFile()); + } + + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Apply an explore filter before using explore, which tells the explore process which chunks have been explored/not explored.", + "", + "The JSON file will follow this format: [{\"x\":0,\"z\":0},...]", + "", + "If 'invert' is specified, the chunks listed will be considered NOT explored, rather than explored.", + "", + "Usage:", + "> explorefilter [invert] - Load the JSON file referenced by the specified path. If invert is specified, it must be the literal word 'invert'." + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/FarmCommand.java b/src/api/java/baritone/api/utils/command/defaults/FarmCommand.java new file mode 100644 index 00000000..1828dec3 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/FarmCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class FarmCommand extends Command { + public FarmCommand() { + super("farm", "Farm nearby crops"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + baritone.getFarmProcess().farm(); + logDirect("Farming"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The farm command starts farming nearby plants. It harvests mature crops and plants new ones.", + "", + "Usage:", + "> farm" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/FindCommand.java b/src/api/java/baritone/api/utils/command/defaults/FindCommand.java new file mode 100644 index 00000000..1f9ce9a7 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/FindCommand.java @@ -0,0 +1,77 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.BlockById; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.block.Block; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class FindCommand extends Command { + public FindCommand() { + super("find", "Find positions of a certain block"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + List toFind = new ArrayList<>(); + + while (args.has()) { + toFind.add(args.getDatatypeFor(BlockById.class)); + } + + BetterBlockPos origin = ctx.playerFeet(); + + toFind.stream() + .flatMap(block -> + ctx.worldData().getCachedWorld().getLocationsOf( + Block.REGISTRY.getNameForObject(block).getPath(), + Integer.MAX_VALUE, + origin.x, + origin.y, + 4 + ).stream() + ) + .map(BetterBlockPos::new) + .map(BetterBlockPos::toString) + .forEach(this::logDirect); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return args.tabCompleteDatatype(BlockById.class); + } + + @Override + public List getLongDesc() { + return asList( + "", + "", + "Usage:", + "> " + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/FollowCommand.java b/src/api/java/baritone/api/utils/command/defaults/FollowCommand.java new file mode 100644 index 00000000..a2ae8951 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/FollowCommand.java @@ -0,0 +1,171 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.EntityClassById; +import baritone.api.utils.command.datatypes.IDatatype; +import baritone.api.utils.command.datatypes.IDatatypeFor; +import baritone.api.utils.command.datatypes.PlayerByUsername; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EntityLiving; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.ResourceLocation; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +public class FollowCommand extends Command { + public FollowCommand() { + super("follow", "Follow entity things"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMin(1); + + FollowGroup group; + FollowList list; + List entities = new ArrayList<>(); + List> classes = new ArrayList<>(); + + if (args.hasExactlyOne()) { + baritone.getFollowProcess().follow((group = args.getE(FollowGroup.class)).filter); + list = null; + } else { + args.requireMin(2); + + group = null; + list = args.getE(FollowList.class); + + while (args.has()) { + //noinspection unchecked + Object gotten = args.getDatatypeFor(list.datatype); + + if (gotten instanceof Class) { + //noinspection unchecked + classes.add((Class) gotten); + } else { + entities.add((Entity) gotten); + } + } + + baritone.getFollowProcess().follow( + classes.isEmpty() + ? entities::contains + : e -> classes.stream().anyMatch(c -> c.isInstance(e)) + ); + } + + if (nonNull(group)) { + logDirect(String.format("Following all %s", group.name().toLowerCase(Locale.US))); + } else { + logDirect("Following these types of entities:"); + + if (classes.isEmpty()) { + entities.stream() + .map(Entity::toString) + .forEach(this::logDirect); + } else { + classes.stream() + .map(EntityList::getKey) + .map(Objects::requireNonNull) + .map(ResourceLocation::toString) + .forEach(this::logDirect); + } + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.hasExactlyOne()) { + return new TabCompleteHelper() + .append(FollowGroup.class) + .append(FollowList.class) + .filterPrefix(args.getS()) + .stream(); + } else { + Class followType; + + try { + followType = args.getE(FollowList.class).datatype; + } catch (NullPointerException e) { + return Stream.empty(); + } + + while (args.has(2)) { + if (isNull(args.peekDatatypeOrNull(followType))) { + return Stream.empty(); + } + + args.get(); + } + + return args.tabCompleteDatatype(followType); + } + } + + @Override + public List getLongDesc() { + return asList( + "The follow command tells Baritone to follow certain kinds of entities.", + "", + "Usage:", + "> follow entities - Follows all entities.", + "> follow entity <...> - Follow certain entities (for example 'skeleton', 'horse' etc.)", + "> follow players - Follow players", + "> follow player <...> - Follow certain players" + ); + } + + private enum FollowGroup { + ENTITIES(EntityLiving.class::isInstance), + PLAYERS(EntityPlayer.class::isInstance); /* , + FRIENDLY(entity -> entity.getAttackTarget() != HELPER.mc.player), + HOSTILE(FRIENDLY.filter.negate()); */ + + final Predicate filter; + + FollowGroup(Predicate filter) { + this.filter = filter; + } + } + + private enum FollowList { + ENTITY(EntityClassById.class), + PLAYER(PlayerByUsername.class); + + final Class datatype; + + FollowList(Class datatype) { + this.datatype = datatype; + } + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ForceCancelCommand.java b/src/api/java/baritone/api/utils/command/defaults/ForceCancelCommand.java new file mode 100644 index 00000000..e78cb696 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ForceCancelCommand.java @@ -0,0 +1,58 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.behavior.IPathingBehavior; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class ForceCancelCommand extends Command { + public ForceCancelCommand() { + super("forcecancel", "Force cancel"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + IPathingBehavior pathingBehavior = baritone.getPathingBehavior(); + pathingBehavior.cancelEverything(); + pathingBehavior.forceCancel(); + logDirect("ok force canceled"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Like cancel, but more forceful.", + "", + "Usage:", + "> forcecancel" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/GcCommand.java b/src/api/java/baritone/api/utils/command/defaults/GcCommand.java new file mode 100644 index 00000000..7790f2a4 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/GcCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class GcCommand extends Command { + public GcCommand() { + super("gc", "Call System.gc()"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + System.gc(); + + logDirect("ok called System.gc()"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Calls System.gc().", + "", + "Usage:", + "> gc" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/GoalCommand.java b/src/api/java/baritone/api/utils/command/defaults/GoalCommand.java new file mode 100644 index 00000000..cb6a2fbf --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/GoalCommand.java @@ -0,0 +1,104 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.process.ICustomGoalProcess; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeCoordinate; +import baritone.api.utils.command.datatypes.RelativeGoal; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +public class GoalCommand extends Command { + public GoalCommand() { + super("goal", "Set or clear the goal"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + ICustomGoalProcess goalProcess = baritone.getCustomGoalProcess(); + + if (args.has() && asList("reset", "clear", "none").contains(args.peekS())) { + args.requireMax(1); + + if (nonNull(goalProcess.getGoal())) { + goalProcess.setGoal(null); + logDirect("Cleared goal"); + } else { + logDirect("There was no goal to clear"); + } + } else { + args.requireMax(3); + BetterBlockPos origin = baritone.getPlayerContext().playerFeet(); + Goal goal = args.getDatatype(RelativeGoal.class).apply(origin); + goalProcess.setGoal(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + TabCompleteHelper helper = new TabCompleteHelper(); + + if (args.hasExactlyOne()) { + helper.append(Stream.of("reset", "clear", "none", "~")); + } else { + if (args.hasAtMost(3)) { + while (args.has(2)) { + if (isNull(args.peekDatatypeOrNull(RelativeCoordinate.class))) { + break; + } + + args.get(); + + if (!args.has(2)) { + helper.append("~"); + } + } + } + } + + return helper.filterPrefix(args.getS()).stream(); + } + + @Override + public List getLongDesc() { + return asList( + "The goal command allows you to set or clear Baritone's goal.", + "", + "Wherever a coordinate is expected, you can use ~ just like in regular Minecraft commands. Or, you can just use regular numbers.", + "", + "Usage:", + "> goal - Set the goal to your current position", + "> goal - Erase the goal", + "> goal - Set the goal to a Y level", + "> goal - Set the goal to an X,Z position", + "> goal - Set the goal to an X,Y,Z position" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/HelpCommand.java b/src/api/java/baritone/api/utils/command/defaults/HelpCommand.java new file mode 100644 index 00000000..e96bf5eb --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/HelpCommand.java @@ -0,0 +1,131 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandNotFoundException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.pagination.Paginator; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.api.utils.command.manager.CommandManager; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static baritone.api.utils.command.BaritoneChatControl.FORCE_COMMAND_PREFIX; +import static baritone.api.utils.command.manager.CommandManager.getCommand; +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; + +public class HelpCommand extends Command { + public HelpCommand() { + super(asList("help", "?"), "View all commands or help on specific ones"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(1); + + if (!args.has() || args.is(Integer.class)) { + Paginator.paginate( + args, new Paginator<>( + CommandManager.REGISTRY.descendingStream() + .filter(command -> !command.hiddenFromHelp()) + .collect(Collectors.toCollection(ArrayList::new)) + ), + () -> logDirect("All Baritone commands (clickable):"), + command -> { + String names = String.join("/", command.names); + String name = command.names.get(0); + + return new TextComponentString(name) {{ + getStyle() + .setColor(TextFormatting.GRAY) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("") {{ + getStyle().setColor(TextFormatting.GRAY); + + appendSibling(new TextComponentString(names + "\n") {{ + getStyle().setColor(TextFormatting.WHITE); + }}); + + appendText(command.shortDesc); + appendText("\n\nClick to view full help"); + }} + )) + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + String.format("help %s", command.names.get(0)) + )); + + appendSibling(new TextComponentString(" - " + command.shortDesc) {{ + getStyle().setColor(TextFormatting.DARK_GRAY); + }}); + }}; + }, + FORCE_COMMAND_PREFIX + "help %d" + ); + } else { + String commandName = args.getS().toLowerCase(); + Command command = getCommand(commandName); + + if (isNull(command)) { + throw new CommandNotFoundException(commandName); + } + + logDirect(String.format("%s - %s", String.join(" / ", command.names), command.shortDesc)); + logDirect(""); + command.getLongDesc().forEach(this::logDirect); + logDirect(""); + logDirect(new TextComponentString("Click to return to the help menu") {{ + getStyle().setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + "help" + )); + }}); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.hasExactlyOne()) { + return new TabCompleteHelper().addCommands().filterPrefix(args.getS()).stream(); + } + + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Using this command, you can view detailed help information on how to use certain commands of Baritone.", + "", + "Usage:", + "> help - Lists all commands and their short descriptions.", + "> help - Displays help information on a specific command." + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/InvertCommand.java b/src/api/java/baritone/api/utils/command/defaults/InvertCommand.java new file mode 100644 index 00000000..da420db8 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/InvertCommand.java @@ -0,0 +1,74 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalInverted; +import baritone.api.process.ICustomGoalProcess; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; + +public class InvertCommand extends Command { + public InvertCommand() { + super("invert", "Run away from the current goal"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); + Goal goal; + + if (isNull(goal = customGoalProcess.getGoal())) { + throw new CommandInvalidStateException("No goal"); + } + + if (goal instanceof GoalInverted) { + goal = ((GoalInverted) goal).origin; + } else { + goal = new GoalInverted(goal); + } + + customGoalProcess.setGoal(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The invert command tells Baritone to head away from the current goal rather than towards it.", + "", + "Usage:", + "> invert - Invert the current goal." + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/MineCommand.java b/src/api/java/baritone/api/utils/command/defaults/MineCommand.java new file mode 100644 index 00000000..ec87dd75 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/MineCommand.java @@ -0,0 +1,69 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.BlockSelector; +import baritone.api.utils.CompositeBlockFilter; +import baritone.api.utils.IBlockFilter; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.BlockById; +import baritone.api.utils.command.datatypes.ForBlockSelector; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class MineCommand extends Command { + public MineCommand() { + super("mine", "Mine some blocks"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + int quantity = args.getAsOrDefault(Integer.class, 0); + args.requireMin(1); + List selectors = new ArrayList<>(); + + while (args.has()) { + selectors.add(args.getDatatypeFor(ForBlockSelector.class)); + } + + IBlockFilter filter = new CompositeBlockFilter(selectors); + baritone.getMineProcess().mine(quantity, filter); + logDirect(String.format("Mining %s", filter.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return args.tabCompleteDatatype(BlockById.class); + } + + @Override + public List getLongDesc() { + return asList( + "", + "", + "Usage:", + "> " + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/PathCommand.java b/src/api/java/baritone/api/utils/command/defaults/PathCommand.java new file mode 100644 index 00000000..2eafe55c --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/PathCommand.java @@ -0,0 +1,92 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.process.ICustomGoalProcess; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.datatypes.RelativeCoordinate; +import baritone.api.utils.command.datatypes.RelativeGoal; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; + +public class PathCommand extends Command { + public PathCommand() { + super("path", "Start heading towards a goal"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + ICustomGoalProcess customGoalProcess = baritone.getCustomGoalProcess(); + Goal goal; + + if (args.has()) { + args.requireMax(3); + goal = args.getDatatype(RelativeGoal.class).apply(ctx.playerFeet()); + } else if (isNull(goal = customGoalProcess.getGoal())) { + throw new CommandInvalidStateException("No goal"); + } + + args.requireMax(0); + customGoalProcess.setGoalAndPath(goal); + logDirect("Now pathing"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.has() && !args.has(4)) { + while (args.has(2)) { + if (isNull(args.peekDatatypeOrNull(RelativeCoordinate.class))) { + break; + } + + args.get(); + + if (!args.has(2)) { + return new TabCompleteHelper() + .append("~") + .filterPrefix(args.getS()) + .stream(); + } + } + } + + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The path command tells Baritone to head towards the current goal.", + "", + "Usage:", + "> path - Start the pathing.", + "> path ", + "> path ", + "> path - Define the goal here" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/PauseResumeCommands.java b/src/api/java/baritone/api/utils/command/defaults/PauseResumeCommands.java new file mode 100644 index 00000000..d7746623 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/PauseResumeCommands.java @@ -0,0 +1,150 @@ +package baritone.api.utils.command.defaults; + +import baritone.api.BaritoneAPI; +import baritone.api.Settings; +import baritone.api.process.IBaritoneProcess; +import baritone.api.process.PathingCommand; +import baritone.api.process.PathingCommandType; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +/** + * Contains the pause, resume, and paused commands. + * + * This thing is scoped to hell, private so far you can't even access it using reflection, because you AREN'T SUPPOSED + * TO USE THIS to pause and resume Baritone. Make your own process that returns {@link PathingCommandType#REQUEST_PAUSE + * REQUEST_PAUSE} + * as needed. + */ +public class PauseResumeCommands { + public static Command pauseCommand; + public static Command resumeCommand; + public static Command pausedCommand; + + static { + final boolean[] paused = {false}; + + BaritoneAPI.getProvider().getPrimaryBaritone().getPathingControlManager().registerProcess( + new IBaritoneProcess() { + @Override + public boolean isActive() { + return paused[0]; + } + + @Override + public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { + return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); + } + + @Override + public boolean isTemporary() { + return true; + } + + @Override + public void onLostControl() { + } + + @Override + public double priority() { + return DEFAULT_PRIORITY + 1; + } + + @Override + public String displayName0() { + return "Pause/Resume Commands"; + } + } + ); + + pauseCommand = new Command("pause", "Pauses Baritone until you use resume") { + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + if (paused[0]) { + throw new CommandInvalidStateException("Already paused"); + } + + paused[0] = true; + logDirect("Paused"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The pause command tells Baritone to temporarily stop whatever it's doing.", + "", + "This can be used to pause pathing, building, following, whatever. A single use of the resume command will start it right back up again!", + "", + "Usage:", + "> pause" + ); + } + }; + + resumeCommand = new Command("resume", "Resumes Baritone after a pause") { + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + if (!paused[0]) { + throw new CommandInvalidStateException("Not paused"); + } + + paused[0] = false; + logDirect("Resumed"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The resume command tells Baritone to resume whatever it was doing when you last used pause.", + "", + "Usage:", + "> resume" + ); + } + }; + + pausedCommand = new Command("paused", "Tells you if Baritone is paused") { + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + logDirect(String.format("Baritone is %spaused", paused[0] ? "" : "not ")); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The paused command tells you if Baritone is currently paused by use of the pause command.", + "", + "Usage:", + "> paused" + ); + } + }; + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ProcCommand.java b/src/api/java/baritone/api/utils/command/defaults/ProcCommand.java new file mode 100644 index 00000000..68dbc339 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ProcCommand.java @@ -0,0 +1,83 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.calc.IPathingControlManager; +import baritone.api.process.IBaritoneProcess; +import baritone.api.process.PathingCommand; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; + +public class ProcCommand extends Command { + public ProcCommand() { + super("proc", "View process state information"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + IPathingControlManager pathingControlManager = baritone.getPathingControlManager(); + IBaritoneProcess process = pathingControlManager.mostRecentInControl().orElse(null); + + if (isNull(process)) { + throw new CommandInvalidStateException("No process in control"); + } + + logDirect(String.format( + "Class: %s\n" + + "Priority: %s\n" + + "Temporary: %s\n" + + "Display name: %s\n" + + "Last command: %s", + process.getClass().getTypeName(), + Double.toString(process.priority()), + Boolean.toString(process.isTemporary()), + process.displayName(), + pathingControlManager + .mostRecentCommand() + .map(PathingCommand::toString) + .orElse("None") + )); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The proc command provides miscellaneous information about the process currently controlling Baritone.", + "", + "You are not expected to understand this if you aren't familiar with how Baritone works.", + "", + "Usage:", + "> proc - View process information, if present" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/ReloadAllCommand.java b/src/api/java/baritone/api/utils/command/defaults/ReloadAllCommand.java new file mode 100644 index 00000000..633701ba --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/ReloadAllCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class ReloadAllCommand extends Command { + public ReloadAllCommand() { + super("reloadall", "Reloads Baritone's cache for this world"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + ctx.worldData().getCachedWorld().reloadAllFromDisk(); + logDirect("Reloaded"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The reloadall command reloads Baritone's world cache.", + "", + "Usage:", + "> reloadall" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/RenderCommand.java b/src/api/java/baritone/api/utils/command/defaults/RenderCommand.java new file mode 100644 index 00000000..82398b35 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/RenderCommand.java @@ -0,0 +1,67 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class RenderCommand extends Command { + public RenderCommand() { + super("render", "Fix glitched chunks"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + BetterBlockPos origin = ctx.playerFeet(); + int renderDistance = (MC.gameSettings.renderDistanceChunks + 1) * 16; + MC.renderGlobal.markBlockRangeForRenderUpdate( + origin.x - renderDistance, + 0, + origin.z - renderDistance, + origin.x + renderDistance, + 255, + origin.z + renderDistance + ); + + logDirect("Done"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The render command fixes glitched chunk rendering without having to reload all of them.", + "", + "Usage:", + "> render" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/RepackCommand.java b/src/api/java/baritone/api/utils/command/defaults/RepackCommand.java new file mode 100644 index 00000000..d371603e --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/RepackCommand.java @@ -0,0 +1,78 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.cache.ICachedWorld; +import baritone.api.utils.BetterBlockPos; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.nonNull; + +public class RepackCommand extends Command { + public RepackCommand() { + super(asList("repack", "rescan"), "Re-cache chunks"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + IChunkProvider chunkProvider = ctx.world().getChunkProvider(); + ICachedWorld cachedWorld = ctx.worldData().getCachedWorld(); + + BetterBlockPos playerPos = ctx.playerFeet(); + int playerChunkX = playerPos.getX() >> 4; + int playerChunkZ = playerPos.getZ() >> 4; + int queued = 0; + for (int x = playerChunkX - 40; x <= playerChunkX + 40; x++) { + for (int z = playerChunkZ - 40; z <= playerChunkZ + 40; z++) { + Chunk chunk = chunkProvider.getLoadedChunk(x, z); + + if (nonNull(chunk) && !chunk.isEmpty()) { + queued++; + cachedWorld.queueForPacking(chunk); + } + } + } + + logDirect(String.format("Queued %d chunks for repacking", queued)); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Repack chunks around you. This basically re-caches them.", + "", + "Usage:", + "> repack - Repack chunks." + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/SaveAllCommand.java b/src/api/java/baritone/api/utils/command/defaults/SaveAllCommand.java new file mode 100644 index 00000000..163408cc --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/SaveAllCommand.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.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class SaveAllCommand extends Command { + public SaveAllCommand() { + super("saveall", "Saves Baritone's cache for this world"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + ctx.worldData().getCachedWorld().save(); + logDirect("Saved"); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The saveall command saves Baritone's world cache.", + "", + "Usage:", + "> saveall" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/SchematicaCommand.java b/src/api/java/baritone/api/utils/command/defaults/SchematicaCommand.java new file mode 100644 index 00000000..2522674f --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/SchematicaCommand.java @@ -0,0 +1,54 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class SchematicaCommand extends Command { + public SchematicaCommand() { + super("schematica", "Opens a Schematica schematic?"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + baritone.getBuilderProcess().buildOpenSchematic(); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "I'm not actually sure what this does.", + "", + "Usage:", + "> schematica" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/SetCommand.java b/src/api/java/baritone/api/utils/command/defaults/SetCommand.java new file mode 100644 index 00000000..e9541131 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/SetCommand.java @@ -0,0 +1,275 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.SettingsUtil; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.helpers.pagination.Paginator; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static baritone.api.utils.SettingsUtil.settingDefaultToString; +import static baritone.api.utils.SettingsUtil.settingTypeToString; +import static baritone.api.utils.SettingsUtil.settingValueToString; +import static baritone.api.utils.command.BaritoneChatControl.FORCE_COMMAND_PREFIX; +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static java.util.stream.Stream.of; + +public class SetCommand extends Command { + public SetCommand() { + super(asList("set", "setting", "settings"), "View or change settings"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + String arg = args.has() ? args.getS().toLowerCase(Locale.US) : "list"; + boolean viewModified = asList("m", "mod", "modified").contains(arg); + boolean viewAll = asList("all", "l", "list").contains(arg); + boolean paginate = viewModified | viewAll; + if (paginate) { + String search = args.has() && args.peekAsOrNull(Integer.class) == null ? args.getS() : ""; + args.requireMax(1); + + List toPaginate = + viewModified + ? SettingsUtil.modifiedSettings(settings) + : settings.allSettings.stream() + .filter(s -> !s.getName().equals("logger")) + .filter(s -> s.getName().toLowerCase(Locale.US).contains(search.toLowerCase(Locale.US))) + .collect(Collectors.toCollection(ArrayList::new)); + + toPaginate.sort((setting1, setting2) -> String.CASE_INSENSITIVE_ORDER.compare( + setting1.getName(), + setting2.getName() + )); + + Paginator.paginate( + args, + new Paginator<>(toPaginate), + () -> logDirect( + !search.isEmpty() + ? String.format("All settings containing the string '%s':", search) + : String.format("All %ssettings:", viewModified ? "modified " : "") + ), + setting -> new TextComponentString(setting.getName()) {{ + getStyle() + .setColor(TextFormatting.GRAY) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("") {{ + getStyle().setColor(TextFormatting.GRAY); + appendText(setting.getName()); + appendText(String.format("\nType: %s", settingTypeToString(setting))); + appendText(String.format("\n\nValue:\n%s", settingValueToString(setting))); + + if (setting.value != setting.defaultValue) { + appendText(String.format("\n\nDefault:\n%s", settingDefaultToString(setting))); + } + }} + )) + .setClickEvent(new ClickEvent( + ClickEvent.Action.SUGGEST_COMMAND, + settings.prefix.value + String.format("set %s ", setting.getName()) + )); + + appendSibling(new TextComponentString(String.format(" (%s)", settingTypeToString(setting))) {{ + getStyle().setColor(TextFormatting.DARK_GRAY); + }}); + }}, + FORCE_COMMAND_PREFIX + "set " + arg + " " + search + " %d" + ); + + return; + } + + args.requireMax(1); + + boolean resetting = arg.equalsIgnoreCase("reset"); + boolean toggling = arg.equalsIgnoreCase("toggle"); + boolean doingSomething = resetting || toggling; + + if (resetting) { + if (!args.has()) { + logDirect("Please specify 'all' as an argument to reset to confirm you'd really like to do this"); + logDirect("ALL settings will be reset. Use the 'set modified' or 'modified' commands to see what will be reset."); + logDirect("Specify a setting name instead of 'all' to only reset one setting"); + } else if (args.peekS().equalsIgnoreCase("all")) { + SettingsUtil.modifiedSettings(settings).forEach(Settings.Setting::reset); + logDirect("All settings have been reset to their default values"); + + return; + } + } + + if (toggling) { + args.requireMin(1); + } + + String settingName = doingSomething ? args.getS() : arg; + Settings.Setting setting = settings.allSettings.stream() + .filter(s -> s.getName().equalsIgnoreCase(settingName)) + .findFirst() + .orElse(null); + + if (isNull(setting)) { + throw new CommandInvalidTypeException(args.consumed(), "a valid setting"); + } + + if (!doingSomething && !args.has()) { + logDirect(String.format("Value of setting %s:", setting.getName())); + logDirect(settingValueToString(setting)); + } else { + String oldValue = settingValueToString(setting); + + if (resetting) { + setting.reset(); + } else if (toggling) { + if (setting.getValueClass() != Boolean.class) { + throw new CommandInvalidTypeException(args.consumed(), "a toggleable setting", "some other setting"); + } + + //noinspection unchecked + ((Settings.Setting) setting).value ^= true; + + logDirect(String.format( + "Toggled setting %s to %s", + setting.getName(), + Boolean.toString((Boolean) setting.value) + )); + } else { + String newValue = args.getS(); + + try { + SettingsUtil.parseAndApply(settings, arg, newValue); + } catch (Throwable t) { + t.printStackTrace(); + throw new CommandInvalidTypeException(args.consumed(), "a valid value", t); + } + } + + if (!toggling) { + logDirect(String.format( + "Successfully %s %s to %s", + resetting ? "reset" : "set", + setting.getName(), + settingValueToString(setting) + )); + } + + logDirect(new TextComponentString(String.format("Old value: %s", oldValue)) {{ + getStyle() + .setColor(TextFormatting.GRAY) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to set the setting back to this value") + )) + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + FORCE_COMMAND_PREFIX + String.format("set %s %s", setting.getName(), oldValue) + )); + }}); + + if ((setting.getName().equals("chatControl") && !(Boolean) setting.value && !settings.chatControlAnyway.value) || + setting.getName().equals("chatControlAnyway") && !(Boolean) setting.value && !settings.chatControl.value) { + logDirect("Warning: Chat commands will no longer work. If you want to revert this change, use prefix control (if enabled) or click the old value listed above.", TextFormatting.RED); + } else if (setting.getName().equals("prefixControl") && !(Boolean) setting.value) { + logDirect("Warning: Prefixed commands will no longer work. If you want to revert this change, use chat control (if enabled) or click the old value listed above.", TextFormatting.RED); + } + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + if (args.has()) { + String arg = args.getS(); + + if (args.hasExactlyOne()) { + if (arg.equalsIgnoreCase("reset")) { + return new TabCompleteHelper() + .addModifiedSettings() + .prepend("all") + .filterPrefix(args.getS()) + .stream(); + } else if (arg.equalsIgnoreCase("toggle")) { + return new TabCompleteHelper() + .addToggleableSettings() + .filterPrefix(args.getS()) + .stream(); + } + + Settings.Setting setting = settings.byLowerName.get(arg.toLowerCase(Locale.US)); + + if (nonNull(setting)) { + if (setting.getType() == Boolean.class) { + TabCompleteHelper helper = new TabCompleteHelper(); + + if ((Boolean) setting.value) { + helper.append(of("true", "false")); + } else { + helper.append(of("false", "true")); + } + + return helper.filterPrefix(args.getS()).stream(); + } else { + return Stream.of(settingValueToString(setting)); + } + } + } else if (!args.has()) { + return new TabCompleteHelper() + .addSettings() + .sortAlphabetically() + .prepend("list", "modified", "reset", "toggle") + .filterPrefix(arg) + .stream(); + } + } + + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "Using the set command, you can manage all of Baritone's settings. Almost every aspect is controlled by these settings - go wild!", + "", + "Usage:", + "> set - Same as `set list`", + "> set list [page] - View all settings", + "> set modified [page] - View modified settings", + "> set - View the current value of a setting", + "> set - Set the value of a setting", + "> set reset all - Reset ALL SETTINGS to their defaults", + "> set reset - Reset a setting to its default", + "> set toggle - Toggle a boolean setting" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/TunnelCommand.java b/src/api/java/baritone/api/utils/command/defaults/TunnelCommand.java new file mode 100644 index 00000000..62b47562 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/TunnelCommand.java @@ -0,0 +1,63 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.pathing.goals.Goal; +import baritone.api.pathing.goals.GoalStrictDirection; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; + +public class TunnelCommand extends Command { + public TunnelCommand() { + super("tunnel", "Set a goal to tunnel in your current direction"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + Goal goal = new GoalStrictDirection( + ctx.playerFeet(), + ctx.player().getHorizontalFacing() + ); + + baritone.getCustomGoalProcess().setGoal(goal); + logDirect(String.format("Goal: %s", goal.toString())); + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The tunnel command sets a goal that tells Baritone to mine completely straight in the direction that you're facing.", + "", + "Usage:", + "> tunnel" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/defaults/VersionCommand.java b/src/api/java/baritone/api/utils/command/defaults/VersionCommand.java new file mode 100644 index 00000000..a232ddc3 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/defaults/VersionCommand.java @@ -0,0 +1,63 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.defaults; + +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.exception.CommandInvalidStateException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Arrays.asList; +import static java.util.Objects.isNull; + +public class VersionCommand extends Command { + public VersionCommand() { + super("version", "View the Baritone version"); + } + + @Override + protected void executed(String label, ArgConsumer args, Settings settings) { + args.requireMax(0); + + String version = getClass().getPackage().getImplementationVersion(); + + if (isNull(version)) { + throw new CommandInvalidStateException("Null version (this is normal in a dev environment)"); + } else { + logDirect(String.format("You are running Baritone v%s", version)); + } + } + + @Override + protected Stream tabCompleted(String label, ArgConsumer args, Settings settings) { + return Stream.empty(); + } + + @Override + public List getLongDesc() { + return asList( + "The version command prints the version of Baritone you're currently running.", + "", + "Usage:", + "> version - View version information, if present" + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandErrorMessageException.java b/src/api/java/baritone/api/utils/command/exception/CommandErrorMessageException.java new file mode 100644 index 00000000..0b33c2d6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandErrorMessageException.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.utils.command.exception; + +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; +import net.minecraft.util.text.TextFormatting; + +import java.util.List; + +import static baritone.api.utils.Helper.HELPER; + +public abstract class CommandErrorMessageException extends CommandException { + protected CommandErrorMessageException(String reason) { + super(reason); + } + + @Override + public void handle(Command command, List args) { + HELPER.logDirect(getMessage(), TextFormatting.RED); + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandException.java b/src/api/java/baritone/api/utils/command/exception/CommandException.java new file mode 100644 index 00000000..8e12f6b6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandException.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.utils.command.exception; + +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; + +import java.util.List; + +public abstract class CommandException extends RuntimeException { + protected CommandException(String reason) { + super(reason); + } + + /** + * 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. + */ + public abstract void handle(Command command, List args); +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandInvalidArgumentException.java b/src/api/java/baritone/api/utils/command/exception/CommandInvalidArgumentException.java new file mode 100644 index 00000000..342cf336 --- /dev/null +++ b/src/api/java/baritone/api/utils/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.utils.command.exception; + +import baritone.api.utils.command.argument.CommandArgument; + +public abstract class CommandInvalidArgumentException extends CommandErrorMessageException { + public final CommandArgument arg; + + protected CommandInvalidArgumentException(CommandArgument arg, String reason) { + super(String.format( + "Error at argument #%s: %s", + arg.index == -1 ? "" : Integer.toString(arg.index + 1), + reason + )); + + this.arg = arg; + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandInvalidStateException.java b/src/api/java/baritone/api/utils/command/exception/CommandInvalidStateException.java new file mode 100644 index 00000000..76ad3af0 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandInvalidStateException.java @@ -0,0 +1,24 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +public class CommandInvalidStateException extends CommandErrorMessageException { + public CommandInvalidStateException(String reason) { + super(reason); + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandInvalidTypeException.java b/src/api/java/baritone/api/utils/command/exception/CommandInvalidTypeException.java new file mode 100644 index 00000000..0988774d --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandInvalidTypeException.java @@ -0,0 +1,38 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +import baritone.api.utils.command.argument.CommandArgument; + +public class CommandInvalidTypeException extends CommandInvalidArgumentException { + public CommandInvalidTypeException(CommandArgument arg, String expected) { + super(arg, String.format("Expected %s", expected)); + } + + public CommandInvalidTypeException(CommandArgument arg, String expected, Throwable cause) { + super(arg, String.format("Expected %s.\nMore details: %s", expected, cause.getMessage())); + } + + public CommandInvalidTypeException(CommandArgument arg, String expected, String got) { + super(arg, String.format("Expected %s, but got %s instead", expected, got)); + } + + public CommandInvalidTypeException(CommandArgument 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/utils/command/exception/CommandNoParserForTypeException.java b/src/api/java/baritone/api/utils/command/exception/CommandNoParserForTypeException.java new file mode 100644 index 00000000..4dbbb962 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandNoParserForTypeException.java @@ -0,0 +1,27 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +public class CommandNoParserForTypeException extends CommandErrorMessageException { + public final Class klass; + + public CommandNoParserForTypeException(Class klass) { + super(String.format("Could not find a handler for type %s", klass.getSimpleName())); + this.klass = klass; + } +} diff --git a/src/api/java/baritone/api/utils/command/exception/CommandNotEnoughArgumentsException.java b/src/api/java/baritone/api/utils/command/exception/CommandNotEnoughArgumentsException.java new file mode 100644 index 00000000..29d5d6ba --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandNotEnoughArgumentsException.java @@ -0,0 +1,24 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.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/utils/command/exception/CommandNotFoundException.java b/src/api/java/baritone/api/utils/command/exception/CommandNotFoundException.java new file mode 100644 index 00000000..44d3b7b2 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandNotFoundException.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.utils.command.exception; + +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; + +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/utils/command/exception/CommandTooManyArgumentsException.java b/src/api/java/baritone/api/utils/command/exception/CommandTooManyArgumentsException.java new file mode 100644 index 00000000..459940ab --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandTooManyArgumentsException.java @@ -0,0 +1,24 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.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/utils/command/exception/CommandUnhandledException.java b/src/api/java/baritone/api/utils/command/exception/CommandUnhandledException.java new file mode 100644 index 00000000..849ace18 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/exception/CommandUnhandledException.java @@ -0,0 +1,83 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.exception; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import static java.util.Arrays.asList; + +public class CommandUnhandledException extends CommandErrorMessageException { + public static String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + throwable.printStackTrace(new PrintWriter(sw)); + return sw.toString(); + } + + public static String getBaritoneStackTrace(String stackTrace) { + List lines = Arrays.stream(stackTrace.split("\n")) + .collect(Collectors.toCollection(ArrayList::new)); + + int lastBaritoneLine = 0; + for (int i = 0; i < lines.size(); i++) { + if (lines.get(i).startsWith("\tat baritone.") && lines.get(i).contains("BaritoneChatControl")) { + lastBaritoneLine = i; + } + } + + return String.join("\n", lines.subList(0, lastBaritoneLine + 1)); + } + + public static String getBaritoneStackTrace(Throwable throwable) { + return getBaritoneStackTrace(getStackTrace(throwable)); + } + + public static String getFriendlierStackTrace(String stackTrace) { + List lines = asList(stackTrace.split("\n")); + + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + if (line.startsWith("\tat ")) { + if (line.startsWith("\tat baritone.")) { + line = line.replaceFirst("^\tat [a-z.]+?([A-Z])", "\tat $1"); + } + + // line = line.replaceFirst("\\(([^)]+)\\)$", "\n\t . $1"); + line = line.replaceFirst("\\([^:]+:(\\d+)\\)$", ":$1"); + lines.set(i, line); + } + } + + return String.join("\n", lines); + } + + public static String getFriendlierStackTrace(Throwable throwable) { + return getFriendlierStackTrace(getBaritoneStackTrace(throwable)); + } + + public CommandUnhandledException(Throwable cause) { + super(String.format( + "An unhandled exception has occurred:\n\n%s", + getFriendlierStackTrace(cause) + )); + } +} diff --git a/src/api/java/baritone/api/utils/command/execution/CommandExecution.java b/src/api/java/baritone/api/utils/command/execution/CommandExecution.java new file mode 100644 index 00000000..2999666a --- /dev/null +++ b/src/api/java/baritone/api/utils/command/execution/CommandExecution.java @@ -0,0 +1,113 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.execution; + +import baritone.api.BaritoneAPI; +import baritone.api.Settings; +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandUnhandledException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import baritone.api.utils.command.manager.CommandManager; +import com.mojang.realmsclient.util.Pair; + +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Objects.isNull; + +public class CommandExecution { + /** + * The command itself + */ + private final Command command; + + /** + * The name this command was called with + */ + public final String label; + + /** + * The arg consumer + */ + public final ArgConsumer args; + + /** + * The Baritone settings + */ + public final Settings settings = BaritoneAPI.getSettings(); + + public CommandExecution(Command command, String label, ArgConsumer args) { + this.command = command; + this.label = label; + this.args = args; + } + + public static String getLabel(String string) { + return string.split("\\s", 2)[0]; + } + + public static Pair> expand(String string, boolean preserveEmptyLast) { + String label = getLabel(string); + List args = CommandArgument.from(string.substring(label.length()), preserveEmptyLast); + return Pair.of(label, args); + } + + public static Pair> expand(String string) { + return expand(string, false); + } + + public void execute() { + try { + command.execute(this); + } catch (CommandException e) { + e.handle(command, args.args); + } catch (Throwable t) { + t.printStackTrace(); + + new CommandUnhandledException(t).handle(command, args.args); + } + } + + public Stream tabComplete() { + return command.tabComplete(this); + } + + public static CommandExecution from(String label, ArgConsumer args) { + Command command = CommandManager.getCommand(label); + + if (isNull(command)) { + return null; + } + + return new CommandExecution( + command, + label, + args + ); + } + + public static CommandExecution from(Pair> pair) { + return from(pair.first(), new ArgConsumer(pair.second())); + } + + public static CommandExecution from(String string) { + return from(expand(string)); + } +} diff --git a/src/api/java/baritone/api/utils/command/helpers/arguments/ArgConsumer.java b/src/api/java/baritone/api/utils/command/helpers/arguments/ArgConsumer.java new file mode 100644 index 00000000..6c9ef884 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/arguments/ArgConsumer.java @@ -0,0 +1,310 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.helpers.arguments; + +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.datatypes.IDatatype; +import baritone.api.utils.command.datatypes.IDatatypeFor; +import baritone.api.utils.command.datatypes.IDatatypePost; +import baritone.api.utils.command.exception.CommandException; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.exception.CommandNotEnoughArgumentsException; +import baritone.api.utils.command.exception.CommandTooManyArgumentsException; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Stream; + +public class ArgConsumer { + public final List args; + public final Deque consumed; + + private ArgConsumer(List args, Deque consumed) { + this.args = new ArrayList<>(args); + this.consumed = new LinkedList<>(consumed); + } + + public ArgConsumer(List args) { + this(args, new LinkedList<>()); + } + + public boolean has(int num) { + return args.size() >= num; + } + + public boolean has() { + return has(1); + } + + public boolean hasAtMost(int num) { + return args.size() <= num; + } + + public boolean hasAtMostOne() { + return hasAtMost(1); + } + + public boolean hasExactly(int num) { + return args.size() == num; + } + + public boolean hasExactlyOne() { + return hasExactly(1); + } + + public CommandArgument peek(int index) { + requireMin(1); + return args.get(index); + } + + public CommandArgument peek() { + return peek(0); + } + + public boolean is(Class type) { + return peek().is(type); + } + + public String peekS(int index) { + return peek(index).value; + } + + public String peekS() { + return peekS(0); + } + + public > E peekE(Class enumClass) { + return peek().getE(enumClass); + } + + public > E peekEOrNull(Class enumClass) { + try { + return peekE(enumClass); + } catch (NoSuchElementException e) { + return null; + } + } + + public T peekAs(Class type, int index) { + return peek(index).getAs(type); + } + + public T peekAs(Class type) { + return peekAs(type, 0); + } + + public T peekAsOrDefault(Class type, T def, int index) { + try { + return peekAs(type, index); + } catch (CommandInvalidTypeException e) { + return def; + } + } + + public T peekAsOrDefault(Class type, T def) { + return peekAsOrDefault(type, def, 0); + } + + public T peekAsOrNull(Class type, int index) { + return peekAsOrDefault(type, null, 0); + } + + public T peekAsOrNull(Class type) { + return peekAsOrNull(type, 0); + } + + public T peekDatatype(Class datatype) { + return clone().getDatatype(datatype); + } + + public T peekDatatypeOrNull(Class datatype) { + return new ArgConsumer(args, consumed).getDatatypeOrNull(datatype); + } + + public > T peekDatatypePost(Class datatype, O original) { + return new ArgConsumer(args, consumed).getDatatypePost(datatype, original); + } + + public > T peekDatatypePostOrDefault(Class datatype, O original, T def) { + return new ArgConsumer(args, consumed).getDatatypePostOrDefault(datatype, original, def); + } + + public > T peekDatatypePostOrNull(Class datatype, O original) { + return peekDatatypePostOrDefault(datatype, original, null); + } + + public > T peekDatatypeFor(Class datatype) { + return new ArgConsumer(args, consumed).peekDatatypeFor(datatype); + } + + public > T peekDatatypeForOrDefault(Class datatype, T def) { + return new ArgConsumer(args, consumed).peekDatatypeForOrDefault(datatype, def); + } + + public > T peekDatatypeForOrNull(Class datatype, T def) { + return peekDatatypeForOrDefault(datatype, null); + } + + public CommandArgument get() { + requireMin(1); + CommandArgument arg = args.remove(0); + consumed.add(arg); + return arg; + } + + public String getS() { + return get().value; + } + + public > E getE(Class enumClass) { + try { + return get().getE(enumClass); + } catch (NoSuchElementException e) { + throw new CommandInvalidTypeException(consumed(), enumClass.getSimpleName()); + } + } + + public > E getEOrNull(Class enumClass) { + try { + peekE(enumClass); + return getE(enumClass); + } catch (CommandInvalidTypeException e) { + return null; + } + } + + public T getAs(Class type) { + return get().getAs(type); + } + + public T getAsOrDefault(Class type, T def) { + try { + T val = peek().getAs(type); + get(); + return val; + } catch (CommandInvalidTypeException e) { + return def; + } + } + + public T getAsOrNull(Class type) { + return getAsOrDefault(type, null); + } + + public T getDatatype(Class datatype) { + try { + return datatype.getConstructor(ArgConsumer.class).newInstance(this); + } catch (RuntimeException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + throw new CommandInvalidTypeException(has() ? peek() : consumed(), datatype.getSimpleName()); + } + } + + public T getDatatypeOrNull(Class datatype) { + try { + return getDatatype(datatype); + } catch (CommandInvalidTypeException e) { + return null; + } + } + + public > T getDatatypePost(Class datatype, O original) { + return getDatatype(datatype).apply(original); + } + + public > T getDatatypePostOrDefault(Class datatype, O original, T def) { + try { + return getDatatypePost(datatype, original); + } catch (CommandException e) { + return def; + } + } + + public > T getDatatypePostOrNull(Class datatype, O original) { + return getDatatypePostOrDefault(datatype, original, null); + } + + public > T getDatatypeFor(Class datatype) { + return getDatatype(datatype).get(); + } + + public > T getDatatypeForOrDefault(Class datatype, T def) { + try { + return getDatatypeFor(datatype); + } catch (CommandInvalidTypeException e) { + return def; + } + } + + public > T getDatatypeForOrNull(Class datatype) { + return getDatatypeForOrDefault(datatype, null); + } + + public Stream tabCompleteDatatype(Class datatype) { + try { + return datatype.getConstructor().newInstance().tabComplete(this); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } catch (CommandException ignored) { + } + + return Stream.empty(); + } + + public String rawRest() { + return args.size() > 0 ? args.get(0).rawRest : ""; + } + + public void requireMin(int min) { + if (args.size() < min) { + throw new CommandNotEnoughArgumentsException(min + consumed.size()); + } + } + + public void requireMax(int max) { + if (args.size() > max) { + throw new CommandTooManyArgumentsException(max + consumed.size()); + } + } + + public void requireExactly(int args) { + requireMin(args); + requireMax(args); + } + + public boolean hasConsumed() { + return !consumed.isEmpty(); + } + + public CommandArgument consumed() { + return consumed.size() > 0 ? consumed.getLast() : CommandArgument.unknown(); + } + + @SuppressWarnings("MethodDoesntCallSuperMethod") + @Override + public ArgConsumer clone() { + return new ArgConsumer(args, consumed); + } + + public static ArgConsumer from(String string) { + return new ArgConsumer(CommandArgument.from(string)); + } +} diff --git a/src/api/java/baritone/api/utils/command/helpers/pagination/Paginator.java b/src/api/java/baritone/api/utils/command/helpers/pagination/Paginator.java new file mode 100644 index 00000000..db7942b6 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/pagination/Paginator.java @@ -0,0 +1,162 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.helpers.pagination; + +import baritone.api.utils.Helper; +import baritone.api.utils.command.exception.CommandInvalidTypeException; +import baritone.api.utils.command.helpers.arguments.ArgConsumer; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; +import net.minecraft.util.text.TextFormatting; +import net.minecraft.util.text.event.ClickEvent; +import net.minecraft.util.text.event.HoverEvent; + +import java.util.List; +import java.util.function.Function; + +import static java.util.Objects.nonNull; + +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 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 commandFormat) { + 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 = nonNull(commandFormat) && validPage(page - 1); + boolean hasNextPage = nonNull(commandFormat) && validPage(page + 1); + + logDirect(new TextComponentString("") {{ + getStyle().setColor(TextFormatting.GRAY); + + appendSibling(new TextComponentString("<<") {{ + if (hasPrevPage) { + getStyle() + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format(commandFormat, page - 1) + )) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to view previous page") + )); + } else { + getStyle().setColor(TextFormatting.DARK_GRAY); + } + }}); + + appendText(" | "); + + appendSibling(new TextComponentString(">>") {{ + if (hasNextPage) { + getStyle() + .setClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, + String.format(commandFormat, page + 1) + )) + .setHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TextComponentString("Click to view next page") + )); + } else { + getStyle().setColor(TextFormatting.DARK_GRAY); + } + }}); + + appendText(String.format(" %d/%d", page, getMaxPage())); + }}); + } + + public void display(Function transform) { + display(transform, null); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Runnable pre, Function transform, String commandFormat) { + int page = 1; + + consumer.requireMax(1); + + if (consumer.has()) { + 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().value + ); + } + } + + pagi.skipPages(page - pagi.page); + + if (nonNull(pre)) { + pre.run(); + } + + pagi.display(transform, commandFormat); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Function transform, String commandName) { + paginate(consumer, pagi, null, transform, commandName); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Runnable pre, Function transform) { + paginate(consumer, pagi, pre, transform, null); + } + + public static void paginate(ArgConsumer consumer, Paginator pagi, Function transform) { + paginate(consumer, pagi, null, transform, null); + } +} diff --git a/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java b/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java new file mode 100644 index 00000000..ce689b0b --- /dev/null +++ b/src/api/java/baritone/api/utils/command/helpers/tabcomplete/TabCompleteHelper.java @@ -0,0 +1,158 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.helpers.tabcomplete; + +import baritone.api.BaritoneAPI; +import baritone.api.Settings; +import baritone.api.utils.SettingsUtil; +import baritone.api.utils.command.manager.CommandManager; +import net.minecraft.util.ResourceLocation; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import static java.util.stream.Stream.concat; +import static java.util.stream.Stream.of; + +public class TabCompleteHelper { + private Stream stream; + + public TabCompleteHelper(String[] base) { + stream = Arrays.stream(base); + } + + public TabCompleteHelper(List base) { + stream = base.stream(); + } + + public TabCompleteHelper() { + this(new String[0]); + } + + public TabCompleteHelper append(Stream source) { + stream = concat(stream, source); + + return this; + } + + public TabCompleteHelper append(String... source) { + return append(of(source)); + } + + public TabCompleteHelper append(Class> num) { + return append( + Arrays.stream(num.getEnumConstants()) + .map(Enum::name) + .map(String::toLowerCase) + ); + } + + public TabCompleteHelper prepend(Stream source) { + stream = concat(source, stream); + + return this; + } + + public TabCompleteHelper prepend(String... source) { + return prepend(of(source)); + } + + public TabCompleteHelper prepend(Class> num) { + return prepend( + Arrays.stream(num.getEnumConstants()) + .map(Enum::name) + .map(String::toLowerCase) + ); + } + + public TabCompleteHelper map(Function transform) { + stream = stream.map(transform); + + return this; + } + + public TabCompleteHelper filter(Predicate filter) { + stream = stream.filter(filter); + + return this; + } + + public TabCompleteHelper sort(Comparator comparator) { + stream = stream.sorted(comparator); + + return this; + } + + public TabCompleteHelper sortAlphabetically() { + return sort(String.CASE_INSENSITIVE_ORDER); + } + + public TabCompleteHelper filterPrefix(String prefix) { + return filter(x -> x.toLowerCase(Locale.US).startsWith(prefix.toLowerCase(Locale.US))); + } + + public TabCompleteHelper filterPrefixNamespaced(String prefix) { + return filterPrefix(new ResourceLocation(prefix).toString()); + } + + public String[] build() { + return stream.toArray(String[]::new); + } + + public Stream stream() { + return stream; + } + + public TabCompleteHelper addCommands() { + return append( + CommandManager.REGISTRY.descendingStream() + .flatMap(command -> command.names.stream()) + .distinct() + ); + } + + public TabCompleteHelper addSettings() { + return append( + BaritoneAPI.getSettings().allSettings.stream() + .map(Settings.Setting::getName) + .filter(s -> !s.equalsIgnoreCase("logger")) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } + + public TabCompleteHelper addModifiedSettings() { + return append( + SettingsUtil.modifiedSettings(BaritoneAPI.getSettings()).stream() + .map(Settings.Setting::getName) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } + + public TabCompleteHelper addToggleableSettings() { + return append( + BaritoneAPI.getSettings().getAllValuesByType(Boolean.class).stream() + .map(Settings.Setting::getName) + .sorted(String.CASE_INSENSITIVE_ORDER) + ); + } +} diff --git a/src/api/java/baritone/api/utils/command/manager/CommandManager.java b/src/api/java/baritone/api/utils/command/manager/CommandManager.java new file mode 100644 index 00000000..75dff566 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/manager/CommandManager.java @@ -0,0 +1,93 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.manager; + +import baritone.api.utils.command.Command; +import baritone.api.utils.command.argument.CommandArgument; +import baritone.api.utils.command.defaults.DefaultCommands; +import baritone.api.utils.command.execution.CommandExecution; +import baritone.api.utils.command.helpers.tabcomplete.TabCompleteHelper; +import baritone.api.utils.command.registry.Registry; +import com.mojang.realmsclient.util.Pair; + +import java.util.List; +import java.util.Locale; +import java.util.stream.Stream; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +public class CommandManager { + public static final Registry REGISTRY = new Registry<>(); + + static { + DefaultCommands.commands.forEach(REGISTRY::register); + } + + /** + * @param name The command name to search for. + * @return The command, if found. + */ + public static Command getCommand(String name) { + for (Command command : REGISTRY.entries) { + if (command.names.contains(name.toLowerCase(Locale.US))) { + return command; + } + } + + return null; + } + + public static void execute(CommandExecution execution) { + execution.execute(); + } + + public static boolean execute(String string) { + CommandExecution execution = CommandExecution.from(string); + + if (nonNull(execution)) { + execution.execute(); + } + + return nonNull(execution); + } + + public static Stream tabComplete(CommandExecution execution) { + return execution.tabComplete(); + } + + public static Stream tabComplete(Pair> pair) { + CommandExecution execution = CommandExecution.from(pair); + return isNull(execution) ? Stream.empty() : tabComplete(execution); + } + + public static Stream tabComplete(String prefix) { + Pair> pair = CommandExecution.expand(prefix, true); + String label = pair.first(); + List args = pair.second(); + + if (args.isEmpty()) { + return new TabCompleteHelper() + .addCommands() + .filterPrefix(label) + .stream(); + } else { + return tabComplete(pair); + } + } +} diff --git a/src/api/java/baritone/api/utils/command/registry/Registry.java b/src/api/java/baritone/api/utils/command/registry/Registry.java new file mode 100644 index 00000000..c92d4d56 --- /dev/null +++ b/src/api/java/baritone/api/utils/command/registry/Registry.java @@ -0,0 +1,148 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Baritone. If not, see . + */ + +package baritone.api.utils.command.registry; + +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * This registry class allows for registration and unregistration of a certain type. This is mainly designed for use by + * event handlers where newly registered ones are encountered first during iteration and can therefore override older + * ones. In Baritone, this is used for commands and argument parsers so that mods and addons can extend Baritone's + * functionality without resorting to hacks, wrappers, or mixins. + * + * @param The entry type that will be stored in this registry. This can be anything, really - preferably anything + * that works as a HashMap key, as that's what's used to keep track of which entries are registered or not. + */ +@SuppressWarnings({"unused", "UnusedReturnValue"}) +public class Registry { + /** + * An internal linked list of all the entries that are currently registered. This is a linked list so that entries + * can be inserted at the beginning, which means that newer entries are encountered first during iteration. This is + * an important property of the registry that makes it more useful than a simple list, and also the reason it does + * not just use a map. + */ + private final Deque _entries = new LinkedList<>(); + + /** + * A HashMap containing keys for every entry currently registered. Map entries are added to this map when something + * is registered and removed from the map when they are unregistered. An entry simply being present in this map + * indicates that it is currently registered and therefore can be removed and should not be reregistered. + */ + private final Map registered = new HashMap<>(); + + /** + * The collection of entries that are currently in this registry. This is a collection (and not a list) because, + * internally, entries are stored in a linked list, which is not the same as a normal list. + */ + public final Collection entries = Collections.unmodifiableCollection(_entries); + + /** + * @param entry The entry to check. + * @return If this entry is currently registered in this registry. + */ + public boolean registered(V entry) { + return registered.containsKey(entry); + } + + /** + * Ensures that the entry {@code entry} is registered. + * + * @param entry The entry to register. + * @return A boolean indicating whether or not this is a new registration. No matter the value of this boolean, the + * entry is always guaranteed to now be in this registry. This boolean simply indicates if the entry was not + * in the map prior to this method call. + */ + public boolean register(V entry) { + if (!registered(entry)) { + _entries.addFirst(entry); + registered.put(entry, true); + + return true; + } + + return false; + } + + /** + * Unregisters this entry from this registry. After this method call, the entry is guaranteed to be removed from the + * registry, since each entry only ever appears once. + * + * @param entry The entry to unregister. + */ + public void unregister(V entry) { + if (registered(entry)) { + return; + } + + _entries.remove(entry); + registered.remove(entry); + } + + /** + * Returns an iterator that iterates over each entry in this registry, with the newest elements iterated over first. + * Internally, as new elements are prepended to the registry rather than appended to the end, this order is the best + * way to search through the registry if you want to discover newer items first. + */ + public Iterator iterator() { + return _entries.iterator(); + } + + /** + * Returns an iterator that iterates over each entry in this registry, in the order they were added. Internally, + * this iterates through the registry backwards, as new elements are prepended to the registry rather than appended + * to the end. You should only do this when you need to, for example, list elements in order - it is almost always + * fine to simply use {@link Iterable#forEach(Consumer) forEach} on the {@link #entries} collection instead. + */ + public Iterator descendingIterator() { + return _entries.descendingIterator(); + } + + /** + * Returns a stream that contains each entry in this registry, with the newest elements ordered first. Internally, + * as new elements are prepended to the registry rather than appended to the end, this order is the best way to + * search through the registry if you want to discover newer items first. + */ + public Stream stream() { + return _entries.stream(); + } + + /** + * Returns a stream that returns each entry in this registry, in the order they were added. Internally, this orders + * the registry backwards, as new elements are prepended to the registry rather than appended to the end. You should + * only use this when you need to, for example, list elements in order - it is almost always fine to simply use the + * regular {@link #stream()} method instead. + */ + public Stream descendingStream() { + return StreamSupport.stream(Spliterators.spliterator( + descendingIterator(), + _entries.size(), + Spliterator.SIZED | Spliterator.SUBSIZED + ), false); + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java b/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java new file mode 100644 index 00000000..8aabfd69 --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinChatTabCompleter.java @@ -0,0 +1,20 @@ +package baritone.launch.mixins; + +import net.minecraft.client.gui.GuiChat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiChat.ChatTabCompleter.class) +public abstract class MixinChatTabCompleter extends MixinTabCompleter { + @Inject(method = "*", at = @At("RETURN")) + private void onConstruction(CallbackInfo ci) { + isChatCompleter = true; + } + + @Inject(method = "complete", at = @At("HEAD"), cancellable = true) + private void onComplete(CallbackInfo ci) { + if (dontComplete) ci.cancel(); + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinGuiChat.java b/src/launch/java/baritone/launch/mixins/MixinGuiChat.java new file mode 100644 index 00000000..310bfd7c --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinGuiChat.java @@ -0,0 +1,23 @@ +package baritone.launch.mixins; + +import baritone.utils.accessor.ITabCompleter; +import net.minecraft.client.gui.GuiChat; +import net.minecraft.util.TabCompleter; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiChat.class) +public abstract class MixinGuiChat implements net.minecraft.util.ITabCompleter { + @Shadow + private TabCompleter tabCompleter; + + @Inject(method = "setCompletions", at = @At("HEAD"), cancellable = true) + private void onSetCompletions(String[] newCompl, CallbackInfo ci) { + if (((ITabCompleter) tabCompleter).onGuiChatSetCompletions(newCompl)) { + ci.cancel(); + } + } +} diff --git a/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java b/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java new file mode 100644 index 00000000..0731df1f --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinGuiScreen.java @@ -0,0 +1,15 @@ +package baritone.launch.mixins; + +import baritone.api.utils.command.Lol; +import net.minecraft.client.gui.GuiScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.net.URI; + +@Mixin(GuiScreen.class) +public abstract class MixinGuiScreen implements Lol { + @Override + @Invoker("openWebLink") + public abstract void openLink(URI url); +} diff --git a/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java b/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java new file mode 100644 index 00000000..4e36491b --- /dev/null +++ b/src/launch/java/baritone/launch/mixins/MixinTabCompleter.java @@ -0,0 +1,115 @@ +package baritone.launch.mixins; + +import baritone.api.BaritoneAPI; +import baritone.api.IBaritone; +import baritone.api.event.events.TabCompleteEvent; +import baritone.utils.accessor.ITabCompleter; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiTextField; +import net.minecraft.util.TabCompleter; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static java.util.Objects.isNull; + +@Mixin(TabCompleter.class) +public abstract class MixinTabCompleter implements ITabCompleter { + @Shadow + @Final + protected GuiTextField textField; + + @Shadow + protected boolean requestedCompletions; + + @Shadow + public abstract void setCompletions(String... newCompl); + + @Unique + protected boolean isChatCompleter = false; + + @Unique + protected boolean dontComplete = false; + + @Override + public String getPrefix() { + return textField.getText().substring(0, textField.getCursorPosition()); + } + + @Override + public void setPrefix(String prefix) { + textField.setText(prefix + textField.getText().substring(textField.getCursorPosition())); + textField.setCursorPosition(prefix.length()); + } + + @Inject(method = "requestCompletions", at = @At("HEAD"), cancellable = true) + private void onRequestCompletions(String prefix, CallbackInfo ci) { + if (!isChatCompleter) { + return; + } + + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(Minecraft.getMinecraft().player); + + if (isNull(baritone)) { + return; + } + + TabCompleteEvent.Pre event = new TabCompleteEvent.Pre(prefix); + baritone.getGameEventHandler().onPreTabComplete(event); + + if (event.isCancelled()) { + ci.cancel(); + return; + } + + if (event.prefix.wasModified()) { + setPrefix(event.prefix.get()); + } + + if (event.completions.wasModified()) { + ci.cancel(); + + dontComplete = true; + + try { + requestedCompletions = true; + setCompletions(event.completions.get()); + } finally { + dontComplete = false; + } + } + } + + @Override + public boolean onGuiChatSetCompletions(String[] newCompl) { + IBaritone baritone = BaritoneAPI.getProvider().getBaritoneForPlayer(Minecraft.getMinecraft().player); + + if (isNull(baritone)) { + return false; + } + + TabCompleteEvent.Post event = new TabCompleteEvent.Post(getPrefix(), newCompl); + baritone.getGameEventHandler().onPostTabComplete(event); + + if (event.isCancelled()) { + return true; + } + + if (event.prefix.wasModified()) { + String prefix = event.prefix.get(); + textField.setText(prefix + textField.getText().substring(textField.getCursorPosition())); + textField.setCursorPosition(prefix.length()); + } + + if (event.completions.wasModified()) { + setCompletions(event.completions.get()); + return true; + } + + return false; + } +} diff --git a/src/launch/resources/mixins.baritone.json b/src/launch/resources/mixins.baritone.json index 3b5fa70c..66ef181d 100644 --- a/src/launch/resources/mixins.baritone.json +++ b/src/launch/resources/mixins.baritone.json @@ -23,6 +23,10 @@ "MixinRenderChunk", "MixinRenderList", "MixinVboRenderList", - "MixinWorldClient" + "MixinWorldClient", + "MixinTabCompleter", + "MixinGuiChat", + "MixinChatTabCompleter", + "MixinGuiScreen" ] } \ No newline at end of file diff --git a/src/main/java/baritone/Baritone.java b/src/main/java/baritone/Baritone.java index a4b2d436..d2aae776 100755 --- a/src/main/java/baritone/Baritone.java +++ b/src/main/java/baritone/Baritone.java @@ -21,7 +21,7 @@ import baritone.api.BaritoneAPI; import baritone.api.IBaritone; import baritone.api.Settings; import baritone.api.event.listener.IEventBus; -import baritone.api.utils.ExampleBaritoneControl; +import baritone.api.utils.command.BaritoneChatControl; import baritone.api.utils.Helper; import baritone.api.utils.IPlayerContext; import baritone.behavior.*; @@ -109,7 +109,7 @@ public class Baritone implements IBaritone { memoryBehavior = new MemoryBehavior(this); inventoryBehavior = new InventoryBehavior(this); inputOverrideHandler = new InputOverrideHandler(this); - new ExampleBaritoneControl(this); + new BaritoneChatControl(this); } this.pathingControlManager = new PathingControlManager(this); diff --git a/src/main/java/baritone/behavior/PathingBehavior.java b/src/main/java/baritone/behavior/PathingBehavior.java index 7219175e..d1d9a7d5 100644 --- a/src/main/java/baritone/behavior/PathingBehavior.java +++ b/src/main/java/baritone/behavior/PathingBehavior.java @@ -19,7 +19,11 @@ package baritone.behavior; import baritone.Baritone; import baritone.api.behavior.IPathingBehavior; -import baritone.api.event.events.*; +import baritone.api.event.events.PathEvent; +import baritone.api.event.events.PlayerUpdateEvent; +import baritone.api.event.events.RenderEvent; +import baritone.api.event.events.SprintStateEvent; +import baritone.api.event.events.TickEvent; import baritone.api.pathing.calc.IPath; import baritone.api.pathing.goals.Goal; import baritone.api.pathing.goals.GoalXZ; @@ -55,6 +59,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, private boolean safeToCancel; private boolean pauseRequestedLastTick; private boolean unpausedLastTick; + private boolean pausedThisTick; private boolean cancelRequested; private boolean calcFailedLastTick; @@ -108,6 +113,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, } private void tickPath() { + pausedThisTick = false; if (pauseRequestedLastTick && safeToCancel) { pauseRequestedLastTick = false; if (unpausedLastTick) { @@ -115,6 +121,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, baritone.getInputOverrideHandler().getBlockBreakHelper().stopBreakingBlock(); } unpausedLastTick = false; + pausedThisTick = true; return; } unpausedLastTick = true; @@ -130,8 +137,8 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, BetterBlockPos calcFrom = inProgress.getStart(); Optional currentBest = inProgress.bestPathSoFar(); if ((current == null || !current.getPath().getDest().equals(calcFrom)) // if current ends in inProgress's start, then we're ok - && !calcFrom.equals(ctx.playerFeet()) && !calcFrom.equals(expectedSegmentStart) // if current starts in our playerFeet or pathStart, then we're ok - && (!currentBest.isPresent() || (!currentBest.get().positions().contains(ctx.playerFeet()) && !currentBest.get().positions().contains(expectedSegmentStart))) // if + && !calcFrom.equals(ctx.playerFeet()) && !calcFrom.equals(expectedSegmentStart) // if current starts in our playerFeet or pathStart, then we're ok + && (!currentBest.isPresent() || (!currentBest.get().positions().contains(ctx.playerFeet()) && !currentBest.get().positions().contains(expectedSegmentStart))) // if ) { // when it was *just* started, currentBest will be empty so we need to also check calcFrom since that's always present inProgress.cancel(); // cancellation doesn't dispatch any events @@ -279,6 +286,16 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior, return goal; } + @Override + public boolean isPathing() { + return hasPath() && !pausedThisTick; + } + + @Override + public boolean hasPath() { + return current != null; + } + @Override public PathExecutor getCurrent() { return current; diff --git a/src/main/java/baritone/cache/WorldScanner.java b/src/main/java/baritone/cache/WorldScanner.java index d6cf69c3..22c042f7 100644 --- a/src/main/java/baritone/cache/WorldScanner.java +++ b/src/main/java/baritone/cache/WorldScanner.java @@ -18,6 +18,7 @@ package baritone.cache; import baritone.api.cache.IWorldScanner; +import baritone.api.utils.IBlockFilter; import baritone.api.utils.IPlayerContext; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; @@ -38,14 +39,8 @@ public enum WorldScanner implements IWorldScanner { private static final int[] DEFAULT_COORDINATE_ITERATION_ORDER = IntStream.range(0, 16).toArray(); @Override - public List scanChunkRadius(IPlayerContext ctx, List blocks, int max, int yLevelThreshold, int maxSearchRadius) { - if (blocks.contains(null)) { - throw new IllegalStateException("Invalid block name should have been caught earlier: " + blocks.toString()); - } + public List scanChunkRadius(IPlayerContext ctx, IBlockFilter filter, int max, int yLevelThreshold, int maxSearchRadius) { ArrayList res = new ArrayList<>(); - if (blocks.isEmpty()) { - return res; - } ChunkProviderClient chunkProvider = (ChunkProviderClient) ctx.world().getChunkProvider(); int maxSearchRadiusSq = maxSearchRadius * maxSearchRadius; @@ -75,7 +70,7 @@ public enum WorldScanner implements IWorldScanner { continue; } allUnloaded = false; - if (scanChunkInto(chunkX << 4, chunkZ << 4, chunk, blocks, res, max, yLevelThreshold, playerY, coordinateIterationOrder)) { + if (scanChunkInto(chunkX << 4, chunkZ << 4, chunk, filter, res, max, yLevelThreshold, playerY, coordinateIterationOrder)) { foundWithinY = true; } } @@ -91,11 +86,7 @@ public enum WorldScanner implements IWorldScanner { } @Override - public List scanChunk(IPlayerContext ctx, List blocks, ChunkPos pos, int max, int yLevelThreshold) { - if (blocks.isEmpty()) { - return Collections.emptyList(); - } - + public List scanChunk(IPlayerContext ctx, IBlockFilter filter, ChunkPos pos, int max, int yLevelThreshold) { ChunkProviderClient chunkProvider = (ChunkProviderClient) ctx.world().getChunkProvider(); Chunk chunk = chunkProvider.getLoadedChunk(pos.x, pos.z); int playerY = ctx.playerFeet().getY(); @@ -105,11 +96,11 @@ public enum WorldScanner implements IWorldScanner { } ArrayList res = new ArrayList<>(); - scanChunkInto(pos.x << 4, pos.z << 4, chunk, blocks, res, max, yLevelThreshold, playerY, DEFAULT_COORDINATE_ITERATION_ORDER); + scanChunkInto(pos.x << 4, pos.z << 4, chunk, filter, res, max, yLevelThreshold, playerY, DEFAULT_COORDINATE_ITERATION_ORDER); return res; } - private boolean scanChunkInto(int chunkX, int chunkZ, Chunk chunk, List search, Collection result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) { + private boolean scanChunkInto(int chunkX, int chunkZ, Chunk chunk, IBlockFilter filter, Collection result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) { ExtendedBlockStorage[] chunkInternalStorageArray = chunk.getBlockStorageArray(); boolean foundWithinY = false; for (int yIndex = 0; yIndex < 16; yIndex++) { @@ -126,7 +117,7 @@ public enum WorldScanner implements IWorldScanner { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { IBlockState state = bsc.get(x, y, z); - if (search.contains(state.getBlock())) { + if (filter.selected(state)) { int yy = yReal | y; if (result.size() >= max) { if (Math.abs(yy - playerY) < yLevelThreshold) { diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index 2ff688ad..76ae0b4e 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -69,6 +69,16 @@ public final class GameEventHandler implements IEventBus, Helper { listeners.forEach(l -> l.onSendChatMessage(event)); } + @Override + public void onPreTabComplete(TabCompleteEvent.Pre event) { + listeners.forEach(l -> l.onPreTabComplete(event)); + } + + @Override + public void onPostTabComplete(TabCompleteEvent.Post event) { + listeners.forEach(l -> l.onPostTabComplete(event)); + } + @Override public final void onChunkEvent(ChunkEvent event) { EventState state = event.getState(); diff --git a/src/main/java/baritone/process/BuilderProcess.java b/src/main/java/baritone/process/BuilderProcess.java index 84ea8da3..0c37ac16 100644 --- a/src/main/java/baritone/process/BuilderProcess.java +++ b/src/main/java/baritone/process/BuilderProcess.java @@ -92,6 +92,11 @@ public final class BuilderProcess extends BaritoneProcessHelper implements IBuil paused = true; } + @Override + public boolean isPaused() { + return paused; + } + @Override public boolean build(String name, File schematic, Vec3i origin) { NBTTagCompound tag; diff --git a/src/main/java/baritone/process/FarmProcess.java b/src/main/java/baritone/process/FarmProcess.java index 00273a9f..e78c0ce1 100644 --- a/src/main/java/baritone/process/FarmProcess.java +++ b/src/main/java/baritone/process/FarmProcess.java @@ -24,6 +24,7 @@ import baritone.api.pathing.goals.GoalComposite; import baritone.api.process.IFarmProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; +import baritone.api.utils.BlockListFilter; import baritone.api.utils.RayTraceUtils; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; @@ -180,7 +181,7 @@ public final class FarmProcess extends BaritoneProcessHelper implements IFarmPro } if (Baritone.settings().mineGoalUpdateInterval.value != 0 && tickCount++ % Baritone.settings().mineGoalUpdateInterval.value == 0) { - Baritone.getExecutor().execute(() -> locations = WorldScanner.INSTANCE.scanChunkRadius(ctx, scan, 256, 10, 10)); + Baritone.getExecutor().execute(() -> locations = WorldScanner.INSTANCE.scanChunkRadius(ctx, new BlockListFilter(scan), 256, 10, 10)); } if (locations == null) { return new PathingCommand(null, PathingCommandType.REQUEST_PAUSE); diff --git a/src/main/java/baritone/process/GetToBlockProcess.java b/src/main/java/baritone/process/GetToBlockProcess.java index 4fb4d540..755f27c2 100644 --- a/src/main/java/baritone/process/GetToBlockProcess.java +++ b/src/main/java/baritone/process/GetToBlockProcess.java @@ -22,6 +22,7 @@ import baritone.api.pathing.goals.*; import baritone.api.process.IGetToBlockProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; +import baritone.api.utils.BlockListFilter; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; import baritone.api.utils.input.Input; @@ -171,7 +172,7 @@ public final class GetToBlockProcess extends BaritoneProcessHelper implements IG } private synchronized void rescan(List known, CalculationContext context) { - List positions = MineProcess.searchWorld(context, Collections.singletonList(gettingTo), 64, known, blacklist); + List positions = MineProcess.searchWorld(context, new BlockListFilter(gettingTo), 64, known, blacklist); positions.removeIf(blacklist::contains); knownLocations = positions; } diff --git a/src/main/java/baritone/process/MineProcess.java b/src/main/java/baritone/process/MineProcess.java index f3b1d20b..1b3ef0f1 100644 --- a/src/main/java/baritone/process/MineProcess.java +++ b/src/main/java/baritone/process/MineProcess.java @@ -22,7 +22,10 @@ import baritone.api.pathing.goals.*; import baritone.api.process.IMineProcess; import baritone.api.process.PathingCommand; import baritone.api.process.PathingCommandType; +import baritone.api.utils.BlockSelector; import baritone.api.utils.BlockUtils; +import baritone.api.utils.CompositeBlockFilter; +import baritone.api.utils.IBlockFilter; import baritone.api.utils.IPlayerContext; import baritone.api.utils.Rotation; import baritone.api.utils.RotationUtils; @@ -59,7 +62,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro private static final int ORE_LOCATIONS_COUNT = 64; - private List mining; + private IBlockFilter filter; private List knownOreLocations; private List blacklist; // inaccessible private BlockPos branchPoint; @@ -73,28 +76,29 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro @Override public boolean isActive() { - return mining != null; + return filter != null; } @Override public PathingCommand onTick(boolean calcFailed, boolean isSafeToCancel) { if (desiredQuantity > 0) { - Item item = mining.get(0).getItemDropped(mining.get(0).getDefaultState(), new Random(), 0); - int curr = ctx.player().inventory.mainInventory.stream().filter(stack -> item.equals(stack.getItem())).mapToInt(ItemStack::getCount).sum(); - System.out.println("Currently have " + curr + " " + item); + int curr = ctx.player().inventory.mainInventory.stream() + .filter(stack -> filter.selected(BlockSelector.stateFromItem(stack))) + .mapToInt(ItemStack::getCount).sum(); + System.out.println("Currently have " + curr + " valid items"); if (curr >= desiredQuantity) { - logDirect("Have " + curr + " " + item.getItemStackDisplayName(new ItemStack(item, 1))); + logDirect("Have " + curr + " valid items"); cancel(); return null; } } if (calcFailed) { if (!knownOreLocations.isEmpty() && Baritone.settings().blacklistClosestOnFailure.value) { - logDirect("Unable to find any path to " + mining + ", blacklisting presumably unreachable closest instance..."); + logDirect("Unable to find any path to " + filter + ", blacklisting presumably unreachable closest instance..."); knownOreLocations.stream().min(Comparator.comparingDouble(ctx.player()::getDistanceSq)).ifPresent(blacklist::add); knownOreLocations.removeIf(blacklist::contains); } else { - logDirect("Unable to find any path to " + mining + ", canceling Mine"); + logDirect("Unable to find any path to " + filter + ", canceling mine"); cancel(); return null; } @@ -146,19 +150,19 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro @Override public void onLostControl() { - mine(0, (Block[]) null); + mine(0, (IBlockFilter) null); } @Override public String displayName0() { - return "Mine " + mining; + return "Mine " + filter; } private PathingCommand updateGoal() { boolean legit = Baritone.settings().legitMine.value; List locs = knownOreLocations; if (!locs.isEmpty()) { - List locs2 = prune(new CalculationContext(baritone), new ArrayList<>(locs), mining, ORE_LOCATIONS_COUNT, blacklist); + List locs2 = prune(new CalculationContext(baritone), new ArrayList<>(locs), filter, ORE_LOCATIONS_COUNT, blacklist); // can't reassign locs, gotta make a new var locs2, because we use it in a lambda right here, and variables you use in a lambda must be effectively final Goal goal = new GoalComposite(locs2.stream().map(loc -> coalesce(loc, locs2)).toArray(Goal[]::new)); knownOreLocations = locs2; @@ -194,16 +198,16 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } private void rescan(List already, CalculationContext context) { - if (mining == null) { + if (filter == null) { return; } if (Baritone.settings().legitMine.value) { return; } - List locs = searchWorld(context, mining, ORE_LOCATIONS_COUNT, already, blacklist); - locs.addAll(droppedItemsScan(mining, ctx.world())); + List locs = searchWorld(context, filter, ORE_LOCATIONS_COUNT, already, blacklist); + locs.addAll(droppedItemsScan(filter, ctx.world())); if (locs.isEmpty()) { - logDirect("No locations for " + mining + " known, cancelling"); + logDirect("No locations for " + filter + " known, cancelling"); cancel(); return; } @@ -215,11 +219,11 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro if (locs.contains(pos)) { return true; } - Block block = BlockStateInterface.getBlock(ctx, pos); - if (Baritone.settings().internalMiningAirException.value && block instanceof BlockAir) { + IBlockState state = BlockStateInterface.get(ctx, pos); + if (Baritone.settings().internalMiningAirException.value && state.getBlock() instanceof BlockAir) { return true; } - return mining.contains(block); + return filter.selected(state); } private Goal coalesce(BlockPos loc, List locs) { @@ -284,22 +288,18 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } } - public static List droppedItemsScan(List mining, World world) { + public static List droppedItemsScan(IBlockFilter filter, World world) { if (!Baritone.settings().mineScanDroppedItems.value) { return Collections.emptyList(); } - Set searchingFor = new HashSet<>(); - for (Block block : mining) { - Item drop = block.getItemDropped(block.getDefaultState(), new Random(), 0); - Item ore = Item.getItemFromBlock(block); - searchingFor.add(drop); - searchingFor.add(ore); - } List ret = new ArrayList<>(); for (Entity entity : world.loadedEntityList) { if (entity instanceof EntityItem) { EntityItem ei = (EntityItem) entity; - if (searchingFor.contains(ei.getItem().getItem())) { + ItemStack stack = ei.getItem(); + Item item = stack.getItem(); + //noinspection deprecation + if (filter.selected(Block.getBlockFromItem(item).getStateFromMeta(stack.getItemDamage()))) { ret.add(new BlockPos(entity)); } } @@ -307,30 +307,16 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro return ret; } - public static List searchWorld(CalculationContext ctx, List mining, int max, List alreadyKnown, List blacklist) { + public static List searchWorld(CalculationContext ctx, IBlockFilter filter, int max, List alreadyKnown, List blacklist) { List locs = new ArrayList<>(); - List uninteresting = new ArrayList<>(); - for (Block m : mining) { - if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(m)) { - // maxRegionDistanceSq 2 means adjacent directly or adjacent diagonally; nothing further than that - locs.addAll(ctx.worldData.getCachedWorld().getLocationsOf(BlockUtils.blockToString(m), Baritone.settings().maxCachedWorldScanCount.value, ctx.getBaritone().getPlayerContext().playerFeet().getX(), ctx.getBaritone().getPlayerContext().playerFeet().getZ(), 2)); - } else { - uninteresting.add(m); - } - } - locs = prune(ctx, locs, mining, max, blacklist); - if (locs.isEmpty() || (Baritone.settings().extendCacheOnThreshold.value && locs.size() < max)) { - uninteresting = mining; - } - if (!uninteresting.isEmpty()) { - locs.addAll(WorldScanner.INSTANCE.scanChunkRadius(ctx.getBaritone().getPlayerContext(), uninteresting, max, 10, 32)); // maxSearchRadius is NOT sq - } + locs = prune(ctx, locs, filter, max, blacklist); + locs.addAll(WorldScanner.INSTANCE.scanChunkRadius(ctx.getBaritone().getPlayerContext(), filter, max, 10, 32)); // maxSearchRadius is NOT sq locs.addAll(alreadyKnown); - return prune(ctx, locs, mining, max, blacklist); + return prune(ctx, locs, filter, max, blacklist); } private void addNearby() { - knownOreLocations.addAll(droppedItemsScan(mining, ctx.world())); + knownOreLocations.addAll(droppedItemsScan(filter, ctx.world())); BlockPos playerFeet = ctx.playerFeet(); BlockStateInterface bsi = new BlockStateInterface(ctx); int searchDist = 10; @@ -340,7 +326,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro for (int z = playerFeet.getZ() - searchDist; z <= playerFeet.getZ() + searchDist; z++) { // crucial to only add blocks we can see because otherwise this // is an x-ray and it'll get caught - if (mining.contains(bsi.get0(x, y, z).getBlock())) { + if (filter.selected(bsi.get0(x, y, z))) { BlockPos pos = new BlockPos(x, y, z); if ((Baritone.settings().legitMineIncludeDiagonals.value && knownOreLocations.stream().anyMatch(ore -> ore.distanceSq(pos) <= 2 /* sq means this is pytha dist <= sqrt(2) */)) || RotationUtils.reachable(ctx.player(), pos, fakedBlockReachDistance).isPresent()) { knownOreLocations.add(pos); @@ -349,14 +335,14 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro } } } - knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, mining, ORE_LOCATIONS_COUNT, blacklist); + knownOreLocations = prune(new CalculationContext(baritone), knownOreLocations, filter, ORE_LOCATIONS_COUNT, blacklist); } - private static List prune(CalculationContext ctx, List locs2, List mining, int max, List blacklist) { - List dropped = droppedItemsScan(mining, ctx.world); + private static List prune(CalculationContext ctx, List locs2, IBlockFilter filter, int max, List blacklist) { + List dropped = droppedItemsScan(filter, ctx.world); dropped.removeIf(drop -> { for (BlockPos pos : locs2) { - if (pos.distanceSq(drop) <= 9 && mining.contains(ctx.getBlock(pos.getX(), pos.getY(), pos.getZ())) && MineProcess.plausibleToBreak(ctx, pos)) { // TODO maybe drop also has to be supported? no lava below? + if (pos.distanceSq(drop) <= 9 && filter.selected(ctx.get(pos.getX(), pos.getY(), pos.getZ())) && MineProcess.plausibleToBreak(ctx, pos)) { // TODO maybe drop also has to be supported? no lava below? return true; } } @@ -367,7 +353,7 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro .distinct() // remove any that are within loaded chunks that aren't actually what we want - .filter(pos -> !ctx.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ()) || mining.contains(ctx.getBlock(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos)) + .filter(pos -> !ctx.bsi.worldContainsLoadedChunk(pos.getX(), pos.getZ()) || filter.selected(ctx.get(pos.getX(), pos.getY(), pos.getZ())) || dropped.contains(pos)) // remove any that are implausible to mine (encased in bedrock, or touching lava) .filter(pos -> MineProcess.plausibleToBreak(ctx, pos)) @@ -394,22 +380,26 @@ public final class MineProcess extends BaritoneProcessHelper implements IMinePro @Override public void mineByName(int quantity, String... blocks) { - mine(quantity, blocks == null || blocks.length == 0 ? null : Arrays.stream(blocks).map(BlockUtils::stringToBlockRequired).toArray(Block[]::new)); + mine(quantity, new CompositeBlockFilter( + Arrays.stream(Objects.requireNonNull(blocks)) + .map(BlockSelector::new) + .toArray(IBlockFilter[]::new) + )); } @Override - public void mine(int quantity, Block... blocks) { - this.mining = blocks == null || blocks.length == 0 ? null : Arrays.asList(blocks); - if (mining != null && !Baritone.settings().allowBreak.value) { + public void mine(int quantity, IBlockFilter filter) { + this.filter = filter; + if (filter != null && !Baritone.settings().allowBreak.value) { logDirect("Unable to mine when allowBreak is false!"); - mining = null; + filter = null; } this.desiredQuantity = quantity; this.knownOreLocations = new ArrayList<>(); this.blacklist = new ArrayList<>(); this.branchPoint = null; this.branchPointRunaway = null; - if (mining != null) { + if (filter != null) { rescan(new ArrayList<>(), new CalculationContext(baritone)); } } diff --git a/src/main/java/baritone/utils/PathRenderer.java b/src/main/java/baritone/utils/PathRenderer.java index b0157247..cb5e7be0 100644 --- a/src/main/java/baritone/utils/PathRenderer.java +++ b/src/main/java/baritone/utils/PathRenderer.java @@ -185,12 +185,17 @@ public final class PathRenderer implements Helper { double d0 = mc.getRenderManager().viewerPosX; double d1 = mc.getRenderManager().viewerPosY; double d2 = mc.getRenderManager().viewerPosZ; - BUFFER.begin(GL_LINE_STRIP, DefaultVertexFormats.POSITION); + boolean renderPathAsFrickinThingy = !Baritone.settings().renderPathAsLine.value; + + BUFFER.begin(renderPathAsFrickinThingy ? GL_LINE_STRIP : GL_LINES, DefaultVertexFormats.POSITION); BUFFER.pos(bp1x + 0.5D - d0, bp1y + 0.5D - d1, bp1z + 0.5D - d2).endVertex(); BUFFER.pos(bp2x + 0.5D - d0, bp2y + 0.5D - d1, bp2z + 0.5D - d2).endVertex(); - BUFFER.pos(bp2x + 0.5D - d0, bp2y + 0.53D - d1, bp2z + 0.5D - d2).endVertex(); - BUFFER.pos(bp1x + 0.5D - d0, bp1y + 0.53D - d1, bp1z + 0.5D - d2).endVertex(); - BUFFER.pos(bp1x + 0.5D - d0, bp1y + 0.5D - d1, bp1z + 0.5D - d2).endVertex(); + + if (renderPathAsFrickinThingy) { + BUFFER.pos(bp2x + 0.5D - d0, bp2y + 0.53D - d1, bp2z + 0.5D - d2).endVertex(); + BUFFER.pos(bp1x + 0.5D - d0, bp1y + 0.53D - d1, bp1z + 0.5D - d2).endVertex(); + BUFFER.pos(bp1x + 0.5D - d0, bp1y + 0.5D - d1, bp1z + 0.5D - d2).endVertex(); + } } public static void drawManySelectionBoxes(Entity player, Collection positions, Color color) { @@ -334,6 +339,9 @@ public final class PathRenderer implements Helper { drawDankLitGoalBox(player, g, partialTicks, color); } return; + } else if (goal instanceof GoalInverted) { + drawDankLitGoalBox(player, ((GoalInverted) goal).origin, partialTicks, Baritone.settings().colorInvertedGoalBox.value); + return; } else if (goal instanceof GoalYLevel) { GoalYLevel goalpos = (GoalYLevel) goal; minX = player.posX - Baritone.settings().yLevelBoxSize.value - renderPosX; diff --git a/src/main/java/baritone/utils/accessor/ITabCompleter.java b/src/main/java/baritone/utils/accessor/ITabCompleter.java new file mode 100644 index 00000000..72733f27 --- /dev/null +++ b/src/main/java/baritone/utils/accessor/ITabCompleter.java @@ -0,0 +1,9 @@ +package baritone.utils.accessor; + +public interface ITabCompleter { + String getPrefix(); + + void setPrefix(String prefix); + + boolean onGuiChatSetCompletions(String[] newCompl); +}